xref: /titanic_44/usr/src/uts/common/io/fcoe/fcoe.c (revision 1a5e258f5471356ca102c7176637cdce45bac147)
12a8164dfSZhong Wang /*
22a8164dfSZhong Wang  * CDDL HEADER START
32a8164dfSZhong Wang  *
42a8164dfSZhong Wang  * The contents of this file are subject to the terms of the
52a8164dfSZhong Wang  * Common Development and Distribution License (the "License").
62a8164dfSZhong Wang  * You may not use this file except in compliance with the License.
72a8164dfSZhong Wang  *
82a8164dfSZhong Wang  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92a8164dfSZhong Wang  * or http://www.opensolaris.org/os/licensing.
102a8164dfSZhong Wang  * See the License for the specific language governing permissions
112a8164dfSZhong Wang  * and limitations under the License.
122a8164dfSZhong Wang  *
132a8164dfSZhong Wang  * When distributing Covered Code, include this CDDL HEADER in each
142a8164dfSZhong Wang  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152a8164dfSZhong Wang  * If applicable, add the following below this CDDL HEADER, with the
162a8164dfSZhong Wang  * fields enclosed by brackets "[]" replaced with your own identifying
172a8164dfSZhong Wang  * information: Portions Copyright [yyyy] [name of copyright owner]
182a8164dfSZhong Wang  *
192a8164dfSZhong Wang  * CDDL HEADER END
202a8164dfSZhong Wang  */
212a8164dfSZhong Wang /*
222a8164dfSZhong Wang  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
232a8164dfSZhong Wang  * Use is subject to license terms.
242a8164dfSZhong Wang  */
25cd21e7c5SGarrett D'Amore /*
26cd21e7c5SGarrett D'Amore  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
27cd21e7c5SGarrett D'Amore  */
282a8164dfSZhong Wang 
292a8164dfSZhong Wang /*
302a8164dfSZhong Wang  * The following notice accompanied the original version of this file:
312a8164dfSZhong Wang  *
322a8164dfSZhong Wang  * BSD LICENSE
332a8164dfSZhong Wang  *
342a8164dfSZhong Wang  * Copyright(c) 2007 Intel Corporation. All rights reserved.
352a8164dfSZhong Wang  * All rights reserved.
362a8164dfSZhong Wang  *
372a8164dfSZhong Wang  * Redistribution and use in source and binary forms, with or without
382a8164dfSZhong Wang  * modification, are permitted provided that the following conditions
392a8164dfSZhong Wang  * are met:
402a8164dfSZhong Wang  *
412a8164dfSZhong Wang  *   * Redistributions of source code must retain the above copyright
422a8164dfSZhong Wang  *     notice, this list of conditions and the following disclaimer.
432a8164dfSZhong Wang  *   * Redistributions in binary form must reproduce the above copyright
442a8164dfSZhong Wang  *     notice, this list of conditions and the following disclaimer in
452a8164dfSZhong Wang  *     the documentation and/or other materials provided with the
462a8164dfSZhong Wang  *     distribution.
472a8164dfSZhong Wang  *   * Neither the name of Intel Corporation nor the names of its
482a8164dfSZhong Wang  *     contributors may be used to endorse or promote products derived
492a8164dfSZhong Wang  *     from this software without specific prior written permission.
502a8164dfSZhong Wang  *
512a8164dfSZhong Wang  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
522a8164dfSZhong Wang  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
532a8164dfSZhong Wang  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
542a8164dfSZhong Wang  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
552a8164dfSZhong Wang  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
562a8164dfSZhong Wang  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
572a8164dfSZhong Wang  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
582a8164dfSZhong Wang  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
592a8164dfSZhong Wang  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
602a8164dfSZhong Wang  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
612a8164dfSZhong Wang  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
622a8164dfSZhong Wang  */
632a8164dfSZhong Wang 
642a8164dfSZhong Wang /*
652a8164dfSZhong Wang  * Common FCoE interface interacts with MAC and FCoE clients, managing
662a8164dfSZhong Wang  * FCoE ports, doing MAC address discovery/managment, and FC frame
672a8164dfSZhong Wang  * encapsulation/decapsulation
682a8164dfSZhong Wang  */
692a8164dfSZhong Wang 
702a8164dfSZhong Wang #include <sys/stat.h>
712a8164dfSZhong Wang #include <sys/conf.h>
722a8164dfSZhong Wang #include <sys/file.h>
732a8164dfSZhong Wang #include <sys/cred.h>
742a8164dfSZhong Wang 
752a8164dfSZhong Wang #include <sys/ddi.h>
762a8164dfSZhong Wang #include <sys/sunddi.h>
772a8164dfSZhong Wang #include <sys/sunndi.h>
782a8164dfSZhong Wang #include <sys/byteorder.h>
792a8164dfSZhong Wang #include <sys/atomic.h>
802a8164dfSZhong Wang #include <sys/sysmacros.h>
812a8164dfSZhong Wang #include <sys/cmn_err.h>
822a8164dfSZhong Wang #include <sys/crc32.h>
832a8164dfSZhong Wang #include <sys/strsubr.h>
842a8164dfSZhong Wang 
852a8164dfSZhong Wang #include <sys/mac_client.h>
862a8164dfSZhong Wang 
872a8164dfSZhong Wang /*
882a8164dfSZhong Wang  * FCoE header files
892a8164dfSZhong Wang  */
902a8164dfSZhong Wang #include <sys/fcoe/fcoeio.h>
912a8164dfSZhong Wang #include <sys/fcoe/fcoe_common.h>
922a8164dfSZhong Wang 
932a8164dfSZhong Wang /*
942a8164dfSZhong Wang  * Driver's own header files
952a8164dfSZhong Wang  */
962a8164dfSZhong Wang #include <fcoe.h>
972a8164dfSZhong Wang #include <fcoe_fc.h>
982a8164dfSZhong Wang #include <fcoe_eth.h>
992a8164dfSZhong Wang 
1002a8164dfSZhong Wang /*
1012a8164dfSZhong Wang  * Function forward declaration
1022a8164dfSZhong Wang  */
1032a8164dfSZhong Wang static int fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1042a8164dfSZhong Wang static int fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1052a8164dfSZhong Wang static int fcoe_bus_ctl(dev_info_t *fca_dip, dev_info_t *rip,
1062a8164dfSZhong Wang     ddi_ctl_enum_t op, void *arg, void *result);
1072a8164dfSZhong Wang static int fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp);
1082a8164dfSZhong Wang static int fcoe_close(dev_t dev, int flag, int otype, cred_t *credp);
1092a8164dfSZhong Wang static int fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
1102a8164dfSZhong Wang     cred_t *credp, int *rval);
1112a8164dfSZhong Wang static int fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio,
1122a8164dfSZhong Wang     void **ibuf, void **abuf, void **obuf);
1132a8164dfSZhong Wang static int fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio,
1142a8164dfSZhong Wang     void *obuf);
1152a8164dfSZhong Wang static int fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode);
1162a8164dfSZhong Wang static int fcoe_attach_init(fcoe_soft_state_t *this_ss);
1172a8164dfSZhong Wang static int fcoe_detach_uninit(fcoe_soft_state_t *this_ss);
1182a8164dfSZhong Wang static int fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip);
1192a8164dfSZhong Wang static int fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip);
1202a8164dfSZhong Wang static void fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac,
1212a8164dfSZhong Wang     int is_pwwn, uint8_t idx);
122d4401b99SKelly Hu static fcoe_mac_t *fcoe_create_mac_by_id(datalink_id_t linkid);
1232a8164dfSZhong Wang static int fcoe_cmp_wwn(fcoe_mac_t *checkedmac);
1242a8164dfSZhong Wang static void fcoe_watchdog(void *arg);
1252a8164dfSZhong Wang static void fcoe_worker_init();
1262a8164dfSZhong Wang static int fcoe_worker_fini();
1272a8164dfSZhong Wang static void fcoe_worker_frame();
1282a8164dfSZhong Wang static int fcoe_get_port_list(fcoe_port_instance_t *ports, int count);
12987dcbdbdSKevin Yu static boolean_t fcoe_mac_existed(fcoe_mac_t *pmac);
1302a8164dfSZhong Wang 
1312a8164dfSZhong Wang /*
1322a8164dfSZhong Wang  * Driver identificaton stuff
1332a8164dfSZhong Wang  */
1342a8164dfSZhong Wang static struct cb_ops fcoe_cb_ops = {
1352a8164dfSZhong Wang 	fcoe_open,
1362a8164dfSZhong Wang 	fcoe_close,
1372a8164dfSZhong Wang 	nodev,
1382a8164dfSZhong Wang 	nodev,
1392a8164dfSZhong Wang 	nodev,
1402a8164dfSZhong Wang 	nodev,
1412a8164dfSZhong Wang 	nodev,
1422a8164dfSZhong Wang 	fcoe_ioctl,
1432a8164dfSZhong Wang 	nodev,
1442a8164dfSZhong Wang 	nodev,
1452a8164dfSZhong Wang 	nodev,
1462a8164dfSZhong Wang 	nochpoll,
1472a8164dfSZhong Wang 	ddi_prop_op,
1482a8164dfSZhong Wang 	0,
1492a8164dfSZhong Wang 	D_MP | D_NEW | D_HOTPLUG,
1502a8164dfSZhong Wang 	CB_REV,
1512a8164dfSZhong Wang 	nodev,
1522a8164dfSZhong Wang 	nodev
1532a8164dfSZhong Wang };
1542a8164dfSZhong Wang 
1552a8164dfSZhong Wang static struct bus_ops fcoe_busops = {
1562a8164dfSZhong Wang 	BUSO_REV,
1572a8164dfSZhong Wang 	nullbusmap,			/* bus_map */
1582a8164dfSZhong Wang 	NULL,				/* bus_get_intrspec */
1592a8164dfSZhong Wang 	NULL,				/* bus_add_intrspec */
1602a8164dfSZhong Wang 	NULL,				/* bus_remove_intrspec */
1612a8164dfSZhong Wang 	i_ddi_map_fault,		/* bus_map_fault */
162cd21e7c5SGarrett D'Amore 	NULL,				/* bus_dma_map */
1632a8164dfSZhong Wang 	ddi_dma_allochdl,		/* bus_dma_allochdl */
1642a8164dfSZhong Wang 	ddi_dma_freehdl,		/* bus_dma_freehdl */
1652a8164dfSZhong Wang 	ddi_dma_bindhdl,		/* bus_dma_bindhdl */
1662a8164dfSZhong Wang 	ddi_dma_unbindhdl,		/* bus_unbindhdl */
1672a8164dfSZhong Wang 	ddi_dma_flush,			/* bus_dma_flush */
1682a8164dfSZhong Wang 	ddi_dma_win,			/* bus_dma_win */
1692a8164dfSZhong Wang 	ddi_dma_mctl,			/* bus_dma_ctl */
1702a8164dfSZhong Wang 	fcoe_bus_ctl,			/* bus_ctl */
1712a8164dfSZhong Wang 	ddi_bus_prop_op,		/* bus_prop_op */
1722a8164dfSZhong Wang 	NULL,				/* bus_get_eventcookie */
1732a8164dfSZhong Wang 	NULL,				/* bus_add_eventcall */
1742a8164dfSZhong Wang 	NULL,				/* bus_remove_event */
1752a8164dfSZhong Wang 	NULL,				/* bus_post_event */
1762a8164dfSZhong Wang 	NULL,				/* bus_intr_ctl */
1772a8164dfSZhong Wang 	NULL,				/* bus_config */
1782a8164dfSZhong Wang 	NULL,				/* bus_unconfig */
1792a8164dfSZhong Wang 	NULL,				/* bus_fm_init */
1802a8164dfSZhong Wang 	NULL,				/* bus_fm_fini */
1812a8164dfSZhong Wang 	NULL,				/* bus_fm_access_enter */
1822a8164dfSZhong Wang 	NULL,				/* bus_fm_access_exit */
1832a8164dfSZhong Wang 	NULL,				/* bus_power */
1842a8164dfSZhong Wang 	NULL
1852a8164dfSZhong Wang };
1862a8164dfSZhong Wang 
1872a8164dfSZhong Wang static struct dev_ops fcoe_ops = {
1882a8164dfSZhong Wang 	DEVO_REV,
1892a8164dfSZhong Wang 	0,
1902a8164dfSZhong Wang 	nodev,
1912a8164dfSZhong Wang 	nulldev,
1922a8164dfSZhong Wang 	nulldev,
1932a8164dfSZhong Wang 	fcoe_attach,
1942a8164dfSZhong Wang 	fcoe_detach,
1952a8164dfSZhong Wang 	nodev,
1962a8164dfSZhong Wang 	&fcoe_cb_ops,
1972a8164dfSZhong Wang 	&fcoe_busops,
1983b753e05SZhong Wang 	ddi_power,
1993b753e05SZhong Wang 	ddi_quiesce_not_needed
2002a8164dfSZhong Wang };
2012a8164dfSZhong Wang 
20287dcbdbdSKevin Yu #define	FCOE_VERSION	"20091123-1.02"
2032a8164dfSZhong Wang #define	FCOE_NAME	"FCoE Transport v" FCOE_VERSION
2042a8164dfSZhong Wang #define	TASKQ_NAME_LEN	32
2052a8164dfSZhong Wang 
2062a8164dfSZhong Wang static struct modldrv modldrv = {
2072a8164dfSZhong Wang 	&mod_driverops,
2082a8164dfSZhong Wang 	FCOE_NAME,
2092a8164dfSZhong Wang 	&fcoe_ops,
2102a8164dfSZhong Wang };
2112a8164dfSZhong Wang 
2122a8164dfSZhong Wang static struct modlinkage modlinkage = {
2132a8164dfSZhong Wang 	MODREV_1, &modldrv, NULL
2142a8164dfSZhong Wang };
2152a8164dfSZhong Wang 
2162a8164dfSZhong Wang /*
2172a8164dfSZhong Wang  * TRACE for all FCoE related modules
2182a8164dfSZhong Wang  */
2192a8164dfSZhong Wang static kmutex_t fcoe_trace_buf_lock;
2202a8164dfSZhong Wang static int	fcoe_trace_buf_curndx	= 0;
2212a8164dfSZhong Wang static int	fcoe_trace_on		= 1;
2222a8164dfSZhong Wang static caddr_t	fcoe_trace_buf		= NULL;
2232a8164dfSZhong Wang static clock_t	fcoe_trace_start	= 0;
2242a8164dfSZhong Wang static caddr_t	ftb			= NULL;
2252a8164dfSZhong Wang static int	fcoe_trace_buf_size	= (1 * 1024 * 1024);
2262a8164dfSZhong Wang 
2272a8164dfSZhong Wang /*
2282a8164dfSZhong Wang  * Driver's global variables
2292a8164dfSZhong Wang  */
2307ff83669SZhong Wang const fcoe_ver_e	 fcoe_ver_now	  = FCOE_VER_NOW;
2312a8164dfSZhong Wang static void		*fcoe_state	  = NULL;
2322a8164dfSZhong Wang fcoe_soft_state_t	*fcoe_global_ss	  = NULL;
2332a8164dfSZhong Wang int			 fcoe_use_ext_log = 1;
2342a8164dfSZhong Wang 
2352a8164dfSZhong Wang static ddi_taskq_t	*fcoe_worker_taskq;
2362a8164dfSZhong Wang static fcoe_worker_t	*fcoe_workers;
2372a8164dfSZhong Wang static uint32_t		fcoe_nworkers_running;
2382a8164dfSZhong Wang 
2392a8164dfSZhong Wang const char		*fcoe_workers_num = "workers-number";
2402a8164dfSZhong Wang volatile int		fcoe_nworkers;
2412a8164dfSZhong Wang 
2422a8164dfSZhong Wang /*
2432a8164dfSZhong Wang  * Common loadable module entry points _init, _fini, _info
2442a8164dfSZhong Wang  */
2452a8164dfSZhong Wang 
2462a8164dfSZhong Wang int
_init(void)2472a8164dfSZhong Wang _init(void)
2482a8164dfSZhong Wang {
2492a8164dfSZhong Wang 	int ret;
2502a8164dfSZhong Wang 
2512a8164dfSZhong Wang 	ret = ddi_soft_state_init(&fcoe_state, sizeof (fcoe_soft_state_t), 0);
2522a8164dfSZhong Wang 	if (ret == 0) {
2532a8164dfSZhong Wang 		ret = mod_install(&modlinkage);
2542a8164dfSZhong Wang 		if (ret != 0) {
2552a8164dfSZhong Wang 			ddi_soft_state_fini(&fcoe_state);
2562a8164dfSZhong Wang 		} else {
2572a8164dfSZhong Wang 			fcoe_trace_start = ddi_get_lbolt();
2582a8164dfSZhong Wang 			ftb = kmem_zalloc(fcoe_trace_buf_size,
2592a8164dfSZhong Wang 			    KM_SLEEP);
2602a8164dfSZhong Wang 			fcoe_trace_buf = ftb;
2612a8164dfSZhong Wang 			mutex_init(&fcoe_trace_buf_lock, NULL, MUTEX_DRIVER, 0);
2622a8164dfSZhong Wang 		}
2632a8164dfSZhong Wang 	}
2642a8164dfSZhong Wang 
2652a8164dfSZhong Wang 	FCOE_LOG("fcoe", "exit _init with %x", ret);
2662a8164dfSZhong Wang 
2672a8164dfSZhong Wang 	return (ret);
2682a8164dfSZhong Wang }
2692a8164dfSZhong Wang 
2702a8164dfSZhong Wang int
_fini(void)2712a8164dfSZhong Wang _fini(void)
2722a8164dfSZhong Wang {
2732a8164dfSZhong Wang 	int ret;
2742a8164dfSZhong Wang 
2752a8164dfSZhong Wang 	ret = mod_remove(&modlinkage);
2762a8164dfSZhong Wang 	if (ret == 0) {
2772a8164dfSZhong Wang 		ddi_soft_state_fini(&fcoe_state);
2782a8164dfSZhong Wang 	}
2792a8164dfSZhong Wang 
2802a8164dfSZhong Wang 	FCOE_LOG("fcoe", "exit _fini with %x", ret);
2812a8164dfSZhong Wang 	if (ret == 0) {
2822a8164dfSZhong Wang 		kmem_free(fcoe_trace_buf, fcoe_trace_buf_size);
2832a8164dfSZhong Wang 		mutex_destroy(&fcoe_trace_buf_lock);
2842a8164dfSZhong Wang 	}
2852a8164dfSZhong Wang 
2862a8164dfSZhong Wang 	return (ret);
2872a8164dfSZhong Wang }
2882a8164dfSZhong Wang 
2892a8164dfSZhong Wang int
_info(struct modinfo * modinfop)2902a8164dfSZhong Wang _info(struct modinfo *modinfop)
2912a8164dfSZhong Wang {
2922a8164dfSZhong Wang 	return (mod_info(&modlinkage, modinfop));
2932a8164dfSZhong Wang }
2942a8164dfSZhong Wang 
2952a8164dfSZhong Wang /*
2962a8164dfSZhong Wang  * Autoconfiguration entry points: attach, detach, getinfo
2972a8164dfSZhong Wang  */
2982a8164dfSZhong Wang 
2992a8164dfSZhong Wang static int
fcoe_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3002a8164dfSZhong Wang fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3012a8164dfSZhong Wang {
3022a8164dfSZhong Wang 	int			 ret = DDI_FAILURE;
3032a8164dfSZhong Wang 	int			 fcoe_ret;
3042a8164dfSZhong Wang 	int			 instance;
3052a8164dfSZhong Wang 	fcoe_soft_state_t	*ss;
3062a8164dfSZhong Wang 
3072a8164dfSZhong Wang 	instance = ddi_get_instance(dip);
3082a8164dfSZhong Wang 	switch (cmd) {
3092a8164dfSZhong Wang 	case DDI_ATTACH:
3102a8164dfSZhong Wang 		ret = ddi_soft_state_zalloc(fcoe_state, instance);
3112a8164dfSZhong Wang 		if (ret == DDI_FAILURE) {
3122a8164dfSZhong Wang 			FCOE_LOG(0, "soft_state_zalloc-%x/%x", ret, instance);
3132a8164dfSZhong Wang 			return (ret);
3142a8164dfSZhong Wang 		}
3152a8164dfSZhong Wang 
3162a8164dfSZhong Wang 		ss = ddi_get_soft_state(fcoe_state, instance);
3172a8164dfSZhong Wang 		ss->ss_dip = dip;
3182a8164dfSZhong Wang 
3192a8164dfSZhong Wang 		ASSERT(fcoe_global_ss == NULL);
3202a8164dfSZhong Wang 		fcoe_global_ss = ss;
3212a8164dfSZhong Wang 		fcoe_ret = fcoe_attach_init(ss);
3222a8164dfSZhong Wang 		if (fcoe_ret == FCOE_SUCCESS) {
3232a8164dfSZhong Wang 			ret = DDI_SUCCESS;
3242a8164dfSZhong Wang 		}
3252a8164dfSZhong Wang 
3262a8164dfSZhong Wang 		FCOE_LOG("fcoe", "fcoe_attach_init end with-%x", fcoe_ret);
3272a8164dfSZhong Wang 		break;
3282a8164dfSZhong Wang 
3292a8164dfSZhong Wang 	case DDI_RESUME:
3302a8164dfSZhong Wang 		ret = DDI_SUCCESS;
3312a8164dfSZhong Wang 		break;
3322a8164dfSZhong Wang 
3332a8164dfSZhong Wang 	default:
3342a8164dfSZhong Wang 		FCOE_LOG("fcoe", "unsupported attach cmd-%x", cmd);
3352a8164dfSZhong Wang 		break;
3362a8164dfSZhong Wang 	}
3372a8164dfSZhong Wang 
3382a8164dfSZhong Wang 	return (ret);
3392a8164dfSZhong Wang }
3402a8164dfSZhong Wang 
3412a8164dfSZhong Wang static int
fcoe_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3422a8164dfSZhong Wang fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3432a8164dfSZhong Wang {
3442a8164dfSZhong Wang 	int			 ret = DDI_FAILURE;
3452a8164dfSZhong Wang 	int			 fcoe_ret;
3462a8164dfSZhong Wang 	int			 instance;
3472a8164dfSZhong Wang 	fcoe_soft_state_t	*ss;
3482a8164dfSZhong Wang 
3492a8164dfSZhong Wang 	instance = ddi_get_instance(dip);
3502a8164dfSZhong Wang 	ss = ddi_get_soft_state(fcoe_state, instance);
3512a8164dfSZhong Wang 	if (ss == NULL) {
3522a8164dfSZhong Wang 		return (ret);
3532a8164dfSZhong Wang 	}
3542a8164dfSZhong Wang 
3552a8164dfSZhong Wang 	ASSERT(fcoe_global_ss != NULL);
3562a8164dfSZhong Wang 	ASSERT(dip == fcoe_global_ss->ss_dip);
3572a8164dfSZhong Wang 	switch (cmd) {
3582a8164dfSZhong Wang 	case DDI_DETACH:
3592a8164dfSZhong Wang 		fcoe_ret = fcoe_detach_uninit(ss);
3602a8164dfSZhong Wang 		if (fcoe_ret == FCOE_SUCCESS) {
3612a8164dfSZhong Wang 			ret = DDI_SUCCESS;
3622a8164dfSZhong Wang 			fcoe_global_ss = NULL;
3632a8164dfSZhong Wang 		}
3642a8164dfSZhong Wang 
3652a8164dfSZhong Wang 		break;
3662a8164dfSZhong Wang 
3672a8164dfSZhong Wang 	case DDI_SUSPEND:
3682a8164dfSZhong Wang 		ret = DDI_SUCCESS;
3692a8164dfSZhong Wang 		break;
3702a8164dfSZhong Wang 
3712a8164dfSZhong Wang 	default:
3722a8164dfSZhong Wang 		FCOE_LOG(0, "unsupported detach cmd-%x", cmd);
3732a8164dfSZhong Wang 		break;
3742a8164dfSZhong Wang 	}
3752a8164dfSZhong Wang 
3762a8164dfSZhong Wang 	return (ret);
3772a8164dfSZhong Wang }
3782a8164dfSZhong Wang 
3792a8164dfSZhong Wang /*
3802a8164dfSZhong Wang  * FCA driver's intercepted bus control operations.
3812a8164dfSZhong Wang  */
3822a8164dfSZhong Wang static int
fcoe_bus_ctl(dev_info_t * fcoe_dip,dev_info_t * rip,ddi_ctl_enum_t op,void * clientarg,void * result)3832a8164dfSZhong Wang fcoe_bus_ctl(dev_info_t *fcoe_dip, dev_info_t *rip,
3842a8164dfSZhong Wang     ddi_ctl_enum_t op, void *clientarg, void *result)
3852a8164dfSZhong Wang {
3862a8164dfSZhong Wang 	int ret;
3872a8164dfSZhong Wang 	switch (op) {
3882a8164dfSZhong Wang 	case DDI_CTLOPS_REPORTDEV:
3892a8164dfSZhong Wang 	case DDI_CTLOPS_IOMIN:
3902a8164dfSZhong Wang 		ret = DDI_SUCCESS;
3912a8164dfSZhong Wang 		break;
3922a8164dfSZhong Wang 
3932a8164dfSZhong Wang 	case DDI_CTLOPS_INITCHILD:
3942a8164dfSZhong Wang 		ret = fcoe_initchild(fcoe_dip, (dev_info_t *)clientarg);
3952a8164dfSZhong Wang 		break;
3962a8164dfSZhong Wang 
3972a8164dfSZhong Wang 	case DDI_CTLOPS_UNINITCHILD:
3982a8164dfSZhong Wang 		ret = fcoe_uninitchild(fcoe_dip, (dev_info_t *)clientarg);
3992a8164dfSZhong Wang 		break;
4002a8164dfSZhong Wang 
4012a8164dfSZhong Wang 	default:
4022a8164dfSZhong Wang 		ret = ddi_ctlops(fcoe_dip, rip, op, clientarg, result);
4032a8164dfSZhong Wang 		break;
4042a8164dfSZhong Wang 	}
4052a8164dfSZhong Wang 
4062a8164dfSZhong Wang 	return (ret);
4072a8164dfSZhong Wang }
4082a8164dfSZhong Wang 
4092a8164dfSZhong Wang /*
4102a8164dfSZhong Wang  * We need specify the dev address for client driver's instance, or we
4112a8164dfSZhong Wang  * can't online client driver's instance.
4122a8164dfSZhong Wang  */
4132a8164dfSZhong Wang /* ARGSUSED */
4142a8164dfSZhong Wang static int
fcoe_initchild(dev_info_t * fcoe_dip,dev_info_t * client_dip)4152a8164dfSZhong Wang fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip)
4162a8164dfSZhong Wang {
4177ff83669SZhong Wang 	char	client_addr[FCOE_STR_LEN];
4187ff83669SZhong Wang 	int	rval;
4192a8164dfSZhong Wang 
4207ff83669SZhong Wang 	rval = ddi_prop_get_int(DDI_DEV_T_ANY, client_dip,
4217ff83669SZhong Wang 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "mac_id", -1);
4227ff83669SZhong Wang 	if (rval == -1) {
4237ff83669SZhong Wang 		FCOE_LOG(__FUNCTION__, "no mac_id property: %p", client_dip);
4247ff83669SZhong Wang 		return (DDI_FAILURE);
4252a8164dfSZhong Wang 	}
4262a8164dfSZhong Wang 
4277ff83669SZhong Wang 	bzero(client_addr, FCOE_STR_LEN);
4287ff83669SZhong Wang 	(void) sprintf((char *)client_addr, "%x,0", rval);
4297ff83669SZhong Wang 	ddi_set_name_addr(client_dip, client_addr);
4302a8164dfSZhong Wang 	return (DDI_SUCCESS);
4312a8164dfSZhong Wang }
4322a8164dfSZhong Wang 
4332a8164dfSZhong Wang /* ARGSUSED */
4342a8164dfSZhong Wang static int
fcoe_uninitchild(dev_info_t * fcoe_dip,dev_info_t * client_dip)4352a8164dfSZhong Wang fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip)
4362a8164dfSZhong Wang {
4372a8164dfSZhong Wang 	ddi_set_name_addr(client_dip, NULL);
4382a8164dfSZhong Wang 	return (DDI_SUCCESS);
4392a8164dfSZhong Wang }
4402a8164dfSZhong Wang 
4412a8164dfSZhong Wang /*
4422a8164dfSZhong Wang  * Device access entry points
4432a8164dfSZhong Wang  */
4442a8164dfSZhong Wang static int
fcoe_open(dev_t * devp,int flag,int otype,cred_t * credp)4452a8164dfSZhong Wang fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp)
4462a8164dfSZhong Wang {
4472a8164dfSZhong Wang 	int			 instance;
4482a8164dfSZhong Wang 	fcoe_soft_state_t	*ss;
4492a8164dfSZhong Wang 
4502a8164dfSZhong Wang 	if (otype != OTYP_CHR) {
4512a8164dfSZhong Wang 		return (EINVAL);
4522a8164dfSZhong Wang 	}
4532a8164dfSZhong Wang 
4542a8164dfSZhong Wang 	/*
4552a8164dfSZhong Wang 	 * Since this is for debugging only, only allow root to issue ioctl now
4562a8164dfSZhong Wang 	 */
4572a8164dfSZhong Wang 	if (drv_priv(credp) != 0) {
4582a8164dfSZhong Wang 		return (EPERM);
4592a8164dfSZhong Wang 	}
4602a8164dfSZhong Wang 
4612a8164dfSZhong Wang 	instance = (int)getminor(*devp);
4622a8164dfSZhong Wang 	ss = ddi_get_soft_state(fcoe_state, instance);
4632a8164dfSZhong Wang 	if (ss == NULL) {
4642a8164dfSZhong Wang 		return (ENXIO);
4652a8164dfSZhong Wang 	}
4662a8164dfSZhong Wang 
4672a8164dfSZhong Wang 	mutex_enter(&ss->ss_ioctl_mutex);
4682a8164dfSZhong Wang 	if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) {
4692a8164dfSZhong Wang 		/*
4702a8164dfSZhong Wang 		 * It is already open for exclusive access.
4712a8164dfSZhong Wang 		 * So shut the door on this caller.
4722a8164dfSZhong Wang 		 */
4732a8164dfSZhong Wang 		mutex_exit(&ss->ss_ioctl_mutex);
4742a8164dfSZhong Wang 		return (EBUSY);
4752a8164dfSZhong Wang 	}
4762a8164dfSZhong Wang 
4772a8164dfSZhong Wang 	if (flag & FEXCL) {
4782a8164dfSZhong Wang 		if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) {
4792a8164dfSZhong Wang 			/*
4802a8164dfSZhong Wang 			 * Exclusive operation not possible
4812a8164dfSZhong Wang 			 * as it is already opened
4822a8164dfSZhong Wang 			 */
4832a8164dfSZhong Wang 			mutex_exit(&ss->ss_ioctl_mutex);
4842a8164dfSZhong Wang 			return (EBUSY);
4852a8164dfSZhong Wang 		}
4862a8164dfSZhong Wang 		ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL;
4872a8164dfSZhong Wang 	}
4882a8164dfSZhong Wang 
4892a8164dfSZhong Wang 	ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_OPEN;
4902a8164dfSZhong Wang 	mutex_exit(&ss->ss_ioctl_mutex);
4912a8164dfSZhong Wang 
4922a8164dfSZhong Wang 	return (0);
4932a8164dfSZhong Wang }
4942a8164dfSZhong Wang 
4952a8164dfSZhong Wang /* ARGSUSED */
4962a8164dfSZhong Wang static int
fcoe_close(dev_t dev,int flag,int otype,cred_t * credp)4972a8164dfSZhong Wang fcoe_close(dev_t dev, int flag, int otype, cred_t *credp)
4982a8164dfSZhong Wang {
4992a8164dfSZhong Wang 	int			 instance;
5002a8164dfSZhong Wang 	fcoe_soft_state_t	*ss;
5012a8164dfSZhong Wang 
5022a8164dfSZhong Wang 	if (otype != OTYP_CHR) {
5032a8164dfSZhong Wang 		return (EINVAL);
5042a8164dfSZhong Wang 	}
5052a8164dfSZhong Wang 
5062a8164dfSZhong Wang 	instance = (int)getminor(dev);
5072a8164dfSZhong Wang 	ss = ddi_get_soft_state(fcoe_state, instance);
5082a8164dfSZhong Wang 	if (ss == NULL) {
5092a8164dfSZhong Wang 		return (ENXIO);
5102a8164dfSZhong Wang 	}
5112a8164dfSZhong Wang 
5122a8164dfSZhong Wang 	mutex_enter(&ss->ss_ioctl_mutex);
5132a8164dfSZhong Wang 	if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) {
5142a8164dfSZhong Wang 		mutex_exit(&ss->ss_ioctl_mutex);
5152a8164dfSZhong Wang 		return (ENODEV);
5162a8164dfSZhong Wang 	}
5172a8164dfSZhong Wang 
5182a8164dfSZhong Wang 	ss->ss_ioctl_flags &= ~FCOE_IOCTL_FLAG_MASK;
5192a8164dfSZhong Wang 	mutex_exit(&ss->ss_ioctl_mutex);
5202a8164dfSZhong Wang 
5212a8164dfSZhong Wang 	return (0);
5222a8164dfSZhong Wang }
5232a8164dfSZhong Wang 
5242a8164dfSZhong Wang /* ARGSUSED */
5252a8164dfSZhong Wang static int
fcoe_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rval)5262a8164dfSZhong Wang fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
5272a8164dfSZhong Wang     cred_t *credp, int *rval)
5282a8164dfSZhong Wang {
5292a8164dfSZhong Wang 	fcoe_soft_state_t	*ss;
5302a8164dfSZhong Wang 	int			 ret = 0;
5312a8164dfSZhong Wang 
5322a8164dfSZhong Wang 	if (drv_priv(credp) != 0) {
5332a8164dfSZhong Wang 		return (EPERM);
5342a8164dfSZhong Wang 	}
5352a8164dfSZhong Wang 
5362a8164dfSZhong Wang 	ss = ddi_get_soft_state(fcoe_state, (int32_t)getminor(dev));
5372a8164dfSZhong Wang 	if (ss == NULL) {
5382a8164dfSZhong Wang 		return (ENXIO);
5392a8164dfSZhong Wang 	}
5402a8164dfSZhong Wang 
5412a8164dfSZhong Wang 	mutex_enter(&ss->ss_ioctl_mutex);
5422a8164dfSZhong Wang 	if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) {
5432a8164dfSZhong Wang 		mutex_exit(&ss->ss_ioctl_mutex);
5442a8164dfSZhong Wang 		return (ENXIO);
5452a8164dfSZhong Wang 	}
5462a8164dfSZhong Wang 	mutex_exit(&ss->ss_ioctl_mutex);
5472a8164dfSZhong Wang 
5482a8164dfSZhong Wang 	switch (cmd) {
5492a8164dfSZhong Wang 	case FCOEIO_CMD:
5502a8164dfSZhong Wang 		ret = fcoe_iocmd(ss, data, mode);
5512a8164dfSZhong Wang 		break;
5522a8164dfSZhong Wang 	default:
5532a8164dfSZhong Wang 		FCOE_LOG(0, "fcoe_ioctl: ioctl-0x%02X", cmd);
5542a8164dfSZhong Wang 		ret = ENOTTY;
5552a8164dfSZhong Wang 		break;
5562a8164dfSZhong Wang 	}
5572a8164dfSZhong Wang 
5582a8164dfSZhong Wang 	return (ret);
5592a8164dfSZhong Wang }
5602a8164dfSZhong Wang 
5612a8164dfSZhong Wang static int
fcoe_copyin_iocdata(intptr_t data,int mode,fcoeio_t ** fcoeio,void ** ibuf,void ** abuf,void ** obuf)5622a8164dfSZhong Wang fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio,
5632a8164dfSZhong Wang     void **ibuf, void **abuf, void **obuf)
5642a8164dfSZhong Wang {
5652a8164dfSZhong Wang 	int ret = 0;
5662a8164dfSZhong Wang 
5672a8164dfSZhong Wang 	*ibuf = NULL;
5682a8164dfSZhong Wang 	*abuf = NULL;
5692a8164dfSZhong Wang 	*obuf = NULL;
5702a8164dfSZhong Wang 	*fcoeio = kmem_zalloc(sizeof (fcoeio_t), KM_SLEEP);
5712a8164dfSZhong Wang 	if (ddi_copyin((void *)data, *fcoeio, sizeof (fcoeio_t), mode) != 0) {
5722a8164dfSZhong Wang 		ret = EFAULT;
5732a8164dfSZhong Wang 		goto copyin_iocdata_fail;
5742a8164dfSZhong Wang 	}
5752a8164dfSZhong Wang 
5762a8164dfSZhong Wang 	if ((*fcoeio)->fcoeio_ilen > FCOEIO_MAX_BUF_LEN ||
5772a8164dfSZhong Wang 	    (*fcoeio)->fcoeio_alen > FCOEIO_MAX_BUF_LEN ||
5782a8164dfSZhong Wang 	    (*fcoeio)->fcoeio_olen > FCOEIO_MAX_BUF_LEN) {
5792a8164dfSZhong Wang 		ret = EFAULT;
5802a8164dfSZhong Wang 		goto copyin_iocdata_fail;
5812a8164dfSZhong Wang 	}
5822a8164dfSZhong Wang 
5832a8164dfSZhong Wang 	if ((*fcoeio)->fcoeio_ilen) {
5842a8164dfSZhong Wang 		*ibuf = kmem_zalloc((*fcoeio)->fcoeio_ilen, KM_SLEEP);
5852a8164dfSZhong Wang 		if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_ibuf,
5862a8164dfSZhong Wang 		    *ibuf, (*fcoeio)->fcoeio_ilen, mode) != 0) {
5872a8164dfSZhong Wang 			ret = EFAULT;
5882a8164dfSZhong Wang 			goto copyin_iocdata_fail;
5892a8164dfSZhong Wang 		}
5902a8164dfSZhong Wang 	}
5912a8164dfSZhong Wang 
5922a8164dfSZhong Wang 	if ((*fcoeio)->fcoeio_alen) {
5932a8164dfSZhong Wang 		*abuf = kmem_zalloc((*fcoeio)->fcoeio_alen, KM_SLEEP);
5942a8164dfSZhong Wang 		if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_abuf,
5952a8164dfSZhong Wang 		    *abuf, (*fcoeio)->fcoeio_alen, mode) != 0) {
5962a8164dfSZhong Wang 			ret = EFAULT;
5972a8164dfSZhong Wang 			goto copyin_iocdata_fail;
5982a8164dfSZhong Wang 		}
5992a8164dfSZhong Wang 	}
6002a8164dfSZhong Wang 
6012a8164dfSZhong Wang 	if ((*fcoeio)->fcoeio_olen) {
6022a8164dfSZhong Wang 		*obuf = kmem_zalloc((*fcoeio)->fcoeio_olen, KM_SLEEP);
6032a8164dfSZhong Wang 	}
6042a8164dfSZhong Wang 	return (ret);
6052a8164dfSZhong Wang 
6062a8164dfSZhong Wang copyin_iocdata_fail:
6072a8164dfSZhong Wang 	if (*abuf) {
6082a8164dfSZhong Wang 		kmem_free(*abuf, (*fcoeio)->fcoeio_alen);
6092a8164dfSZhong Wang 		*abuf = NULL;
6102a8164dfSZhong Wang 	}
6112a8164dfSZhong Wang 
6122a8164dfSZhong Wang 	if (*ibuf) {
6132a8164dfSZhong Wang 		kmem_free(*ibuf, (*fcoeio)->fcoeio_ilen);
6142a8164dfSZhong Wang 		*ibuf = NULL;
6152a8164dfSZhong Wang 	}
6162a8164dfSZhong Wang 
6172a8164dfSZhong Wang 	kmem_free(*fcoeio, sizeof (fcoeio_t));
6182a8164dfSZhong Wang 	return (ret);
6192a8164dfSZhong Wang }
6202a8164dfSZhong Wang 
6212a8164dfSZhong Wang static int
fcoe_copyout_iocdata(intptr_t data,int mode,fcoeio_t * fcoeio,void * obuf)6222a8164dfSZhong Wang fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, void *obuf)
6232a8164dfSZhong Wang {
6242a8164dfSZhong Wang 	if (fcoeio->fcoeio_olen) {
6252a8164dfSZhong Wang 		if (ddi_copyout(obuf,
6262a8164dfSZhong Wang 		    (void *)(unsigned long)fcoeio->fcoeio_obuf,
6272a8164dfSZhong Wang 		    fcoeio->fcoeio_olen, mode) != 0) {
6282a8164dfSZhong Wang 			return (EFAULT);
6292a8164dfSZhong Wang 		}
6302a8164dfSZhong Wang 	}
6312a8164dfSZhong Wang 
6322a8164dfSZhong Wang 	if (ddi_copyout(fcoeio, (void *)data, sizeof (fcoeio_t), mode) != 0) {
6332a8164dfSZhong Wang 		return (EFAULT);
6342a8164dfSZhong Wang 	}
6352a8164dfSZhong Wang 	return (0);
6362a8164dfSZhong Wang }
6372a8164dfSZhong Wang 
6382a8164dfSZhong Wang static int
fcoe_iocmd(fcoe_soft_state_t * ss,intptr_t data,int mode)6392a8164dfSZhong Wang fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode)
6402a8164dfSZhong Wang {
6412a8164dfSZhong Wang 	int		ret;
6422a8164dfSZhong Wang 	fcoe_mac_t	*fcoe_mac;
6432a8164dfSZhong Wang 	void		*ibuf = NULL;
6442a8164dfSZhong Wang 	void		*obuf = NULL;
6452a8164dfSZhong Wang 	void		*abuf = NULL;
6462a8164dfSZhong Wang 	fcoeio_t	*fcoeio;
6472a8164dfSZhong Wang 
6482a8164dfSZhong Wang 	ret = fcoe_copyin_iocdata(data, mode, &fcoeio, &ibuf, &abuf, &obuf);
6492a8164dfSZhong Wang 	if (ret != 0) {
6502a8164dfSZhong Wang 		goto fcoeiocmd_release_buf;
6512a8164dfSZhong Wang 	}
6522a8164dfSZhong Wang 
6532a8164dfSZhong Wang 	/*
6542a8164dfSZhong Wang 	 * If an exclusive open was demanded during open, ensure that
6552a8164dfSZhong Wang 	 * only one thread can execute an ioctl at a time
6562a8164dfSZhong Wang 	 */
6572a8164dfSZhong Wang 	mutex_enter(&ss->ss_ioctl_mutex);
6582a8164dfSZhong Wang 	if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) {
6592a8164dfSZhong Wang 		if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL_BUSY) {
6602a8164dfSZhong Wang 			mutex_exit(&ss->ss_ioctl_mutex);
6612a8164dfSZhong Wang 			fcoeio->fcoeio_status = FCOEIOE_BUSY;
6622a8164dfSZhong Wang 			ret = EBUSY;
6632a8164dfSZhong Wang 			goto fcoeiocmd_release_buf;
6642a8164dfSZhong Wang 		}
6652a8164dfSZhong Wang 		ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL_BUSY;
6662a8164dfSZhong Wang 	}
6672a8164dfSZhong Wang 	mutex_exit(&ss->ss_ioctl_mutex);
6682a8164dfSZhong Wang 
6692a8164dfSZhong Wang 	fcoeio->fcoeio_status = 0;
6702a8164dfSZhong Wang 
6712a8164dfSZhong Wang 	switch (fcoeio->fcoeio_cmd) {
6722a8164dfSZhong Wang 	case FCOEIO_CREATE_FCOE_PORT: {
6732a8164dfSZhong Wang 		fcoeio_create_port_param_t	*param =
6742a8164dfSZhong Wang 		    (fcoeio_create_port_param_t *)ibuf;
6752a8164dfSZhong Wang 		int		cmpwwn = 0;
6762a8164dfSZhong Wang 		fcoe_port_t	*eport;
6772a8164dfSZhong Wang 
6782a8164dfSZhong Wang 		if (fcoeio->fcoeio_ilen !=
6792a8164dfSZhong Wang 		    sizeof (fcoeio_create_port_param_t) ||
6802a8164dfSZhong Wang 		    fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) {
6812a8164dfSZhong Wang 			fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
6822a8164dfSZhong Wang 			ret = EINVAL;
6832a8164dfSZhong Wang 			break;
6842a8164dfSZhong Wang 		}
6852a8164dfSZhong Wang 
6862a8164dfSZhong Wang 		mutex_enter(&ss->ss_ioctl_mutex);
687d4401b99SKelly Hu 		fcoe_mac = fcoe_create_mac_by_id(param->fcp_mac_linkid);
6882a8164dfSZhong Wang 		if (fcoe_mac == NULL) {
6892a8164dfSZhong Wang 			mutex_exit(&ss->ss_ioctl_mutex);
6902a8164dfSZhong Wang 			fcoeio->fcoeio_status = FCOEIOE_CREATE_MAC;
6912a8164dfSZhong Wang 			ret = EIO;
6922a8164dfSZhong Wang 			break;
6932a8164dfSZhong Wang 		}
6942a8164dfSZhong Wang 
6952a8164dfSZhong Wang 		if (fcoe_mac->fm_flags & FCOE_MAC_FLAG_ENABLED) {
6962a8164dfSZhong Wang 			mutex_exit(&ss->ss_ioctl_mutex);
6972a8164dfSZhong Wang 			fcoeio->fcoeio_status = FCOEIOE_ALREADY;
6982a8164dfSZhong Wang 			ret = EALREADY;
6992a8164dfSZhong Wang 			break;
7002a8164dfSZhong Wang 		} else {
7012a8164dfSZhong Wang 			ret = fcoe_open_mac(fcoe_mac, param->fcp_force_promisc,
7022a8164dfSZhong Wang 			    &fcoeio->fcoeio_status);
7032a8164dfSZhong Wang 			if (ret != 0) {
7042a8164dfSZhong Wang 				fcoe_destroy_mac(fcoe_mac);
7052a8164dfSZhong Wang 				mutex_exit(&ss->ss_ioctl_mutex);
7062a8164dfSZhong Wang 				if (fcoeio->fcoeio_status == 0) {
7072a8164dfSZhong Wang 					fcoeio->fcoeio_status =
7082a8164dfSZhong Wang 					    FCOEIOE_OPEN_MAC;
7092a8164dfSZhong Wang 				}
7102a8164dfSZhong Wang 				ret = EIO;
7112a8164dfSZhong Wang 				break;
7122a8164dfSZhong Wang 			} else {
7132a8164dfSZhong Wang 				fcoe_mac->fm_flags |= FCOE_MAC_FLAG_ENABLED;
7142a8164dfSZhong Wang 			}
7152a8164dfSZhong Wang 		}
7162a8164dfSZhong Wang 
7172a8164dfSZhong Wang 		/*
7182a8164dfSZhong Wang 		 * Provide PWWN and NWWN based on mac address
7192a8164dfSZhong Wang 		 */
7202a8164dfSZhong Wang 		eport = &fcoe_mac->fm_eport;
7212a8164dfSZhong Wang 		if (!param->fcp_pwwn_provided) {
7222a8164dfSZhong Wang 			fcoe_init_wwn_from_mac(eport->eport_portwwn,
7232a8164dfSZhong Wang 			    fcoe_mac->fm_current_addr, 1, 0);
7242a8164dfSZhong Wang 		} else {
7252a8164dfSZhong Wang 			(void) memcpy(eport->eport_portwwn, param->fcp_pwwn, 8);
7262a8164dfSZhong Wang 		}
7272a8164dfSZhong Wang 
7282a8164dfSZhong Wang 		if (!param->fcp_nwwn_provided) {
7292a8164dfSZhong Wang 			fcoe_init_wwn_from_mac(eport->eport_nodewwn,
7302a8164dfSZhong Wang 			    fcoe_mac->fm_current_addr, 0, 0);
7312a8164dfSZhong Wang 		} else {
7322a8164dfSZhong Wang 			(void) memcpy(eport->eport_nodewwn, param->fcp_nwwn, 8);
7332a8164dfSZhong Wang 		}
7342a8164dfSZhong Wang 
7352a8164dfSZhong Wang 		cmpwwn = fcoe_cmp_wwn(fcoe_mac);
7362a8164dfSZhong Wang 
7372a8164dfSZhong Wang 		if (cmpwwn != 0) {
7382a8164dfSZhong Wang 			if (cmpwwn == 1) {
7392a8164dfSZhong Wang 				fcoeio->fcoeio_status = FCOEIOE_PWWN_CONFLICTED;
7402a8164dfSZhong Wang 			} else if (cmpwwn == -1) {
7412a8164dfSZhong Wang 				fcoeio->fcoeio_status = FCOEIOE_NWWN_CONFLICTED;
7422a8164dfSZhong Wang 			}
7432a8164dfSZhong Wang 			(void) fcoe_close_mac(fcoe_mac);
7442a8164dfSZhong Wang 			fcoe_destroy_mac(fcoe_mac);
7452a8164dfSZhong Wang 			mutex_exit(&ss->ss_ioctl_mutex);
7462a8164dfSZhong Wang 			ret = ENOTUNIQ;
7472a8164dfSZhong Wang 			break;
7482a8164dfSZhong Wang 		}
7492a8164dfSZhong Wang 
7502a8164dfSZhong Wang 		if (ret == 0) {
7512a8164dfSZhong Wang 			ret = fcoe_create_port(ss->ss_dip,
7522a8164dfSZhong Wang 			    fcoe_mac,
7532a8164dfSZhong Wang 			    (param->fcp_port_type == FCOE_CLIENT_TARGET));
7542a8164dfSZhong Wang 			if (ret != 0) {
75587dcbdbdSKevin Yu 				if (fcoe_mac_existed(fcoe_mac) == B_TRUE) {
7562a8164dfSZhong Wang 					(void) fcoe_close_mac(fcoe_mac);
7572a8164dfSZhong Wang 					fcoe_destroy_mac(fcoe_mac);
75887dcbdbdSKevin Yu 				}
7592a8164dfSZhong Wang 				fcoeio->fcoeio_status = FCOEIOE_CREATE_PORT;
7602a8164dfSZhong Wang 				ret = EIO;
7612a8164dfSZhong Wang 			}
7622a8164dfSZhong Wang 		}
7632a8164dfSZhong Wang 		mutex_exit(&ss->ss_ioctl_mutex);
7642a8164dfSZhong Wang 
7652a8164dfSZhong Wang 		break;
7662a8164dfSZhong Wang 	}
7672a8164dfSZhong Wang 
7682a8164dfSZhong Wang 	case FCOEIO_DELETE_FCOE_PORT: {
769d4401b99SKelly Hu 		fcoeio_delete_port_param_t *del_port_param =
770d4401b99SKelly Hu 		    (fcoeio_delete_port_param_t *)ibuf;
771e6eb57e7SKevin Yu 		uint64_t *is_target = (uint64_t *)obuf;
7722a8164dfSZhong Wang 
773d4401b99SKelly Hu 		if (fcoeio->fcoeio_ilen < sizeof (fcoeio_delete_port_param_t) ||
774e6eb57e7SKevin Yu 		    fcoeio->fcoeio_olen != sizeof (uint64_t) ||
775e6eb57e7SKevin Yu 		    fcoeio->fcoeio_xfer != FCOEIO_XFER_RW) {
7762a8164dfSZhong Wang 			fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
7772a8164dfSZhong Wang 			ret = EINVAL;
7782a8164dfSZhong Wang 			break;
7792a8164dfSZhong Wang 		}
7802a8164dfSZhong Wang 
7812a8164dfSZhong Wang 		mutex_enter(&ss->ss_ioctl_mutex);
782d4401b99SKelly Hu 		ret = fcoe_delete_port(ss->ss_dip, fcoeio,
783e6eb57e7SKevin Yu 		    del_port_param->fdp_mac_linkid, is_target);
7842a8164dfSZhong Wang 		mutex_exit(&ss->ss_ioctl_mutex);
7857ff83669SZhong Wang 		FCOE_LOG("fcoe", "fcoe_delete_port %x return: %d",
7867ff83669SZhong Wang 		    del_port_param->fdp_mac_linkid, ret);
7872a8164dfSZhong Wang 		break;
7882a8164dfSZhong Wang 	}
7892a8164dfSZhong Wang 
7902a8164dfSZhong Wang 	case FCOEIO_GET_FCOE_PORT_LIST: {
7912a8164dfSZhong Wang 		fcoe_port_list_t *list = (fcoe_port_list_t *)obuf;
7922a8164dfSZhong Wang 		int		count;
7932a8164dfSZhong Wang 
7942a8164dfSZhong Wang 		if (fcoeio->fcoeio_xfer != FCOEIO_XFER_READ ||
7952a8164dfSZhong Wang 		    fcoeio->fcoeio_olen < sizeof (fcoe_port_list_t)) {
7962a8164dfSZhong Wang 			fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
7972a8164dfSZhong Wang 			ret = EINVAL;
7982a8164dfSZhong Wang 			break;
7992a8164dfSZhong Wang 		}
8002a8164dfSZhong Wang 		mutex_enter(&ss->ss_ioctl_mutex);
8012a8164dfSZhong Wang 
8022a8164dfSZhong Wang 		list->numPorts = 1 + (fcoeio->fcoeio_olen -
8032a8164dfSZhong Wang 		    sizeof (fcoe_port_list_t))/sizeof (fcoe_port_instance_t);
8042a8164dfSZhong Wang 
8052a8164dfSZhong Wang 		count = fcoe_get_port_list(list->ports, list->numPorts);
8062a8164dfSZhong Wang 
8072a8164dfSZhong Wang 		if (count > list->numPorts) {
8082a8164dfSZhong Wang 			fcoeio->fcoeio_status = FCOEIOE_MORE_DATA;
8092a8164dfSZhong Wang 			ret = ENOSPC;
8102a8164dfSZhong Wang 		}
8112a8164dfSZhong Wang 		list->numPorts = count;
8122a8164dfSZhong Wang 		mutex_exit(&ss->ss_ioctl_mutex);
8132a8164dfSZhong Wang 
8142a8164dfSZhong Wang 		break;
8152a8164dfSZhong Wang 
8162a8164dfSZhong Wang 	}
8172a8164dfSZhong Wang 
8182a8164dfSZhong Wang 	default:
8192a8164dfSZhong Wang 		return (ENOTTY);
8202a8164dfSZhong Wang 	}
8212a8164dfSZhong Wang 
8227ff83669SZhong Wang 	FCOE_LOG("fcoe", "fcoe_ioctl %x returned %d, fcoeio_status = %d",
8237ff83669SZhong Wang 	    fcoeio->fcoeio_cmd, ret, fcoeio->fcoeio_status);
8242a8164dfSZhong Wang 
8252a8164dfSZhong Wang fcoeiocmd_release_buf:
8262a8164dfSZhong Wang 	if (ret == 0) {
8272a8164dfSZhong Wang 		ret = fcoe_copyout_iocdata(data, mode, fcoeio, obuf);
8282a8164dfSZhong Wang 	} else if (fcoeio->fcoeio_status) {
8292a8164dfSZhong Wang 		(void) fcoe_copyout_iocdata(data, mode, fcoeio, obuf);
8302a8164dfSZhong Wang 	}
8312a8164dfSZhong Wang 
8322a8164dfSZhong Wang 	if (obuf != NULL) {
8332a8164dfSZhong Wang 		kmem_free(obuf, fcoeio->fcoeio_olen);
8342a8164dfSZhong Wang 		obuf = NULL;
8352a8164dfSZhong Wang 	}
8362a8164dfSZhong Wang 	if (abuf != NULL) {
8372a8164dfSZhong Wang 		kmem_free(abuf, fcoeio->fcoeio_alen);
8382a8164dfSZhong Wang 		abuf = NULL;
8392a8164dfSZhong Wang 	}
8402a8164dfSZhong Wang 
8412a8164dfSZhong Wang 	if (ibuf != NULL) {
8422a8164dfSZhong Wang 		kmem_free(ibuf, fcoeio->fcoeio_ilen);
8432a8164dfSZhong Wang 		ibuf = NULL;
8442a8164dfSZhong Wang 	}
8452a8164dfSZhong Wang 	kmem_free(fcoeio, sizeof (fcoeio_t));
8462a8164dfSZhong Wang 
8472a8164dfSZhong Wang 	return (ret);
8482a8164dfSZhong Wang }
8492a8164dfSZhong Wang 
8502a8164dfSZhong Wang /*
8512a8164dfSZhong Wang  * Finish final initialization
8522a8164dfSZhong Wang  */
8532a8164dfSZhong Wang static int
fcoe_attach_init(fcoe_soft_state_t * ss)8542a8164dfSZhong Wang fcoe_attach_init(fcoe_soft_state_t *ss)
8552a8164dfSZhong Wang {
8562a8164dfSZhong Wang 	char taskq_name[TASKQ_NAME_LEN];
8572a8164dfSZhong Wang 
8582a8164dfSZhong Wang 	if (ddi_create_minor_node(ss->ss_dip, "admin", S_IFCHR,
8592a8164dfSZhong Wang 	    ddi_get_instance(ss->ss_dip), DDI_PSEUDO, 0) != DDI_SUCCESS) {
8602a8164dfSZhong Wang 		FCOE_LOG("FCOE", "ddi_create_minor_node failed");
8612a8164dfSZhong Wang 		return (FCOE_FAILURE);
8622a8164dfSZhong Wang 	}
8632a8164dfSZhong Wang 
8642a8164dfSZhong Wang 	/*
8652a8164dfSZhong Wang 	 * watchdog responsible for release frame and dispatch events
8662a8164dfSZhong Wang 	 */
8672a8164dfSZhong Wang 	(void) snprintf(taskq_name, sizeof (taskq_name), "fcoe_mac");
8682a8164dfSZhong Wang 	taskq_name[TASKQ_NAME_LEN - 1] = 0;
8692a8164dfSZhong Wang 	if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL,
8702a8164dfSZhong Wang 	    taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) {
8712a8164dfSZhong Wang 		return (FCOE_FAILURE);
8722a8164dfSZhong Wang 	}
8732a8164dfSZhong Wang 
8742a8164dfSZhong Wang 	ss->ss_ioctl_flags = 0;
8752a8164dfSZhong Wang 	mutex_init(&ss->ss_ioctl_mutex, NULL, MUTEX_DRIVER, NULL);
8762a8164dfSZhong Wang 	list_create(&ss->ss_mac_list, sizeof (fcoe_mac_t),
8772a8164dfSZhong Wang 	    offsetof(fcoe_mac_t, fm_ss_node));
8782a8164dfSZhong Wang 	list_create(&ss->ss_pfrm_list, sizeof (fcoe_i_frame_t),
8792a8164dfSZhong Wang 	    offsetof(fcoe_i_frame_t, fmi_pending_node));
8802a8164dfSZhong Wang 
8812a8164dfSZhong Wang 	mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0);
8822a8164dfSZhong Wang 	cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL);
8832a8164dfSZhong Wang 	ss->ss_flags &= ~SS_FLAG_TERMINATE_WATCHDOG;
8842a8164dfSZhong Wang 	(void) ddi_taskq_dispatch(ss->ss_watchdog_taskq,
8852a8164dfSZhong Wang 	    fcoe_watchdog, ss, DDI_SLEEP);
8862a8164dfSZhong Wang 	while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) {
8872a8164dfSZhong Wang 		delay(10);
8882a8164dfSZhong Wang 	}
8892a8164dfSZhong Wang 	fcoe_nworkers = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip,
8902a8164dfSZhong Wang 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (char *)fcoe_workers_num, 4);
8912a8164dfSZhong Wang 	if (fcoe_nworkers < 1) {
8922a8164dfSZhong Wang 		fcoe_nworkers = 4;
8932a8164dfSZhong Wang 	}
8942a8164dfSZhong Wang 	fcoe_worker_init();
8952a8164dfSZhong Wang 
8962a8164dfSZhong Wang 	ddi_report_dev(ss->ss_dip);
8972a8164dfSZhong Wang 	return (FCOE_SUCCESS);
8982a8164dfSZhong Wang }
8992a8164dfSZhong Wang 
9002a8164dfSZhong Wang /*
9012a8164dfSZhong Wang  * Finish final uninitialization
9022a8164dfSZhong Wang  */
9032a8164dfSZhong Wang static int
fcoe_detach_uninit(fcoe_soft_state_t * ss)9042a8164dfSZhong Wang fcoe_detach_uninit(fcoe_soft_state_t *ss)
9052a8164dfSZhong Wang {
9062a8164dfSZhong Wang 	int ret;
9072a8164dfSZhong Wang 	if (!list_is_empty(&ss->ss_mac_list)) {
9082a8164dfSZhong Wang 		FCOE_LOG("fcoe", "ss_mac_list is not empty when detach");
9092a8164dfSZhong Wang 		return (FCOE_FAILURE);
9102a8164dfSZhong Wang 	}
9112a8164dfSZhong Wang 
9122a8164dfSZhong Wang 	if ((ret = fcoe_worker_fini()) != FCOE_SUCCESS) {
9132a8164dfSZhong Wang 		return (ret);
9142a8164dfSZhong Wang 	}
9152a8164dfSZhong Wang 
9162a8164dfSZhong Wang 	/*
9172a8164dfSZhong Wang 	 * Stop watchdog
9182a8164dfSZhong Wang 	 */
9192a8164dfSZhong Wang 	if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
9202a8164dfSZhong Wang 		mutex_enter(&ss->ss_watch_mutex);
9212a8164dfSZhong Wang 		ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG;
9222a8164dfSZhong Wang 		cv_broadcast(&ss->ss_watch_cv);
9232a8164dfSZhong Wang 		mutex_exit(&ss->ss_watch_mutex);
9242a8164dfSZhong Wang 		while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
9252a8164dfSZhong Wang 			delay(10);
9262a8164dfSZhong Wang 		}
9272a8164dfSZhong Wang 	}
9282a8164dfSZhong Wang 
9292a8164dfSZhong Wang 	ddi_taskq_destroy(ss->ss_watchdog_taskq);
9302a8164dfSZhong Wang 	mutex_destroy(&ss->ss_watch_mutex);
9312a8164dfSZhong Wang 	cv_destroy(&ss->ss_watch_cv);
9322a8164dfSZhong Wang 
9332a8164dfSZhong Wang 	ddi_remove_minor_node(ss->ss_dip, NULL);
9342a8164dfSZhong Wang 	mutex_destroy(&ss->ss_ioctl_mutex);
9352a8164dfSZhong Wang 	list_destroy(&ss->ss_mac_list);
9362a8164dfSZhong Wang 
9372a8164dfSZhong Wang 	return (FCOE_SUCCESS);
9382a8164dfSZhong Wang }
9392a8164dfSZhong Wang 
9402a8164dfSZhong Wang /*
9412a8164dfSZhong Wang  * Return mac instance if it exist, or else return NULL.
9422a8164dfSZhong Wang  */
9432a8164dfSZhong Wang fcoe_mac_t *
fcoe_lookup_mac_by_id(datalink_id_t linkid)944d4401b99SKelly Hu fcoe_lookup_mac_by_id(datalink_id_t linkid)
9452a8164dfSZhong Wang {
9462a8164dfSZhong Wang 	fcoe_mac_t	*mac = NULL;
9472a8164dfSZhong Wang 
948d4401b99SKelly Hu 	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
9492a8164dfSZhong Wang 	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
9502a8164dfSZhong Wang 	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
951d4401b99SKelly Hu 		if (linkid != mac->fm_linkid) {
9522a8164dfSZhong Wang 			continue;
9532a8164dfSZhong Wang 		}
9542a8164dfSZhong Wang 		return (mac);
9552a8164dfSZhong Wang 	}
9562a8164dfSZhong Wang 	return (NULL);
9572a8164dfSZhong Wang }
9582a8164dfSZhong Wang 
9592a8164dfSZhong Wang /*
96087dcbdbdSKevin Yu  * Return B_TRUE if mac exists, or else return B_FALSE
96187dcbdbdSKevin Yu  */
96287dcbdbdSKevin Yu static boolean_t
fcoe_mac_existed(fcoe_mac_t * pmac)96387dcbdbdSKevin Yu fcoe_mac_existed(fcoe_mac_t *pmac)
96487dcbdbdSKevin Yu {
96587dcbdbdSKevin Yu 	fcoe_mac_t	*mac = NULL;
96687dcbdbdSKevin Yu 
96787dcbdbdSKevin Yu 	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
96887dcbdbdSKevin Yu 	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
96987dcbdbdSKevin Yu 	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
97087dcbdbdSKevin Yu 		if (mac == pmac) {
97187dcbdbdSKevin Yu 			return (B_TRUE);
97287dcbdbdSKevin Yu 		}
97387dcbdbdSKevin Yu 	}
97487dcbdbdSKevin Yu 	return (B_FALSE);
97587dcbdbdSKevin Yu }
97687dcbdbdSKevin Yu 
97787dcbdbdSKevin Yu /*
9782a8164dfSZhong Wang  * port wwn will start with 20:..., node wwn will start with 10:...
9792a8164dfSZhong Wang  */
9802a8164dfSZhong Wang static void
fcoe_init_wwn_from_mac(uint8_t * wwn,uint8_t * mac,int is_pwwn,uint8_t idx)9812a8164dfSZhong Wang fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, int is_pwwn, uint8_t idx)
9822a8164dfSZhong Wang {
9832a8164dfSZhong Wang 	ASSERT(wwn != NULL);
9842a8164dfSZhong Wang 	ASSERT(mac != NULL);
9852a8164dfSZhong Wang 	wwn[0] = (is_pwwn + 1) << 4;
9862a8164dfSZhong Wang 	wwn[1] = idx;
9872a8164dfSZhong Wang 	bcopy(mac, wwn + 2, ETHERADDRL);
9882a8164dfSZhong Wang }
9892a8164dfSZhong Wang 
9902a8164dfSZhong Wang /*
9912a8164dfSZhong Wang  * Return fcoe_mac if it exists, otherwise create a new one
9922a8164dfSZhong Wang  */
9932a8164dfSZhong Wang static fcoe_mac_t *
fcoe_create_mac_by_id(datalink_id_t linkid)994d4401b99SKelly Hu fcoe_create_mac_by_id(datalink_id_t linkid)
9952a8164dfSZhong Wang {
9962a8164dfSZhong Wang 	fcoe_mac_t	*mac = NULL;
997d4401b99SKelly Hu 	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
9982a8164dfSZhong Wang 
999d4401b99SKelly Hu 	mac = fcoe_lookup_mac_by_id(linkid);
10002a8164dfSZhong Wang 	if (mac != NULL) {
1001d4401b99SKelly Hu 		FCOE_LOG("fcoe", "fcoe_create_mac_by_id found one mac %d",
1002d4401b99SKelly Hu 		    linkid);
10032a8164dfSZhong Wang 		return (mac);
10042a8164dfSZhong Wang 	}
10052a8164dfSZhong Wang 
10062a8164dfSZhong Wang 	mac = kmem_zalloc(sizeof (fcoe_mac_t), KM_SLEEP);
1007d4401b99SKelly Hu 	mac->fm_linkid = linkid;
10082a8164dfSZhong Wang 	mac->fm_flags = 0;
10092a8164dfSZhong Wang 	mac->fm_ss = fcoe_global_ss;
10102a8164dfSZhong Wang 	list_insert_tail(&mac->fm_ss->ss_mac_list, mac);
1011d4401b99SKelly Hu 	FCOE_LOG("fcoe", "fcoe_create_mac_by_id created one mac %d", linkid);
10122a8164dfSZhong Wang 	return (mac);
10132a8164dfSZhong Wang }
10142a8164dfSZhong Wang 
10152a8164dfSZhong Wang void
fcoe_destroy_mac(fcoe_mac_t * mac)10162a8164dfSZhong Wang fcoe_destroy_mac(fcoe_mac_t *mac)
10172a8164dfSZhong Wang {
10182a8164dfSZhong Wang 	ASSERT(mac != NULL);
10192a8164dfSZhong Wang 	list_remove(&mac->fm_ss->ss_mac_list, mac);
10202a8164dfSZhong Wang 	kmem_free(mac, sizeof (fcoe_mac_t));
10212a8164dfSZhong Wang }
10222a8164dfSZhong Wang 
10232a8164dfSZhong Wang /*
10242a8164dfSZhong Wang  * raw frame layout:
10252a8164dfSZhong Wang  * ethernet header + vlan header (optional) + FCoE header +
10262a8164dfSZhong Wang  * FC frame + FCoE tailer
10272a8164dfSZhong Wang  */
10282a8164dfSZhong Wang /* ARGSUSED */
10292a8164dfSZhong Wang mblk_t *
fcoe_get_mblk(fcoe_mac_t * mac,uint32_t raw_frame_size)10302a8164dfSZhong Wang fcoe_get_mblk(fcoe_mac_t *mac, uint32_t raw_frame_size)
10312a8164dfSZhong Wang {
10322a8164dfSZhong Wang 	mblk_t	*mp;
10332a8164dfSZhong Wang 	int	 err;
10342a8164dfSZhong Wang 
10352a8164dfSZhong Wang 	/*
10362a8164dfSZhong Wang 	 * FCFH_SIZE + PADDING_SIZE
10372a8164dfSZhong Wang 	 */
10382a8164dfSZhong Wang 	ASSERT(raw_frame_size >= 60);
10392a8164dfSZhong Wang 	while ((mp = allocb((size_t)raw_frame_size, 0)) == NULL) {
10402a8164dfSZhong Wang 		if ((err = strwaitbuf((size_t)raw_frame_size, BPRI_LO)) != 0) {
10412a8164dfSZhong Wang 			FCOE_LOG("fcoe_get_mblk", "strwaitbuf return %d", err);
10422a8164dfSZhong Wang 			return (NULL);
10432a8164dfSZhong Wang 		}
10442a8164dfSZhong Wang 	}
10452a8164dfSZhong Wang 	mp->b_wptr = mp->b_rptr + raw_frame_size;
10462a8164dfSZhong Wang 
10472a8164dfSZhong Wang 	/*
10482a8164dfSZhong Wang 	 * We should always zero FC frame header
10492a8164dfSZhong Wang 	 */
10502a8164dfSZhong Wang 	bzero(mp->b_rptr + PADDING_HEADER_SIZE,
10512a8164dfSZhong Wang 	    sizeof (fcoe_fc_frame_header_t));
10522a8164dfSZhong Wang 	return (mp);
10532a8164dfSZhong Wang }
10542a8164dfSZhong Wang 
10552a8164dfSZhong Wang static void
fcoe_watchdog(void * arg)10562a8164dfSZhong Wang fcoe_watchdog(void *arg)
10572a8164dfSZhong Wang {
10582a8164dfSZhong Wang 	fcoe_soft_state_t	*ss	   = (fcoe_soft_state_t *)arg;
10592a8164dfSZhong Wang 	fcoe_i_frame_t		*fmi;
10602a8164dfSZhong Wang 	fcoe_mac_t		*mac = NULL;
10612a8164dfSZhong Wang 
10622a8164dfSZhong Wang 	FCOE_LOG("fcoe", "fcoe_soft_state is %p", ss);
10632a8164dfSZhong Wang 
10642a8164dfSZhong Wang 	mutex_enter(&ss->ss_watch_mutex);
10652a8164dfSZhong Wang 	ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING;
10662a8164dfSZhong Wang 	while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) {
10672a8164dfSZhong Wang 		while (fmi = (fcoe_i_frame_t *)list_head(&ss->ss_pfrm_list)) {
10682a8164dfSZhong Wang 			list_remove(&ss->ss_pfrm_list, fmi);
10692a8164dfSZhong Wang 			mutex_exit(&ss->ss_watch_mutex);
10702a8164dfSZhong Wang 
10712a8164dfSZhong Wang 			mac = EPORT2MAC(fmi->fmi_frame->frm_eport);
10722a8164dfSZhong Wang 			mac->fm_client.ect_release_sol_frame(fmi->fmi_frame);
10732a8164dfSZhong Wang 
10742a8164dfSZhong Wang 			mutex_enter(&ss->ss_watch_mutex);
10752a8164dfSZhong Wang 			mac->fm_frm_cnt--;
10762a8164dfSZhong Wang 		}
10772a8164dfSZhong Wang 
10782a8164dfSZhong Wang 		ss->ss_flags |= SS_FLAG_DOG_WAITING;
10792a8164dfSZhong Wang 		(void) cv_wait(&ss->ss_watch_cv, &ss->ss_watch_mutex);
10802a8164dfSZhong Wang 		ss->ss_flags &= ~SS_FLAG_DOG_WAITING;
10812a8164dfSZhong Wang 	}
10822a8164dfSZhong Wang 
10832a8164dfSZhong Wang 	ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING;
10842a8164dfSZhong Wang 	mutex_exit(&ss->ss_watch_mutex);
10852a8164dfSZhong Wang }
10862a8164dfSZhong Wang 
10872a8164dfSZhong Wang static void
fcoe_worker_init()10882a8164dfSZhong Wang fcoe_worker_init()
10892a8164dfSZhong Wang {
10902a8164dfSZhong Wang 	uint32_t i;
10912a8164dfSZhong Wang 
10922a8164dfSZhong Wang 	fcoe_nworkers_running = 0;
10932a8164dfSZhong Wang 	fcoe_worker_taskq = ddi_taskq_create(0, "FCOE_WORKER_TASKQ",
10942a8164dfSZhong Wang 	    fcoe_nworkers, TASKQ_DEFAULTPRI, 0);
10952a8164dfSZhong Wang 	fcoe_workers = (fcoe_worker_t *)kmem_zalloc(sizeof (fcoe_worker_t) *
10962a8164dfSZhong Wang 	    fcoe_nworkers, KM_SLEEP);
10972a8164dfSZhong Wang 	for (i = 0; i < fcoe_nworkers; i++) {
10982a8164dfSZhong Wang 		fcoe_worker_t *w = &fcoe_workers[i];
10992a8164dfSZhong Wang 		mutex_init(&w->worker_lock, NULL, MUTEX_DRIVER, NULL);
11002a8164dfSZhong Wang 		cv_init(&w->worker_cv, NULL, CV_DRIVER, NULL);
11012a8164dfSZhong Wang 		w->worker_flags &= ~FCOE_WORKER_TERMINATE;
11022a8164dfSZhong Wang 		list_create(&w->worker_frm_list, sizeof (fcoe_i_frame_t),
11032a8164dfSZhong Wang 		    offsetof(fcoe_i_frame_t, fmi_pending_node));
11042a8164dfSZhong Wang 		(void) ddi_taskq_dispatch(fcoe_worker_taskq, fcoe_worker_frame,
11052a8164dfSZhong Wang 		    w, DDI_SLEEP);
11062a8164dfSZhong Wang 	}
11072a8164dfSZhong Wang 	while (fcoe_nworkers_running != fcoe_nworkers) {
11082a8164dfSZhong Wang 		delay(10);
11092a8164dfSZhong Wang 	}
11102a8164dfSZhong Wang }
11112a8164dfSZhong Wang 
11122a8164dfSZhong Wang static int
fcoe_worker_fini()11132a8164dfSZhong Wang fcoe_worker_fini()
11142a8164dfSZhong Wang {
11152a8164dfSZhong Wang 	uint32_t i;
11162a8164dfSZhong Wang 
11172a8164dfSZhong Wang 	for (i = 0; i < fcoe_nworkers; i++) {
11182a8164dfSZhong Wang 		fcoe_worker_t *w = &fcoe_workers[i];
11192a8164dfSZhong Wang 		mutex_enter(&w->worker_lock);
11202a8164dfSZhong Wang 		if (w->worker_flags & FCOE_WORKER_STARTED) {
11212a8164dfSZhong Wang 			w->worker_flags |= FCOE_WORKER_TERMINATE;
11222a8164dfSZhong Wang 			cv_signal(&w->worker_cv);
11232a8164dfSZhong Wang 		}
11242a8164dfSZhong Wang 		mutex_exit(&w->worker_lock);
11252a8164dfSZhong Wang 	}
11262a8164dfSZhong Wang 
11272a8164dfSZhong Wang 	while (fcoe_nworkers_running != 0) {
11282a8164dfSZhong Wang 		delay(drv_usectohz(10000));
11292a8164dfSZhong Wang 	}
11302a8164dfSZhong Wang 
11312a8164dfSZhong Wang 	ddi_taskq_destroy(fcoe_worker_taskq);
11322a8164dfSZhong Wang 	kmem_free(fcoe_workers, sizeof (fcoe_worker_t) * fcoe_nworkers);
11332a8164dfSZhong Wang 	fcoe_workers = NULL;
11342a8164dfSZhong Wang 	return (FCOE_SUCCESS);
11352a8164dfSZhong Wang }
11362a8164dfSZhong Wang 
11372a8164dfSZhong Wang static int
fcoe_crc_verify(fcoe_frame_t * frm)11382a8164dfSZhong Wang fcoe_crc_verify(fcoe_frame_t *frm)
11392a8164dfSZhong Wang {
11402a8164dfSZhong Wang 	uint32_t crc;
11412a8164dfSZhong Wang 	uint8_t *crc_array = FRM2FMI(frm)->fmi_fft->fft_crc;
11422a8164dfSZhong Wang 	uint32_t crc_from_frame = ~(crc_array[0] | (crc_array[1] << 8) |
11432a8164dfSZhong Wang 	    (crc_array[2] << 16) | (crc_array[3] << 24));
11442a8164dfSZhong Wang 	CRC32(crc, frm->frm_fc_frame, frm->frm_fc_frame_size, -1U, crc32_table);
11452a8164dfSZhong Wang 	return (crc == crc_from_frame ? FCOE_SUCCESS : FCOE_FAILURE);
11462a8164dfSZhong Wang }
11472a8164dfSZhong Wang 
11482a8164dfSZhong Wang static void
fcoe_worker_frame(void * arg)11492a8164dfSZhong Wang fcoe_worker_frame(void *arg)
11502a8164dfSZhong Wang {
11512a8164dfSZhong Wang 	fcoe_worker_t	*w = (fcoe_worker_t *)arg;
11522a8164dfSZhong Wang 	fcoe_i_frame_t	*fmi;
11532a8164dfSZhong Wang 	int		ret;
11542a8164dfSZhong Wang 
1155*1a5e258fSJosef 'Jeff' Sipek 	atomic_inc_32(&fcoe_nworkers_running);
11562a8164dfSZhong Wang 	mutex_enter(&w->worker_lock);
11572a8164dfSZhong Wang 	w->worker_flags |= FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE;
11582a8164dfSZhong Wang 	while ((w->worker_flags & FCOE_WORKER_TERMINATE) == 0) {
11592a8164dfSZhong Wang 		/*
11602a8164dfSZhong Wang 		 * loop through the frames
11612a8164dfSZhong Wang 		 */
11622a8164dfSZhong Wang 		while (fmi = list_head(&w->worker_frm_list)) {
11632a8164dfSZhong Wang 			list_remove(&w->worker_frm_list, fmi);
11642a8164dfSZhong Wang 			mutex_exit(&w->worker_lock);
11652a8164dfSZhong Wang 			/*
11662a8164dfSZhong Wang 			 * do the checksum
11672a8164dfSZhong Wang 			 */
11682a8164dfSZhong Wang 			ret = fcoe_crc_verify(fmi->fmi_frame);
11692a8164dfSZhong Wang 			if (ret == FCOE_SUCCESS) {
11702a8164dfSZhong Wang 				fmi->fmi_mac->fm_client.ect_rx_frame(
11712a8164dfSZhong Wang 				    fmi->fmi_frame);
11722a8164dfSZhong Wang 			} else {
11732a8164dfSZhong Wang 				fcoe_release_frame(fmi->fmi_frame);
11742a8164dfSZhong Wang 			}
11752a8164dfSZhong Wang 			mutex_enter(&w->worker_lock);
11762a8164dfSZhong Wang 			w->worker_ntasks--;
11772a8164dfSZhong Wang 		}
11782a8164dfSZhong Wang 		w->worker_flags &= ~FCOE_WORKER_ACTIVE;
11792a8164dfSZhong Wang 		cv_wait(&w->worker_cv, &w->worker_lock);
11802a8164dfSZhong Wang 		w->worker_flags |= FCOE_WORKER_ACTIVE;
11812a8164dfSZhong Wang 	}
11822a8164dfSZhong Wang 	w->worker_flags &= ~(FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE);
11832a8164dfSZhong Wang 	mutex_exit(&w->worker_lock);
1184*1a5e258fSJosef 'Jeff' Sipek 	atomic_dec_32(&fcoe_nworkers_running);
11852a8164dfSZhong Wang 	list_destroy(&w->worker_frm_list);
11862a8164dfSZhong Wang }
11872a8164dfSZhong Wang 
11882a8164dfSZhong Wang void
fcoe_post_frame(fcoe_frame_t * frm)11892a8164dfSZhong Wang fcoe_post_frame(fcoe_frame_t *frm)
11902a8164dfSZhong Wang {
11912a8164dfSZhong Wang 	fcoe_worker_t *w;
11922a8164dfSZhong Wang 	uint16_t	oxid = FRM_OXID(frm);
11932a8164dfSZhong Wang 
11942a8164dfSZhong Wang 	w = &fcoe_workers[oxid % fcoe_nworkers_running];
11952a8164dfSZhong Wang 	mutex_enter(&w->worker_lock);
11962a8164dfSZhong Wang 	list_insert_tail(&w->worker_frm_list, frm->frm_fcoe_private);
11972a8164dfSZhong Wang 	w->worker_ntasks++;
11982a8164dfSZhong Wang 	if ((w->worker_flags & FCOE_WORKER_ACTIVE) == 0) {
11992a8164dfSZhong Wang 		cv_signal(&w->worker_cv);
12002a8164dfSZhong Wang 	}
12012a8164dfSZhong Wang 	mutex_exit(&w->worker_lock);
12022a8164dfSZhong Wang }
12032a8164dfSZhong Wang 
12042a8164dfSZhong Wang /*
12052a8164dfSZhong Wang  * The max length of every LOG is 158
12062a8164dfSZhong Wang  */
12072a8164dfSZhong Wang void
fcoe_trace(caddr_t ident,const char * fmt,...)12082a8164dfSZhong Wang fcoe_trace(caddr_t ident, const char *fmt, ...)
12092a8164dfSZhong Wang {
12102a8164dfSZhong Wang 	va_list args;
12112a8164dfSZhong Wang 	char	tbuf[160];
12122a8164dfSZhong Wang 	int	len;
12132a8164dfSZhong Wang 	clock_t curclock;
12142a8164dfSZhong Wang 	clock_t usec;
12152a8164dfSZhong Wang 
12162a8164dfSZhong Wang 	if (fcoe_trace_on == 0) {
12172a8164dfSZhong Wang 		return;
12182a8164dfSZhong Wang 	}
12192a8164dfSZhong Wang 
12202a8164dfSZhong Wang 	curclock = ddi_get_lbolt();
12212a8164dfSZhong Wang 	usec = (curclock - fcoe_trace_start) * usec_per_tick;
12222a8164dfSZhong Wang 	len = snprintf(tbuf, 158, "%lu.%03lus 0t%lu %s ", (usec /
12232a8164dfSZhong Wang 	    (1000 * 1000)), ((usec % (1000 * 1000)) / 1000),
12242a8164dfSZhong Wang 	    curclock, (ident ? ident : "unknown"));
12252a8164dfSZhong Wang 	va_start(args, fmt);
12262a8164dfSZhong Wang 	len += vsnprintf(tbuf + len, 158 - len, fmt, args);
12272a8164dfSZhong Wang 	va_end(args);
12282a8164dfSZhong Wang 
12292a8164dfSZhong Wang 	if (len > 158) {
12302a8164dfSZhong Wang 		len = 158;
12312a8164dfSZhong Wang 	}
12322a8164dfSZhong Wang 	tbuf[len++] = '\n';
12332a8164dfSZhong Wang 	tbuf[len] = 0;
12342a8164dfSZhong Wang 
12352a8164dfSZhong Wang 	mutex_enter(&fcoe_trace_buf_lock);
12362a8164dfSZhong Wang 	bcopy(tbuf, &fcoe_trace_buf[fcoe_trace_buf_curndx], len+1);
12372a8164dfSZhong Wang 	fcoe_trace_buf_curndx += len;
12382a8164dfSZhong Wang 	if (fcoe_trace_buf_curndx > (fcoe_trace_buf_size - 320)) {
12392a8164dfSZhong Wang 		fcoe_trace_buf_curndx = 0;
12402a8164dfSZhong Wang 	}
12412a8164dfSZhong Wang 	mutex_exit(&fcoe_trace_buf_lock);
12422a8164dfSZhong Wang }
12432a8164dfSZhong Wang 
12442a8164dfSZhong Wang /*
12452a8164dfSZhong Wang  * Check whether the pwwn or nwwn already exist or not
12462a8164dfSZhong Wang  * Return value:
12472a8164dfSZhong Wang  * 1: PWWN conflicted
12482a8164dfSZhong Wang  * -1: NWWN conflicted
12492a8164dfSZhong Wang  * 0: No conflict
12502a8164dfSZhong Wang  */
12512a8164dfSZhong Wang static int
fcoe_cmp_wwn(fcoe_mac_t * checkedmac)12522a8164dfSZhong Wang fcoe_cmp_wwn(fcoe_mac_t *checkedmac)
12532a8164dfSZhong Wang {
12542a8164dfSZhong Wang 	fcoe_mac_t	*mac;
12552a8164dfSZhong Wang 	uint8_t		*nwwn, *pwwn, *cnwwn, *cpwwn;
12562a8164dfSZhong Wang 
12572a8164dfSZhong Wang 	cnwwn = checkedmac->fm_eport.eport_nodewwn;
12582a8164dfSZhong Wang 	cpwwn = checkedmac->fm_eport.eport_portwwn;
1259d4401b99SKelly Hu 	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
12602a8164dfSZhong Wang 
12612a8164dfSZhong Wang 	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
12622a8164dfSZhong Wang 	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
12632a8164dfSZhong Wang 		if (mac == checkedmac) {
12642a8164dfSZhong Wang 			continue;
12652a8164dfSZhong Wang 		}
12662a8164dfSZhong Wang 		nwwn = mac->fm_eport.eport_nodewwn;
12672a8164dfSZhong Wang 		pwwn = mac->fm_eport.eport_portwwn;
12682a8164dfSZhong Wang 
12692a8164dfSZhong Wang 		if (memcmp(nwwn, cnwwn, 8) == 0) {
12702a8164dfSZhong Wang 			return (-1);
12712a8164dfSZhong Wang 		}
12722a8164dfSZhong Wang 
12732a8164dfSZhong Wang 		if (memcmp(pwwn, cpwwn, 8) == 0) {
12742a8164dfSZhong Wang 			return (1);
12752a8164dfSZhong Wang 		}
12762a8164dfSZhong Wang 	}
12772a8164dfSZhong Wang 	return (0);
12782a8164dfSZhong Wang }
12792a8164dfSZhong Wang 
12802a8164dfSZhong Wang static int
fcoe_get_port_list(fcoe_port_instance_t * ports,int count)12812a8164dfSZhong Wang fcoe_get_port_list(fcoe_port_instance_t *ports, int count)
12822a8164dfSZhong Wang {
12832a8164dfSZhong Wang 	fcoe_mac_t	*mac = NULL;
12842a8164dfSZhong Wang 	int		i = 0;
12852a8164dfSZhong Wang 
12862a8164dfSZhong Wang 	ASSERT(ports != NULL);
1287d4401b99SKelly Hu 	ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex));
12882a8164dfSZhong Wang 
12892a8164dfSZhong Wang 	for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
12902a8164dfSZhong Wang 	    mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
12912a8164dfSZhong Wang 		if (i < count) {
12922a8164dfSZhong Wang 			bcopy(mac->fm_eport.eport_portwwn,
12932a8164dfSZhong Wang 			    ports[i].fpi_pwwn, 8);
1294d4401b99SKelly Hu 			ports[i].fpi_mac_linkid = mac->fm_linkid;
12952a8164dfSZhong Wang 			bcopy(mac->fm_current_addr,
12962a8164dfSZhong Wang 			    ports[i].fpi_mac_current_addr, ETHERADDRL);
12972a8164dfSZhong Wang 			bcopy(mac->fm_primary_addr,
12982a8164dfSZhong Wang 			    ports[i].fpi_mac_factory_addr, ETHERADDRL);
12992a8164dfSZhong Wang 			ports[i].fpi_port_type =
13002a8164dfSZhong Wang 			    EPORT_CLT_TYPE(&mac->fm_eport);
13012a8164dfSZhong Wang 			ports[i].fpi_mtu_size =
13022a8164dfSZhong Wang 			    mac->fm_eport.eport_mtu;
13032a8164dfSZhong Wang 			ports[i].fpi_mac_promisc =
13042a8164dfSZhong Wang 			    mac->fm_promisc_handle != NULL ? 1 : 0;
13052a8164dfSZhong Wang 		}
13062a8164dfSZhong Wang 		i++;
13072a8164dfSZhong Wang 	}
13082a8164dfSZhong Wang 	return (i);
13092a8164dfSZhong Wang }
1310