1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * The following notice accompanied the original version of this file:
28 *
29 * BSD LICENSE
30 *
31 * Copyright(c) 2007 Intel Corporation. All rights reserved.
32 * All rights reserved.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
36 * are met:
37 *
38 *   * Redistributions of source code must retain the above copyright
39 *     notice, this list of conditions and the following disclaimer.
40 *   * Redistributions in binary form must reproduce the above copyright
41 *     notice, this list of conditions and the following disclaimer in
42 *     the documentation and/or other materials provided with the
43 *     distribution.
44 *   * Neither the name of Intel Corporation nor the names of its
45 *     contributors may be used to endorse or promote products derived
46 *     from this software without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
52 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
53 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
54 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
58 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 */
60
61/*
62 * Common FCoE interface interacts with MAC and FCoE clients, managing
63 * FCoE ports, doing MAC address discovery/managment, and FC frame
64 * encapsulation/decapsulation
65 */
66
67#include <sys/stat.h>
68#include <sys/conf.h>
69#include <sys/file.h>
70#include <sys/cred.h>
71
72#include <sys/ddi.h>
73#include <sys/sunddi.h>
74#include <sys/sunndi.h>
75#include <sys/byteorder.h>
76#include <sys/atomic.h>
77#include <sys/sysmacros.h>
78#include <sys/cmn_err.h>
79#include <sys/crc32.h>
80#include <sys/strsubr.h>
81
82#include <sys/mac_client.h>
83
84/*
85 * FCoE header files
86 */
87#include <sys/fcoe/fcoeio.h>
88#include <sys/fcoe/fcoe_common.h>
89
90/*
91 * Driver's own header files
92 */
93#include <fcoe.h>
94#include <fcoe_fc.h>
95#include <fcoe_eth.h>
96
97/*
98 * Function forward declaration
99 */
100static int fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
101static int fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
102static int fcoe_bus_ctl(dev_info_t *fca_dip, dev_info_t *rip,
103    ddi_ctl_enum_t op, void *arg, void *result);
104static int fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp);
105static int fcoe_close(dev_t dev, int flag, int otype, cred_t *credp);
106static int fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
107    cred_t *credp, int *rval);
108static int fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio,
109    void **ibuf, void **abuf, void **obuf);
110static int fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio,
111    void *obuf);
112static int fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode);
113static int fcoe_attach_init(fcoe_soft_state_t *this_ss);
114static int fcoe_detach_uninit(fcoe_soft_state_t *this_ss);
115static int fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip);
116static int fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip);
117static void fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac,
118    int is_pwwn, uint8_t idx);
119static fcoe_mac_t *fcoe_create_mac_by_id(datalink_id_t linkid);
120static int fcoe_cmp_wwn(fcoe_mac_t *checkedmac);
121static void fcoe_watchdog(void *arg);
122static void fcoe_worker_init();
123static int fcoe_worker_fini();
124static void fcoe_worker_frame();
125static int fcoe_get_port_list(fcoe_port_instance_t *ports, int count);
126static boolean_t fcoe_mac_existed(fcoe_mac_t *pmac);
127
128/*
129 * Driver identificaton stuff
130 */
131static struct cb_ops fcoe_cb_ops = {
132	fcoe_open,
133	fcoe_close,
134	nodev,
135	nodev,
136	nodev,
137	nodev,
138	nodev,
139	fcoe_ioctl,
140	nodev,
141	nodev,
142	nodev,
143	nochpoll,
144	ddi_prop_op,
145	0,
146	D_MP | D_NEW | D_HOTPLUG,
147	CB_REV,
148	nodev,
149	nodev
150};
151
152static struct bus_ops fcoe_busops = {
153	BUSO_REV,
154	nullbusmap,			/* bus_map */
155	NULL,				/* bus_get_intrspec */
156	NULL,				/* bus_add_intrspec */
157	NULL,				/* bus_remove_intrspec */
158	i_ddi_map_fault,		/* bus_map_fault */
159	ddi_dma_map,			/* bus_dma_map */
160	ddi_dma_allochdl,		/* bus_dma_allochdl */
161	ddi_dma_freehdl,		/* bus_dma_freehdl */
162	ddi_dma_bindhdl,		/* bus_dma_bindhdl */
163	ddi_dma_unbindhdl,		/* bus_unbindhdl */
164	ddi_dma_flush,			/* bus_dma_flush */
165	ddi_dma_win,			/* bus_dma_win */
166	ddi_dma_mctl,			/* bus_dma_ctl */
167	fcoe_bus_ctl,			/* bus_ctl */
168	ddi_bus_prop_op,		/* bus_prop_op */
169	NULL,				/* bus_get_eventcookie */
170	NULL,				/* bus_add_eventcall */
171	NULL,				/* bus_remove_event */
172	NULL,				/* bus_post_event */
173	NULL,				/* bus_intr_ctl */
174	NULL,				/* bus_config */
175	NULL,				/* bus_unconfig */
176	NULL,				/* bus_fm_init */
177	NULL,				/* bus_fm_fini */
178	NULL,				/* bus_fm_access_enter */
179	NULL,				/* bus_fm_access_exit */
180	NULL,				/* bus_power */
181	NULL
182};
183
184static struct dev_ops fcoe_ops = {
185	DEVO_REV,
186	0,
187	nodev,
188	nulldev,
189	nulldev,
190	fcoe_attach,
191	fcoe_detach,
192	nodev,
193	&fcoe_cb_ops,
194	&fcoe_busops,
195	ddi_power,
196	ddi_quiesce_not_needed
197};
198
199#define	FCOE_VERSION	"20091123-1.02"
200#define	FCOE_NAME	"FCoE Transport v" FCOE_VERSION
201#define	TASKQ_NAME_LEN	32
202
203static struct modldrv modldrv = {
204	&mod_driverops,
205	FCOE_NAME,
206	&fcoe_ops,
207};
208
209static struct modlinkage modlinkage = {
210	MODREV_1, &modldrv, NULL
211};
212
213/*
214 * TRACE for all FCoE related modules
215 */
216static kmutex_t fcoe_trace_buf_lock;
217static int	fcoe_trace_buf_curndx	= 0;
218static int	fcoe_trace_on		= 1;
219static caddr_t	fcoe_trace_buf		= NULL;
220static clock_t	fcoe_trace_start	= 0;
221static caddr_t	ftb			= NULL;
222static int	fcoe_trace_buf_size	= (1 * 1024 * 1024);
223
224/*
225 * Driver's global variables
226 */
227const fcoe_ver_e	 fcoe_ver_now	  = FCOE_VER_NOW;
228static void		*fcoe_state	  = NULL;
229fcoe_soft_state_t	*fcoe_global_ss	  = NULL;
230int			 fcoe_use_ext_log = 1;
231
232static ddi_taskq_t	*fcoe_worker_taskq;
233static fcoe_worker_t	*fcoe_workers;
234static uint32_t		fcoe_nworkers_running;
235
236const char		*fcoe_workers_num = "workers-number";
237volatile int		fcoe_nworkers;
238
239/*
240 * Common loadable module entry points _init, _fini, _info
241 */
242
243int
244_init(void)
245{
246	int ret;
247
248	ret = ddi_soft_state_init(&fcoe_state, sizeof (fcoe_soft_state_t), 0);
249	if (ret == 0) {
250		ret = mod_install(&modlinkage);
251		if (ret != 0) {
252			ddi_soft_state_fini(&fcoe_state);
253		} else {
254			fcoe_trace_start = ddi_get_lbolt();
255			ftb = kmem_zalloc(fcoe_trace_buf_size,
256			    KM_SLEEP);
257			fcoe_trace_buf = ftb;
258			mutex_init(&fcoe_trace_buf_lock, NULL, MUTEX_DRIVER, 0);
259		}
260	}
261
262	FCOE_LOG("fcoe", "exit _init with %x", ret);
263
264	return (ret);
265}
266
267int
268_fini(void)
269{
270	int ret;
271
272	ret = mod_remove(&modlinkage);
273	if (ret == 0) {
274		ddi_soft_state_fini(&fcoe_state);
275	}
276
277	FCOE_LOG("fcoe", "exit _fini with %x", ret);
278	if (ret == 0) {
279		kmem_free(fcoe_trace_buf, fcoe_trace_buf_size);
280		mutex_destroy(&fcoe_trace_buf_lock);
281	}
282
283	return (ret);
284}
285
286int
287_info(struct modinfo *modinfop)
288{
289	return (mod_info(&modlinkage, modinfop));
290}
291
292/*
293 * Autoconfiguration entry points: attach, detach, getinfo
294 */
295
296static int
297fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
298{
299	int			 ret = DDI_FAILURE;
300	int			 fcoe_ret;
301	int			 instance;
302	fcoe_soft_state_t	*ss;
303
304	instance = ddi_get_instance(dip);
305	switch (cmd) {
306	case DDI_ATTACH:
307		ret = ddi_soft_state_zalloc(fcoe_state, instance);
308		if (ret == DDI_FAILURE) {
309			FCOE_LOG(0, "soft_state_zalloc-%x/%x", ret, instance);
310			return (ret);
311		}
312
313		ss = ddi_get_soft_state(fcoe_state, instance);
314		ss->ss_dip = dip;
315
316		ASSERT(fcoe_global_ss == NULL);
317		fcoe_global_ss = ss;
318		fcoe_ret = fcoe_attach_init(ss);
319		if (fcoe_ret == FCOE_SUCCESS) {
320			ret = DDI_SUCCESS;
321		}
322
323		FCOE_LOG("fcoe", "fcoe_attach_init end with-%x", fcoe_ret);
324		break;
325
326	case DDI_RESUME:
327		ret = DDI_SUCCESS;
328		break;
329
330	default:
331		FCOE_LOG("fcoe", "unsupported attach cmd-%x", cmd);
332		break;
333	}
334
335	return (ret);
336}
337
338static int
339fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
340{
341	int			 ret = DDI_FAILURE;
342	int			 fcoe_ret;
343	int			 instance;
344	fcoe_soft_state_t	*ss;
345
346	instance = ddi_get_instance(dip);
347	ss = ddi_get_soft_state(fcoe_state, instance);
348	if (ss == NULL) {
349		return (ret);
350	}
351
352	ASSERT(fcoe_global_ss != NULL);
353	ASSERT(dip == fcoe_global_ss->ss_dip);
354	switch (cmd) {
355	case DDI_DETACH:
356		fcoe_ret = fcoe_detach_uninit(ss);
357		if (fcoe_ret == FCOE_SUCCESS) {
358			ret = DDI_SUCCESS;
359			fcoe_global_ss = NULL;
360		}
361
362		break;
363
364	case DDI_SUSPEND:
365		ret = DDI_SUCCESS;
366		break;
367
368	default:
369		FCOE_LOG(0, "unsupported detach cmd-%x", cmd);
370		break;
371	}
372
373	return (ret);
374}
375
376/*
377 * FCA driver's intercepted bus control operations.
378 */
379static int
380fcoe_bus_ctl(dev_info_t *fcoe_dip, dev_info_t *rip,
381    ddi_ctl_enum_t op, void *clientarg, void *result)
382{
383	int ret;
384	switch (op) {
385	case DDI_CTLOPS_REPORTDEV:
386	case DDI_CTLOPS_IOMIN:
387		ret = DDI_SUCCESS;
388		break;
389
390	case DDI_CTLOPS_INITCHILD:
391		ret = fcoe_initchild(fcoe_dip, (dev_info_t *)clientarg);
392		break;
393
394	case DDI_CTLOPS_UNINITCHILD:
395		ret = fcoe_uninitchild(fcoe_dip, (dev_info_t *)clientarg);
396		break;
397
398	default:
399		ret = ddi_ctlops(fcoe_dip, rip, op, clientarg, result);
400		break;
401	}
402
403	return (ret);
404}
405
406/*
407 * We need specify the dev address for client driver's instance, or we
408 * can't online client driver's instance.
409 */
410/* ARGSUSED */
411static int
412fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip)
413{
414	char	client_addr[FCOE_STR_LEN];
415	int	rval;
416
417	rval = ddi_prop_get_int(DDI_DEV_T_ANY, client_dip,
418	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "mac_id", -1);
419	if (rval == -1) {
420		FCOE_LOG(__FUNCTION__, "no mac_id property: %p", client_dip);
421		return (DDI_FAILURE);
422	}
423
424	bzero(client_addr, FCOE_STR_LEN);
425	(void) sprintf((char *)client_addr, "%x,0", rval);
426	ddi_set_name_addr(client_dip, client_addr);
427	return (DDI_SUCCESS);
428}
429
430/* ARGSUSED */
431static int
432fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip)
433{
434	ddi_set_name_addr(client_dip, NULL);
435	return (DDI_SUCCESS);
436}
437
438/*
439 * Device access entry points
440 */
441static int
442fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp)
443{
444	int			 instance;
445	fcoe_soft_state_t	*ss;
446
447	if (otype != OTYP_CHR) {
448		return (EINVAL);
449	}
450
451	/*
452	 * Since this is for debugging only, only allow root to issue ioctl now
453	 */
454	if (drv_priv(credp) != 0) {
455		return (EPERM);
456	}
457
458	instance = (int)getminor(*devp);
459	ss = ddi_get_soft_state(fcoe_state, instance);
460	if (ss == NULL) {
461		return (ENXIO);
462	}
463
464	mutex_enter(&ss->ss_ioctl_mutex);
465	if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) {
466		/*
467		 * It is already open for exclusive access.
468		 * So shut the door on this caller.
469		 */
470		mutex_exit(&ss->ss_ioctl_mutex);
471		return (EBUSY);
472	}
473
474	if (flag & FEXCL) {
475		if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) {
476			/*
477			 * Exclusive operation not possible
478			 * as it is already opened
479			 */
480			mutex_exit(&ss->ss_ioctl_mutex);
481			return (EBUSY);
482		}
483		ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL;
484	}
485
486	ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_OPEN;
487	mutex_exit(&ss->ss_ioctl_mutex);
488
489	return (0);
490}
491
492/* ARGSUSED */
493static int
494fcoe_close(dev_t dev, int flag, int otype, cred_t *credp)
495{
496	int			 instance;
497	fcoe_soft_state_t	*ss;
498
499	if (otype != OTYP_CHR) {
500		return (EINVAL);
501	}
502
503	instance = (int)getminor(dev);
504	ss = ddi_get_soft_state(fcoe_state, instance);
505	if (ss == NULL) {
506		return (ENXIO);
507	}
508
509	mutex_enter(&ss->ss_ioctl_mutex);
510	if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) {
511		mutex_exit(&ss->ss_ioctl_mutex);
512		return (ENODEV);
513	}
514
515	ss->ss_ioctl_flags &= ~FCOE_IOCTL_FLAG_MASK;
516	mutex_exit(&ss->ss_ioctl_mutex);
517
518	return (0);
519}
520
521/* ARGSUSED */
522static int
523fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
524    cred_t *credp, int *rval)
525{
526	fcoe_soft_state_t	*ss;
527	int			 ret = 0;
528
529	if (drv_priv(credp) != 0) {
530		return (EPERM);
531	}
532
533	ss = ddi_get_soft_state(fcoe_state, (int32_t)getminor(dev));
534	if (ss == NULL) {
535		return (ENXIO);
536	}
537
538	mutex_enter(&ss->ss_ioctl_mutex);
539	if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) {
540		mutex_exit(&ss->ss_ioctl_mutex);
541		return (ENXIO);
542	}
543	mutex_exit(&ss->ss_ioctl_mutex);
544
545	switch (cmd) {
546	case FCOEIO_CMD:
547		ret = fcoe_iocmd(ss, data, mode);
548		break;
549	default:
550		FCOE_LOG(0, "fcoe_ioctl: ioctl-0x%02X", cmd);
551		ret = ENOTTY;
552		break;
553	}
554
555	return (ret);
556}
557
558static int
559fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio,
560    void **ibuf, void **abuf, void **obuf)
561{
562	int ret = 0;
563
564	*ibuf = NULL;
565	*abuf = NULL;
566	*obuf = NULL;
567	*fcoeio = kmem_zalloc(sizeof (fcoeio_t), KM_SLEEP);
568	if (ddi_copyin((void *)data, *fcoeio, sizeof (fcoeio_t), mode) != 0) {
569		ret = EFAULT;
570		goto copyin_iocdata_fail;
571	}
572
573	if ((*fcoeio)->fcoeio_ilen > FCOEIO_MAX_BUF_LEN ||
574	    (*fcoeio)->fcoeio_alen > FCOEIO_MAX_BUF_LEN ||
575	    (*fcoeio)->fcoeio_olen > FCOEIO_MAX_BUF_LEN) {
576		ret = EFAULT;
577		goto copyin_iocdata_fail;
578	}
579
580	if ((*fcoeio)->fcoeio_ilen) {
581		*ibuf = kmem_zalloc((*fcoeio)->fcoeio_ilen, KM_SLEEP);
582		if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_ibuf,
583		    *ibuf, (*fcoeio)->fcoeio_ilen, mode) != 0) {
584			ret = EFAULT;
585			goto copyin_iocdata_fail;
586		}
587	}
588
589	if ((*fcoeio)->fcoeio_alen) {
590		*abuf = kmem_zalloc((*fcoeio)->fcoeio_alen, KM_SLEEP);
591		if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_abuf,
592		    *abuf, (*fcoeio)->fcoeio_alen, mode) != 0) {
593			ret = EFAULT;
594			goto copyin_iocdata_fail;
595		}
596	}
597
598	if ((*fcoeio)->fcoeio_olen) {
599		*obuf = kmem_zalloc((*fcoeio)->fcoeio_olen, KM_SLEEP);
600	}
601	return (ret);
602
603copyin_iocdata_fail:
604	if (*abuf) {
605		kmem_free(*abuf, (*fcoeio)->fcoeio_alen);
606		*abuf = NULL;
607	}
608
609	if (*ibuf) {
610		kmem_free(*ibuf, (*fcoeio)->fcoeio_ilen);
611		*ibuf = NULL;
612	}
613
614	kmem_free(*fcoeio, sizeof (fcoeio_t));
615	return (ret);
616}
617
618static int
619fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, void *obuf)
620{
621	if (fcoeio->fcoeio_olen) {
622		if (ddi_copyout(obuf,
623		    (void *)(unsigned long)fcoeio->fcoeio_obuf,
624		    fcoeio->fcoeio_olen, mode) != 0) {
625			return (EFAULT);
626		}
627	}
628
629	if (ddi_copyout(fcoeio, (void *)data, sizeof (fcoeio_t), mode) != 0) {
630		return (EFAULT);
631	}
632	return (0);
633}
634
635static int
636fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode)
637{
638	int		ret;
639	fcoe_mac_t	*fcoe_mac;
640	void		*ibuf = NULL;
641	void		*obuf = NULL;
642	void		*abuf = NULL;
643	fcoeio_t	*fcoeio;
644
645	ret = fcoe_copyin_iocdata(data, mode, &fcoeio, &ibuf, &abuf, &obuf);
646	if (ret != 0) {
647		goto fcoeiocmd_release_buf;
648	}
649
650	/*
651	 * If an exclusive open was demanded during open, ensure that
652	 * only one thread can execute an ioctl at a time
653	 */
654	mutex_enter(&ss->ss_ioctl_mutex);
655	if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) {
656		if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL_BUSY) {
657			mutex_exit(&ss->ss_ioctl_mutex);
658			fcoeio->fcoeio_status = FCOEIOE_BUSY;
659			ret = EBUSY;
660			goto fcoeiocmd_release_buf;
661		}
662		ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL_BUSY;
663	}
664	mutex_exit(&ss->ss_ioctl_mutex);
665
666	fcoeio->fcoeio_status = 0;
667
668	switch (fcoeio->fcoeio_cmd) {
669	case FCOEIO_CREATE_FCOE_PORT: {
670		fcoeio_create_port_param_t	*param =
671		    (fcoeio_create_port_param_t *)ibuf;
672		int		cmpwwn = 0;
673		fcoe_port_t	*eport;
674
675		if (fcoeio->fcoeio_ilen !=
676		    sizeof (fcoeio_create_port_param_t) ||
677		    fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) {
678			fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
679			ret = EINVAL;
680			break;
681		}
682
683		mutex_enter(&ss->ss_ioctl_mutex);
684		fcoe_mac = fcoe_create_mac_by_id(param->fcp_mac_linkid);
685		if (fcoe_mac == NULL) {
686			mutex_exit(&ss->ss_ioctl_mutex);
687			fcoeio->fcoeio_status = FCOEIOE_CREATE_MAC;
688			ret = EIO;
689			break;
690		}
691
692		if (fcoe_mac->fm_flags & FCOE_MAC_FLAG_ENABLED) {
693			mutex_exit(&ss->ss_ioctl_mutex);
694			fcoeio->fcoeio_status = FCOEIOE_ALREADY;
695			ret = EALREADY;
696			break;
697		} else {
698			ret = fcoe_open_mac(fcoe_mac, param->fcp_force_promisc,
699			    &fcoeio->fcoeio_status);
700			if (ret != 0) {
701				fcoe_destroy_mac(fcoe_mac);
702				mutex_exit(&ss->ss_ioctl_mutex);
703				if (fcoeio->fcoeio_status == 0) {
704					fcoeio->fcoeio_status =
705					    FCOEIOE_OPEN_MAC;
706				}
707				ret = EIO;
708				break;
709			} else {
710				fcoe_mac->fm_flags |= FCOE_MAC_FLAG_ENABLED;
711			}
712		}
713
714		/*
715		 * Provide PWWN and NWWN based on mac address
716		 */
717		eport = &fcoe_mac->fm_eport;
718		if (!param->fcp_pwwn_provided) {
719			fcoe_init_wwn_from_mac(eport->eport_portwwn,
720			    fcoe_mac->fm_current_addr, 1, 0);
721		} else {
722			(void) memcpy(eport->eport_portwwn, param->fcp_pwwn, 8);
723		}
724
725		if (!param->fcp_nwwn_provided) {
726			fcoe_init_wwn_from_mac(eport->eport_nodewwn,
727			    fcoe_mac->fm_current_addr, 0, 0);
728		} else {
729			(void) memcpy(eport->eport_nodewwn, param->fcp_nwwn, 8);
730		}
731
732		cmpwwn = fcoe_cmp_wwn(fcoe_mac);
733
734		if (cmpwwn != 0) {
735			if (cmpwwn == 1) {
736				fcoeio->fcoeio_status = FCOEIOE_PWWN_CONFLICTED;
737			} else if (cmpwwn == -1) {
738				fcoeio->fcoeio_status = FCOEIOE_NWWN_CONFLICTED;
739			}
740			(void) fcoe_close_mac(fcoe_mac);
741			fcoe_destroy_mac(fcoe_mac);
742			mutex_exit(&ss->ss_ioctl_mutex);
743			ret = ENOTUNIQ;
744			break;
745		}
746
747		if (ret == 0) {
748			ret = fcoe_create_port(ss->ss_dip,
749			    fcoe_mac,
750			    (param->fcp_port_type == FCOE_CLIENT_TARGET));
751			if (ret != 0) {
752				if (fcoe_mac_existed(fcoe_mac) == B_TRUE) {
753					(void) fcoe_close_mac(fcoe_mac);
754					fcoe_destroy_mac(fcoe_mac);
755				}
756				fcoeio->fcoeio_status = FCOEIOE_CREATE_PORT;
757				ret = EIO;
758			}
759		}
760		mutex_exit(&ss->ss_ioctl_mutex);
761
762		break;
763	}
764
765	case FCOEIO_DELETE_FCOE_PORT: {
766		fcoeio_delete_port_param_t *del_port_param =
767		    (fcoeio_delete_port_param_t *)ibuf;
768		uint64_t *is_target = (uint64_t *)obuf;
769
770		if (fcoeio->fcoeio_ilen < sizeof (fcoeio_delete_port_param_t) ||
771		    fcoeio->fcoeio_olen != sizeof (uint64_t) ||
772		    fcoeio->fcoeio_xfer != FCOEIO_XFER_RW) {
773			fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
774			ret = EINVAL;
775			break;
776		}
777
778		mutex_enter(&ss->ss_ioctl_mutex);
779		ret = fcoe_delete_port(ss->ss_dip, fcoeio,
780		    del_port_param->fdp_mac_linkid, is_target);
781		mutex_exit(&ss->ss_ioctl_mutex);
782		FCOE_LOG("fcoe", "fcoe_delete_port %x return: %d",
783		    del_port_param->fdp_mac_linkid, ret);
784		break;
785	}
786
787	case FCOEIO_GET_FCOE_PORT_LIST: {
788		fcoe_port_list_t *list = (fcoe_port_list_t *)obuf;
789		int		count;
790
791		if (fcoeio->fcoeio_xfer != FCOEIO_XFER_READ ||
792		    fcoeio->fcoeio_olen < sizeof (fcoe_port_list_t)) {
793			fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
794			ret = EINVAL;
795			break;
796		}
797		mutex_enter(&ss->ss_ioctl_mutex);
798
799		list->numPorts = 1 + (fcoeio->fcoeio_olen -
800		    sizeof (fcoe_port_list_t))/sizeof (fcoe_port_instance_t);
801
802		count = fcoe_get_port_list(list->ports, list->numPorts);
803
804		if (count > list->numPorts) {
805			fcoeio->fcoeio_status = FCOEIOE_MORE_DATA;
806			ret = ENOSPC;
807		}
808		list->numPorts = count;
809		mutex_exit(&ss->ss_ioctl_mutex);
810
811		break;
812
813	}
814
815	default:
816		return (ENOTTY);
817	}
818
819	FCOE_LOG("fcoe", "fcoe_ioctl %x returned %d, fcoeio_status = %d",
820	    fcoeio->fcoeio_cmd, ret, fcoeio->fcoeio_status);
821
822fcoeiocmd_release_buf:
823	if (ret == 0) {
824		ret = fcoe_copyout_iocdata(data, mode, fcoeio, obuf);
825	} else if (fcoeio->fcoeio_status) {
826		(void) fcoe_copyout_iocdata(data, mode, fcoeio, obuf);
827	}
828
829	if (obuf != NULL) {
830		kmem_free(obuf, fcoeio->fcoeio_olen);
831		obuf = NULL;
832	}
833	if (abuf != NULL) {
834		kmem_free(abuf, fcoeio->fcoeio_alen);
835		abuf = NULL;
836	}
837
838	if (ibuf != NULL) {
839		kmem_free(ibuf, fcoeio->fcoeio_ilen);
840		ibuf = NULL;
841	}
842	kmem_free(fcoeio, sizeof (fcoeio_t));
843
844	return (ret);
845}
846
847/*
848 * Finish final initialization
849 */
850static int
851fcoe_attach_init(fcoe_soft_state_t *ss)
852{
853	char taskq_name[TASKQ_NAME_LEN];
854
855	if (ddi_create_minor_node(ss->ss_dip, "admin", S_IFCHR,
856	    ddi_get_instance(ss->ss_dip), DDI_PSEUDO, 0) != DDI_SUCCESS) {
857		FCOE_LOG("FCOE", "ddi_create_minor_node failed");
858		return (FCOE_FAILURE);
859	}
860
861	/*
862	 * watchdog responsible for release frame and dispatch events
863	 */
864	(void) snprintf(taskq_name, sizeof (taskq_name), "fcoe_mac");
865	taskq_name[TASKQ_NAME_LEN - 1] = 0;
866	if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL,
867	    taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) {
868		return (FCOE_FAILURE);
869	}
870
871	ss->ss_ioctl_flags = 0;
872	mutex_init(&ss->ss_ioctl_mutex, NULL, MUTEX_DRIVER, NULL);
873	list_create(&ss->ss_mac_list, sizeof (fcoe_mac_t),
874	    offsetof(fcoe_mac_t, fm_ss_node));
875	list_create(&ss->ss_pfrm_list, sizeof (fcoe_i_frame_t),
876	    offsetof(fcoe_i_frame_t, fmi_pending_node));
877
878	mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0);
879	cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL);
880	ss->ss_flags &= ~SS_FLAG_TERMINATE_WATCHDOG;
881	(void) ddi_taskq_dispatch(ss->ss_watchdog_taskq,
882	    fcoe_watchdog, ss, DDI_SLEEP);
883	while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) {
884		delay(10);
885	}
886	fcoe_nworkers = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip,
887	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (char *)fcoe_workers_num, 4);
888	if (fcoe_nworkers < 1) {
889		fcoe_nworkers = 4;
890	}
891	fcoe_worker_init();
892
893	ddi_report_dev(ss->ss_dip);
894	return (FCOE_SUCCESS);
895}
896
897/*
898 * Finish final uninitialization
899 */
900static int
901fcoe_detach_uninit(fcoe_soft_state_t *ss)
902{
903	int ret;
904	if (!list_is_empty(&ss->ss_mac_list)) {
905		FCOE_LOG("fcoe", "ss_mac_list is not empty when detach");
906		return (FCOE_FAILURE);
907	}
908
909	if ((ret = fcoe_worker_fini()) != FCOE_SUCCESS) {
910		return (ret);
911	}
912
913	/*
914	 * Stop watchdog
915	 */
916	if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
917		mutex_enter(&ss->ss_watch_mutex);
918		ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG;
919		cv_broadcast(&ss->ss_watch_cv);
920		mutex_exit(&ss->ss_watch_mutex);
921		while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
922			delay(10);
923		}
924	}
925
926	ddi_taskq_destroy(ss->ss_watchdog_taskq);
927	mutex_destroy(&ss->ss_watch_mutex);
928	cv_destroy(&ss->ss_watch_cv);
929
930	ddi_remove_minor_node(ss->ss_dip, NULL);
931	mutex_destroy(&ss->ss_ioctl_mutex);
932	list_destroy(&ss->ss_mac_list);
933
934	return (FCOE_SUCCESS);
935}
936
937/*
938 * Return mac instance if it exist, or else return NULL.
939 */
940fcoe_mac_t *
941fcoe_lookup_mac_by_id(datalink_id_t linkid)
942{
943	fcoe_mac_t	*mac = NULL;
944
945	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
946	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
947	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
948		if (linkid != mac->fm_linkid) {
949			continue;
950		}
951		return (mac);
952	}
953	return (NULL);
954}
955
956/*
957 * Return B_TRUE if mac exists, or else return B_FALSE
958 */
959static boolean_t
960fcoe_mac_existed(fcoe_mac_t *pmac)
961{
962	fcoe_mac_t	*mac = NULL;
963
964	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
965	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
966	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
967		if (mac == pmac) {
968			return (B_TRUE);
969		}
970	}
971	return (B_FALSE);
972}
973
974/*
975 * port wwn will start with 20:..., node wwn will start with 10:...
976 */
977static void
978fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, int is_pwwn, uint8_t idx)
979{
980	ASSERT(wwn != NULL);
981	ASSERT(mac != NULL);
982	wwn[0] = (is_pwwn + 1) << 4;
983	wwn[1] = idx;
984	bcopy(mac, wwn + 2, ETHERADDRL);
985}
986
987/*
988 * Return fcoe_mac if it exists, otherwise create a new one
989 */
990static fcoe_mac_t *
991fcoe_create_mac_by_id(datalink_id_t linkid)
992{
993	fcoe_mac_t	*mac = NULL;
994	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
995
996	mac = fcoe_lookup_mac_by_id(linkid);
997	if (mac != NULL) {
998		FCOE_LOG("fcoe", "fcoe_create_mac_by_id found one mac %d",
999		    linkid);
1000		return (mac);
1001	}
1002
1003	mac = kmem_zalloc(sizeof (fcoe_mac_t), KM_SLEEP);
1004	mac->fm_linkid = linkid;
1005	mac->fm_flags = 0;
1006	mac->fm_ss = fcoe_global_ss;
1007	list_insert_tail(&mac->fm_ss->ss_mac_list, mac);
1008	FCOE_LOG("fcoe", "fcoe_create_mac_by_id created one mac %d", linkid);
1009	return (mac);
1010}
1011
1012void
1013fcoe_destroy_mac(fcoe_mac_t *mac)
1014{
1015	ASSERT(mac != NULL);
1016	list_remove(&mac->fm_ss->ss_mac_list, mac);
1017	kmem_free(mac, sizeof (fcoe_mac_t));
1018}
1019
1020/*
1021 * raw frame layout:
1022 * ethernet header + vlan header (optional) + FCoE header +
1023 * FC frame + FCoE tailer
1024 */
1025/* ARGSUSED */
1026mblk_t *
1027fcoe_get_mblk(fcoe_mac_t *mac, uint32_t raw_frame_size)
1028{
1029	mblk_t	*mp;
1030	int	 err;
1031
1032	/*
1033	 * FCFH_SIZE + PADDING_SIZE
1034	 */
1035	ASSERT(raw_frame_size >= 60);
1036	while ((mp = allocb((size_t)raw_frame_size, 0)) == NULL) {
1037		if ((err = strwaitbuf((size_t)raw_frame_size, BPRI_LO)) != 0) {
1038			FCOE_LOG("fcoe_get_mblk", "strwaitbuf return %d", err);
1039			return (NULL);
1040		}
1041	}
1042	mp->b_wptr = mp->b_rptr + raw_frame_size;
1043
1044	/*
1045	 * We should always zero FC frame header
1046	 */
1047	bzero(mp->b_rptr + PADDING_HEADER_SIZE,
1048	    sizeof (fcoe_fc_frame_header_t));
1049	return (mp);
1050}
1051
1052static void
1053fcoe_watchdog(void *arg)
1054{
1055	fcoe_soft_state_t	*ss	   = (fcoe_soft_state_t *)arg;
1056	fcoe_i_frame_t		*fmi;
1057	fcoe_mac_t		*mac = NULL;
1058
1059	FCOE_LOG("fcoe", "fcoe_soft_state is %p", ss);
1060
1061	mutex_enter(&ss->ss_watch_mutex);
1062	ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING;
1063	while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) {
1064		while (fmi = (fcoe_i_frame_t *)list_head(&ss->ss_pfrm_list)) {
1065			list_remove(&ss->ss_pfrm_list, fmi);
1066			mutex_exit(&ss->ss_watch_mutex);
1067
1068			mac = EPORT2MAC(fmi->fmi_frame->frm_eport);
1069			mac->fm_client.ect_release_sol_frame(fmi->fmi_frame);
1070
1071			mutex_enter(&ss->ss_watch_mutex);
1072			mac->fm_frm_cnt--;
1073		}
1074
1075		ss->ss_flags |= SS_FLAG_DOG_WAITING;
1076		(void) cv_wait(&ss->ss_watch_cv, &ss->ss_watch_mutex);
1077		ss->ss_flags &= ~SS_FLAG_DOG_WAITING;
1078	}
1079
1080	ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING;
1081	mutex_exit(&ss->ss_watch_mutex);
1082}
1083
1084static void
1085fcoe_worker_init()
1086{
1087	uint32_t i;
1088
1089	fcoe_nworkers_running = 0;
1090	fcoe_worker_taskq = ddi_taskq_create(0, "FCOE_WORKER_TASKQ",
1091	    fcoe_nworkers, TASKQ_DEFAULTPRI, 0);
1092	fcoe_workers = (fcoe_worker_t *)kmem_zalloc(sizeof (fcoe_worker_t) *
1093	    fcoe_nworkers, KM_SLEEP);
1094	for (i = 0; i < fcoe_nworkers; i++) {
1095		fcoe_worker_t *w = &fcoe_workers[i];
1096		mutex_init(&w->worker_lock, NULL, MUTEX_DRIVER, NULL);
1097		cv_init(&w->worker_cv, NULL, CV_DRIVER, NULL);
1098		w->worker_flags &= ~FCOE_WORKER_TERMINATE;
1099		list_create(&w->worker_frm_list, sizeof (fcoe_i_frame_t),
1100		    offsetof(fcoe_i_frame_t, fmi_pending_node));
1101		(void) ddi_taskq_dispatch(fcoe_worker_taskq, fcoe_worker_frame,
1102		    w, DDI_SLEEP);
1103	}
1104	while (fcoe_nworkers_running != fcoe_nworkers) {
1105		delay(10);
1106	}
1107}
1108
1109static int
1110fcoe_worker_fini()
1111{
1112	uint32_t i;
1113
1114	for (i = 0; i < fcoe_nworkers; i++) {
1115		fcoe_worker_t *w = &fcoe_workers[i];
1116		mutex_enter(&w->worker_lock);
1117		if (w->worker_flags & FCOE_WORKER_STARTED) {
1118			w->worker_flags |= FCOE_WORKER_TERMINATE;
1119			cv_signal(&w->worker_cv);
1120		}
1121		mutex_exit(&w->worker_lock);
1122	}
1123
1124	while (fcoe_nworkers_running != 0) {
1125		delay(drv_usectohz(10000));
1126	}
1127
1128	ddi_taskq_destroy(fcoe_worker_taskq);
1129	kmem_free(fcoe_workers, sizeof (fcoe_worker_t) * fcoe_nworkers);
1130	fcoe_workers = NULL;
1131	return (FCOE_SUCCESS);
1132}
1133
1134static int
1135fcoe_crc_verify(fcoe_frame_t *frm)
1136{
1137	uint32_t crc;
1138	uint8_t *crc_array = FRM2FMI(frm)->fmi_fft->fft_crc;
1139	uint32_t crc_from_frame = ~(crc_array[0] | (crc_array[1] << 8) |
1140	    (crc_array[2] << 16) | (crc_array[3] << 24));
1141	CRC32(crc, frm->frm_fc_frame, frm->frm_fc_frame_size, -1U, crc32_table);
1142	return (crc == crc_from_frame ? FCOE_SUCCESS : FCOE_FAILURE);
1143}
1144
1145static void
1146fcoe_worker_frame(void *arg)
1147{
1148	fcoe_worker_t	*w = (fcoe_worker_t *)arg;
1149	fcoe_i_frame_t	*fmi;
1150	int		ret;
1151
1152	atomic_add_32(&fcoe_nworkers_running, 1);
1153	mutex_enter(&w->worker_lock);
1154	w->worker_flags |= FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE;
1155	while ((w->worker_flags & FCOE_WORKER_TERMINATE) == 0) {
1156		/*
1157		 * loop through the frames
1158		 */
1159		while (fmi = list_head(&w->worker_frm_list)) {
1160			list_remove(&w->worker_frm_list, fmi);
1161			mutex_exit(&w->worker_lock);
1162			/*
1163			 * do the checksum
1164			 */
1165			ret = fcoe_crc_verify(fmi->fmi_frame);
1166			if (ret == FCOE_SUCCESS) {
1167				fmi->fmi_mac->fm_client.ect_rx_frame(
1168				    fmi->fmi_frame);
1169			} else {
1170				fcoe_release_frame(fmi->fmi_frame);
1171			}
1172			mutex_enter(&w->worker_lock);
1173			w->worker_ntasks--;
1174		}
1175		w->worker_flags &= ~FCOE_WORKER_ACTIVE;
1176		cv_wait(&w->worker_cv, &w->worker_lock);
1177		w->worker_flags |= FCOE_WORKER_ACTIVE;
1178	}
1179	w->worker_flags &= ~(FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE);
1180	mutex_exit(&w->worker_lock);
1181	atomic_add_32(&fcoe_nworkers_running, -1);
1182	list_destroy(&w->worker_frm_list);
1183}
1184
1185void
1186fcoe_post_frame(fcoe_frame_t *frm)
1187{
1188	fcoe_worker_t *w;
1189	uint16_t	oxid = FRM_OXID(frm);
1190
1191	w = &fcoe_workers[oxid % fcoe_nworkers_running];
1192	mutex_enter(&w->worker_lock);
1193	list_insert_tail(&w->worker_frm_list, frm->frm_fcoe_private);
1194	w->worker_ntasks++;
1195	if ((w->worker_flags & FCOE_WORKER_ACTIVE) == 0) {
1196		cv_signal(&w->worker_cv);
1197	}
1198	mutex_exit(&w->worker_lock);
1199}
1200
1201/*
1202 * The max length of every LOG is 158
1203 */
1204void
1205fcoe_trace(caddr_t ident, const char *fmt, ...)
1206{
1207	va_list args;
1208	char	tbuf[160];
1209	int	len;
1210	clock_t curclock;
1211	clock_t usec;
1212
1213	if (fcoe_trace_on == 0) {
1214		return;
1215	}
1216
1217	curclock = ddi_get_lbolt();
1218	usec = (curclock - fcoe_trace_start) * usec_per_tick;
1219	len = snprintf(tbuf, 158, "%lu.%03lus 0t%lu %s ", (usec /
1220	    (1000 * 1000)), ((usec % (1000 * 1000)) / 1000),
1221	    curclock, (ident ? ident : "unknown"));
1222	va_start(args, fmt);
1223	len += vsnprintf(tbuf + len, 158 - len, fmt, args);
1224	va_end(args);
1225
1226	if (len > 158) {
1227		len = 158;
1228	}
1229	tbuf[len++] = '\n';
1230	tbuf[len] = 0;
1231
1232	mutex_enter(&fcoe_trace_buf_lock);
1233	bcopy(tbuf, &fcoe_trace_buf[fcoe_trace_buf_curndx], len+1);
1234	fcoe_trace_buf_curndx += len;
1235	if (fcoe_trace_buf_curndx > (fcoe_trace_buf_size - 320)) {
1236		fcoe_trace_buf_curndx = 0;
1237	}
1238	mutex_exit(&fcoe_trace_buf_lock);
1239}
1240
1241/*
1242 * Check whether the pwwn or nwwn already exist or not
1243 * Return value:
1244 * 1: PWWN conflicted
1245 * -1: NWWN conflicted
1246 * 0: No conflict
1247 */
1248static int
1249fcoe_cmp_wwn(fcoe_mac_t *checkedmac)
1250{
1251	fcoe_mac_t	*mac;
1252	uint8_t		*nwwn, *pwwn, *cnwwn, *cpwwn;
1253
1254	cnwwn = checkedmac->fm_eport.eport_nodewwn;
1255	cpwwn = checkedmac->fm_eport.eport_portwwn;
1256	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
1257
1258	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
1259	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
1260		if (mac == checkedmac) {
1261			continue;
1262		}
1263		nwwn = mac->fm_eport.eport_nodewwn;
1264		pwwn = mac->fm_eport.eport_portwwn;
1265
1266		if (memcmp(nwwn, cnwwn, 8) == 0) {
1267			return (-1);
1268		}
1269
1270		if (memcmp(pwwn, cpwwn, 8) == 0) {
1271			return (1);
1272		}
1273	}
1274	return (0);
1275}
1276
1277static int
1278fcoe_get_port_list(fcoe_port_instance_t *ports, int count)
1279{
1280	fcoe_mac_t	*mac = NULL;
1281	int		i = 0;
1282
1283	ASSERT(ports != NULL);
1284	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
1285
1286	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
1287	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
1288		if (i < count) {
1289			bcopy(mac->fm_eport.eport_portwwn,
1290			    ports[i].fpi_pwwn, 8);
1291			ports[i].fpi_mac_linkid = mac->fm_linkid;
1292			bcopy(mac->fm_current_addr,
1293			    ports[i].fpi_mac_current_addr, ETHERADDRL);
1294			bcopy(mac->fm_primary_addr,
1295			    ports[i].fpi_mac_factory_addr, ETHERADDRL);
1296			ports[i].fpi_port_type =
1297			    EPORT_CLT_TYPE(&mac->fm_eport);
1298			ports[i].fpi_mtu_size =
1299			    mac->fm_eport.eport_mtu;
1300			ports[i].fpi_mac_promisc =
1301			    mac->fm_promisc_handle != NULL ? 1 : 0;
1302		}
1303		i++;
1304	}
1305	return (i);
1306}
1307