xref: /onnv-gate/usr/src/uts/common/io/fibre-channel/fca/fcoei/fcoei.c (revision 12868:76234fce8dc5)
110264SZhong.Wang@Sun.COM /*
210264SZhong.Wang@Sun.COM  * CDDL HEADER START
310264SZhong.Wang@Sun.COM  *
410264SZhong.Wang@Sun.COM  * The contents of this file are subject to the terms of the
510264SZhong.Wang@Sun.COM  * Common Development and Distribution License (the "License").
610264SZhong.Wang@Sun.COM  * You may not use this file except in compliance with the License.
710264SZhong.Wang@Sun.COM  *
810264SZhong.Wang@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910264SZhong.Wang@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010264SZhong.Wang@Sun.COM  * See the License for the specific language governing permissions
1110264SZhong.Wang@Sun.COM  * and limitations under the License.
1210264SZhong.Wang@Sun.COM  *
1310264SZhong.Wang@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410264SZhong.Wang@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510264SZhong.Wang@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610264SZhong.Wang@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710264SZhong.Wang@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810264SZhong.Wang@Sun.COM  *
1910264SZhong.Wang@Sun.COM  * CDDL HEADER END
2010264SZhong.Wang@Sun.COM  */
2110264SZhong.Wang@Sun.COM 
2210264SZhong.Wang@Sun.COM /*
23*12868SRenia.Miao@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2410264SZhong.Wang@Sun.COM  */
2510264SZhong.Wang@Sun.COM 
2610264SZhong.Wang@Sun.COM #include <sys/conf.h>
2710264SZhong.Wang@Sun.COM #include <sys/ddi.h>
2810264SZhong.Wang@Sun.COM #include <sys/stat.h>
2910264SZhong.Wang@Sun.COM #include <sys/pci.h>
3010264SZhong.Wang@Sun.COM #include <sys/sunddi.h>
3110264SZhong.Wang@Sun.COM #include <sys/modctl.h>
3210264SZhong.Wang@Sun.COM #include <sys/file.h>
3310264SZhong.Wang@Sun.COM #include <sys/cred.h>
3410264SZhong.Wang@Sun.COM #include <sys/byteorder.h>
3510264SZhong.Wang@Sun.COM #include <sys/atomic.h>
3610264SZhong.Wang@Sun.COM #include <sys/scsi/scsi.h>
3710264SZhong.Wang@Sun.COM #include <sys/mac_client.h>
3810264SZhong.Wang@Sun.COM #include <sys/modhash.h>
3910264SZhong.Wang@Sun.COM 
4010264SZhong.Wang@Sun.COM /*
4110264SZhong.Wang@Sun.COM  * leadville header files
4210264SZhong.Wang@Sun.COM  */
4310264SZhong.Wang@Sun.COM #include <sys/fibre-channel/fc.h>
4410264SZhong.Wang@Sun.COM #include <sys/fibre-channel/impl/fc_fcaif.h>
4510264SZhong.Wang@Sun.COM 
4610264SZhong.Wang@Sun.COM /*
4710264SZhong.Wang@Sun.COM  * fcoe header files
4810264SZhong.Wang@Sun.COM  */
4910264SZhong.Wang@Sun.COM #include <sys/fcoe/fcoe_common.h>
5010264SZhong.Wang@Sun.COM 
5110264SZhong.Wang@Sun.COM /*
5210264SZhong.Wang@Sun.COM  * fcoei header files
5310264SZhong.Wang@Sun.COM  */
5410264SZhong.Wang@Sun.COM #include <fcoei.h>
5510264SZhong.Wang@Sun.COM 
5610264SZhong.Wang@Sun.COM /*
5710264SZhong.Wang@Sun.COM  * forward declaration of stack functions
5810264SZhong.Wang@Sun.COM  */
5910264SZhong.Wang@Sun.COM static uint32_t fcoei_xch_check(
6010264SZhong.Wang@Sun.COM 	mod_hash_key_t key, mod_hash_val_t *val, void *arg);
6110264SZhong.Wang@Sun.COM static int fcoei_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
6210264SZhong.Wang@Sun.COM static int fcoei_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
6310264SZhong.Wang@Sun.COM static int fcoei_open(dev_t *devp, int flag, int otype, cred_t *credp);
6410264SZhong.Wang@Sun.COM static int fcoei_close(dev_t dev, int flag, int otype, cred_t *credp);
6510264SZhong.Wang@Sun.COM static int fcoei_ioctl(
6610264SZhong.Wang@Sun.COM 	dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval);
6710264SZhong.Wang@Sun.COM static int fcoei_attach_init(fcoei_soft_state_t *ss);
6810264SZhong.Wang@Sun.COM static int fcoei_detach_uninit(fcoei_soft_state_t *ss);
6910264SZhong.Wang@Sun.COM static void fcoei_watchdog(void *arg);
7010264SZhong.Wang@Sun.COM static void fcoei_process_events(fcoei_soft_state_t *ss);
7110264SZhong.Wang@Sun.COM static void fcoei_trigger_fp_attach(void *arg);
7210264SZhong.Wang@Sun.COM static void fcoei_abts_exchange(fcoei_exchange_t *xch);
7310264SZhong.Wang@Sun.COM static void fcoei_clear_watchdog_jobs(fcoei_soft_state_t *ss);
7410264SZhong.Wang@Sun.COM 
7510264SZhong.Wang@Sun.COM /*
7610264SZhong.Wang@Sun.COM  * Driver identificaton stuff
7710264SZhong.Wang@Sun.COM  */
7810264SZhong.Wang@Sun.COM static struct cb_ops fcoei_cb_ops = {
7910264SZhong.Wang@Sun.COM 	fcoei_open,
8010264SZhong.Wang@Sun.COM 	fcoei_close,
8110264SZhong.Wang@Sun.COM 	nodev,
8210264SZhong.Wang@Sun.COM 	nodev,
8310264SZhong.Wang@Sun.COM 	nodev,
8410264SZhong.Wang@Sun.COM 	nodev,
8510264SZhong.Wang@Sun.COM 	nodev,
8610264SZhong.Wang@Sun.COM 	fcoei_ioctl,
8710264SZhong.Wang@Sun.COM 	nodev,
8810264SZhong.Wang@Sun.COM 	nodev,
8910264SZhong.Wang@Sun.COM 	nodev,
9010264SZhong.Wang@Sun.COM 	nochpoll,
9110264SZhong.Wang@Sun.COM 	ddi_prop_op,
9210264SZhong.Wang@Sun.COM 	0,
9310264SZhong.Wang@Sun.COM 	D_MP | D_NEW | D_HOTPLUG,
9410264SZhong.Wang@Sun.COM 	CB_REV,
9510264SZhong.Wang@Sun.COM 	nodev,
9610264SZhong.Wang@Sun.COM 	nodev
9710264SZhong.Wang@Sun.COM };
9810264SZhong.Wang@Sun.COM 
9910264SZhong.Wang@Sun.COM static struct dev_ops fcoei_ops = {
10010264SZhong.Wang@Sun.COM 	DEVO_REV,
10110264SZhong.Wang@Sun.COM 	0,
10210264SZhong.Wang@Sun.COM 	nodev,
10310264SZhong.Wang@Sun.COM 	nulldev,
10410264SZhong.Wang@Sun.COM 	nulldev,
10510264SZhong.Wang@Sun.COM 	fcoei_attach,
10610264SZhong.Wang@Sun.COM 	fcoei_detach,
10710264SZhong.Wang@Sun.COM 	nodev,
10810264SZhong.Wang@Sun.COM 	&fcoei_cb_ops,
10910264SZhong.Wang@Sun.COM 	NULL,
11010264SZhong.Wang@Sun.COM 	ddi_power,
11110264SZhong.Wang@Sun.COM 	ddi_quiesce_not_needed
11210264SZhong.Wang@Sun.COM };
11310264SZhong.Wang@Sun.COM 
11410264SZhong.Wang@Sun.COM static struct modldrv modldrv = {
11510264SZhong.Wang@Sun.COM 	&mod_driverops,
11610264SZhong.Wang@Sun.COM 	FCOEI_NAME_VERSION,
11710264SZhong.Wang@Sun.COM 	&fcoei_ops,
11810264SZhong.Wang@Sun.COM };
11910264SZhong.Wang@Sun.COM 
12010264SZhong.Wang@Sun.COM static struct modlinkage modlinkage = {
12110264SZhong.Wang@Sun.COM 	MODREV_1,
12210264SZhong.Wang@Sun.COM 	&modldrv,
12310264SZhong.Wang@Sun.COM 	NULL
12410264SZhong.Wang@Sun.COM };
12510264SZhong.Wang@Sun.COM 
12610264SZhong.Wang@Sun.COM /*
12710264SZhong.Wang@Sun.COM  * Driver's global variables
12810264SZhong.Wang@Sun.COM  */
12910264SZhong.Wang@Sun.COM void	*fcoei_state	   = NULL;
13010264SZhong.Wang@Sun.COM int	 fcoei_use_ext_log = 0;
13110264SZhong.Wang@Sun.COM 
13210264SZhong.Wang@Sun.COM /*
13310264SZhong.Wang@Sun.COM  * Common loadable module entry points _init, _fini, _info
13410264SZhong.Wang@Sun.COM  */
13510264SZhong.Wang@Sun.COM int
_init(void)13610264SZhong.Wang@Sun.COM _init(void)
13710264SZhong.Wang@Sun.COM {
13810264SZhong.Wang@Sun.COM 	int ret;
13910264SZhong.Wang@Sun.COM 
14010264SZhong.Wang@Sun.COM 	ret = ddi_soft_state_init(&fcoei_state, sizeof (fcoei_soft_state_t), 0);
14110264SZhong.Wang@Sun.COM 	if (ret != DDI_SUCCESS) {
14210264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "soft state init failed: %x", ret);
14310264SZhong.Wang@Sun.COM 		return (ret);
14410264SZhong.Wang@Sun.COM 	}
14510264SZhong.Wang@Sun.COM 
14610264SZhong.Wang@Sun.COM 	ret = mod_install(&modlinkage);
14710264SZhong.Wang@Sun.COM 	if (ret != 0) {
14810264SZhong.Wang@Sun.COM 		ddi_soft_state_fini(&fcoei_state);
14910264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "fcoei mod_install failed: %x", ret);
15010264SZhong.Wang@Sun.COM 		return (ret);
15110264SZhong.Wang@Sun.COM 	}
15210264SZhong.Wang@Sun.COM 
15310264SZhong.Wang@Sun.COM 	/*
15410264SZhong.Wang@Sun.COM 	 * Let FCTL initialize devo_bus_ops
15510264SZhong.Wang@Sun.COM 	 */
15610264SZhong.Wang@Sun.COM 	fc_fca_init(&fcoei_ops);
15710264SZhong.Wang@Sun.COM 
15810264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "fcoei _init succeeded");
15910264SZhong.Wang@Sun.COM 	return (ret);
16010264SZhong.Wang@Sun.COM }
16110264SZhong.Wang@Sun.COM 
16210264SZhong.Wang@Sun.COM int
_fini(void)16310264SZhong.Wang@Sun.COM _fini(void)
16410264SZhong.Wang@Sun.COM {
16510264SZhong.Wang@Sun.COM 	int ret;
16610264SZhong.Wang@Sun.COM 
16710264SZhong.Wang@Sun.COM 	ret = mod_remove(&modlinkage);
16810264SZhong.Wang@Sun.COM 	if (ret != 0) {
16910264SZhong.Wang@Sun.COM 		FCOEI_EXT_LOG(__FUNCTION__, "fcoei mod_remove failed: %x", ret);
17010264SZhong.Wang@Sun.COM 		return (ret);
17110264SZhong.Wang@Sun.COM 	}
17210264SZhong.Wang@Sun.COM 
17310264SZhong.Wang@Sun.COM 	ddi_soft_state_fini(&fcoei_state);
17410264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "fcoei _fini succeeded");
17510264SZhong.Wang@Sun.COM 	return (ret);
17610264SZhong.Wang@Sun.COM }
17710264SZhong.Wang@Sun.COM 
17810264SZhong.Wang@Sun.COM int
_info(struct modinfo * modinfop)17910264SZhong.Wang@Sun.COM _info(struct modinfo *modinfop)
18010264SZhong.Wang@Sun.COM {
18110264SZhong.Wang@Sun.COM 	return (mod_info(&modlinkage, modinfop));
18210264SZhong.Wang@Sun.COM }
18310264SZhong.Wang@Sun.COM 
18410264SZhong.Wang@Sun.COM /*
18510264SZhong.Wang@Sun.COM  * Autoconfiguration entry points: attach, detach, getinfo
18610264SZhong.Wang@Sun.COM  */
18710264SZhong.Wang@Sun.COM 
18810264SZhong.Wang@Sun.COM static int
fcoei_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)18910264SZhong.Wang@Sun.COM fcoei_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
19010264SZhong.Wang@Sun.COM {
19110264SZhong.Wang@Sun.COM 	int			 ret;
19210264SZhong.Wang@Sun.COM 	int			 fcoe_ret;
19310264SZhong.Wang@Sun.COM 	int			 instance;
19410264SZhong.Wang@Sun.COM 	fcoei_soft_state_t	*ss;
19510264SZhong.Wang@Sun.COM 
19610264SZhong.Wang@Sun.COM 	instance = ddi_get_instance(dip);
19710264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "instance is %d", instance);
19810264SZhong.Wang@Sun.COM 	switch (cmd) {
19910264SZhong.Wang@Sun.COM 	case DDI_ATTACH:
20010264SZhong.Wang@Sun.COM 		ret = ddi_soft_state_zalloc(fcoei_state, instance);
20110264SZhong.Wang@Sun.COM 		if (ret != DDI_SUCCESS) {
20210264SZhong.Wang@Sun.COM 			FCOEI_LOG(__FUNCTION__, "ss zalloc failed: %x", ret);
20310264SZhong.Wang@Sun.COM 			return (ret);
20410264SZhong.Wang@Sun.COM 		}
20510264SZhong.Wang@Sun.COM 
20610264SZhong.Wang@Sun.COM 		/*
20710264SZhong.Wang@Sun.COM 		 * Get the soft state, and do basic initialization with dip
20810264SZhong.Wang@Sun.COM 		 */
20910264SZhong.Wang@Sun.COM 		ss = ddi_get_soft_state(fcoei_state, instance);
21010264SZhong.Wang@Sun.COM 		ss->ss_dip = dip;
21110264SZhong.Wang@Sun.COM 
21210264SZhong.Wang@Sun.COM 		fcoe_ret = fcoei_attach_init(ss);
21310264SZhong.Wang@Sun.COM 		if (fcoe_ret != FCOE_SUCCESS) {
21410264SZhong.Wang@Sun.COM 			ddi_soft_state_free(fcoei_state, instance);
21510264SZhong.Wang@Sun.COM 			FCOEI_LOG(__FUNCTION__, "fcoei_attach_init failed: "
21610264SZhong.Wang@Sun.COM 			    "%x", fcoe_ret);
21710264SZhong.Wang@Sun.COM 			return (DDI_FAILURE);
21810264SZhong.Wang@Sun.COM 		}
21910264SZhong.Wang@Sun.COM 
22010264SZhong.Wang@Sun.COM 		ss->ss_flags |= SS_FLAG_TRIGGER_FP_ATTACH;
22110264SZhong.Wang@Sun.COM 		(void) timeout(fcoei_trigger_fp_attach, ss, FCOE_SEC2TICK(1));
22210264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "fcoei_attach succeeded: dip-%p, "
22310264SZhong.Wang@Sun.COM 		    "cmd-%x", dip, cmd);
22410264SZhong.Wang@Sun.COM 		return (DDI_SUCCESS);
22510264SZhong.Wang@Sun.COM 
22610264SZhong.Wang@Sun.COM 	case DDI_RESUME:
22710264SZhong.Wang@Sun.COM 		return (DDI_SUCCESS);
22810264SZhong.Wang@Sun.COM 
22910264SZhong.Wang@Sun.COM 	default:
23010264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "unsupported attach cmd-%X", cmd);
23110264SZhong.Wang@Sun.COM 		return (DDI_FAILURE);
23210264SZhong.Wang@Sun.COM 	}
23310264SZhong.Wang@Sun.COM }
23410264SZhong.Wang@Sun.COM 
23510264SZhong.Wang@Sun.COM static int
fcoei_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)23610264SZhong.Wang@Sun.COM fcoei_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
23710264SZhong.Wang@Sun.COM {
23810264SZhong.Wang@Sun.COM 	int			 fcoe_ret;
23910264SZhong.Wang@Sun.COM 	int			 instance;
24010264SZhong.Wang@Sun.COM 	fcoei_soft_state_t	*ss;
24110264SZhong.Wang@Sun.COM 
24210264SZhong.Wang@Sun.COM 	instance = ddi_get_instance(dip);
24310264SZhong.Wang@Sun.COM 	ss = ddi_get_soft_state(fcoei_state, instance);
24410264SZhong.Wang@Sun.COM 	if (ss == NULL) {
24510264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "get ss failed: dip-%p", dip);
24610264SZhong.Wang@Sun.COM 		return (DDI_FAILURE);
24710264SZhong.Wang@Sun.COM 	}
24810264SZhong.Wang@Sun.COM 
24910264SZhong.Wang@Sun.COM 	switch (cmd) {
25010264SZhong.Wang@Sun.COM 	case DDI_DETACH:
25110264SZhong.Wang@Sun.COM 		if (ss->ss_flags & SS_FLAG_TRIGGER_FP_ATTACH) {
25210264SZhong.Wang@Sun.COM 			FCOEI_LOG(__FUNCTION__, "still await fp attach");
25310264SZhong.Wang@Sun.COM 			return (DDI_FAILURE);
25410264SZhong.Wang@Sun.COM 		}
25510264SZhong.Wang@Sun.COM 
25610264SZhong.Wang@Sun.COM 		if (ss->ss_flags & SS_FLAG_LV_BOUND) {
25710264SZhong.Wang@Sun.COM 			FCOEI_LOG(__FUNCTION__, "fp is not detached yet");
25810264SZhong.Wang@Sun.COM 			return (DDI_FAILURE);
25910264SZhong.Wang@Sun.COM 		}
26010264SZhong.Wang@Sun.COM 
26110264SZhong.Wang@Sun.COM 		fcoe_ret = fcoei_detach_uninit(ss);
26210264SZhong.Wang@Sun.COM 		if (fcoe_ret != FCOE_SUCCESS) {
26310264SZhong.Wang@Sun.COM 			FCOEI_LOG(__FUNCTION__, "fcoei_detach_uninit failed:"
26410264SZhong.Wang@Sun.COM 			    " dip-%p, fcoe_ret-%d", dip, fcoe_ret);
26510264SZhong.Wang@Sun.COM 			return (DDI_FAILURE);
26610264SZhong.Wang@Sun.COM 		}
26710264SZhong.Wang@Sun.COM 
26810264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "succeeded: dip-%p, cmd-%x", dip, cmd);
26910264SZhong.Wang@Sun.COM 		return (DDI_SUCCESS);
27010264SZhong.Wang@Sun.COM 
27110264SZhong.Wang@Sun.COM 	case DDI_SUSPEND:
27210264SZhong.Wang@Sun.COM 		return (DDI_SUCCESS);
27310264SZhong.Wang@Sun.COM 
27410264SZhong.Wang@Sun.COM 	default:
27510264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "unspported detach cmd-%X", cmd);
27610264SZhong.Wang@Sun.COM 		return (DDI_FAILURE);
27710264SZhong.Wang@Sun.COM 	}
27810264SZhong.Wang@Sun.COM }
27910264SZhong.Wang@Sun.COM 
28010264SZhong.Wang@Sun.COM /*
28110264SZhong.Wang@Sun.COM  * Device access entry points: open, close, ioctl
28210264SZhong.Wang@Sun.COM  */
28310264SZhong.Wang@Sun.COM 
28410264SZhong.Wang@Sun.COM static int
fcoei_open(dev_t * devp,int flag,int otype,cred_t * credp)28510264SZhong.Wang@Sun.COM fcoei_open(dev_t *devp, int flag, int otype, cred_t *credp)
28610264SZhong.Wang@Sun.COM {
28710264SZhong.Wang@Sun.COM 	fcoei_soft_state_t	*ss;
28810264SZhong.Wang@Sun.COM 
28910264SZhong.Wang@Sun.COM 	if (otype != OTYP_CHR) {
29010264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "flag: %x", flag);
29110264SZhong.Wang@Sun.COM 		return (EINVAL);
29210264SZhong.Wang@Sun.COM 	}
29310264SZhong.Wang@Sun.COM 
29410264SZhong.Wang@Sun.COM 	if (drv_priv(credp)) {
29510264SZhong.Wang@Sun.COM 		return (EPERM);
29610264SZhong.Wang@Sun.COM 	}
29710264SZhong.Wang@Sun.COM 
29810264SZhong.Wang@Sun.COM 	/*
29910264SZhong.Wang@Sun.COM 	 * First of all, get related soft state
30010264SZhong.Wang@Sun.COM 	 */
30110264SZhong.Wang@Sun.COM 	ss = ddi_get_soft_state(fcoei_state, (int)getminor(*devp));
30210264SZhong.Wang@Sun.COM 	if (ss == NULL) {
30310264SZhong.Wang@Sun.COM 		return (ENXIO);
30410264SZhong.Wang@Sun.COM 	}
30510264SZhong.Wang@Sun.COM 
30610264SZhong.Wang@Sun.COM 	mutex_enter(&ss->ss_ioctl_mutex);
30710264SZhong.Wang@Sun.COM 	if (ss->ss_ioctl_flags & FCOEI_IOCTL_FLAG_OPEN) {
30810264SZhong.Wang@Sun.COM 		/*
30910264SZhong.Wang@Sun.COM 		 * We don't support concurrent open
31010264SZhong.Wang@Sun.COM 		 */
31110264SZhong.Wang@Sun.COM 		mutex_exit(&ss->ss_ioctl_mutex);
31210264SZhong.Wang@Sun.COM 		return (EBUSY);
31310264SZhong.Wang@Sun.COM 	}
31410264SZhong.Wang@Sun.COM 
31510264SZhong.Wang@Sun.COM 	ss->ss_ioctl_flags |= FCOEI_IOCTL_FLAG_OPEN;
31610264SZhong.Wang@Sun.COM 	mutex_exit(&ss->ss_ioctl_mutex);
31710264SZhong.Wang@Sun.COM 
31810264SZhong.Wang@Sun.COM 	return (0);
31910264SZhong.Wang@Sun.COM }
32010264SZhong.Wang@Sun.COM 
32110264SZhong.Wang@Sun.COM static int
fcoei_close(dev_t dev,int flag,int otype,cred_t * credp)32210264SZhong.Wang@Sun.COM fcoei_close(dev_t dev, int flag, int otype, cred_t *credp)
32310264SZhong.Wang@Sun.COM {
32410264SZhong.Wang@Sun.COM 	fcoei_soft_state_t	*ss;
32510264SZhong.Wang@Sun.COM 
32610264SZhong.Wang@Sun.COM 	if (otype != OTYP_CHR) {
32710264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "flag: %x, %p", flag, credp);
32810264SZhong.Wang@Sun.COM 		return (EINVAL);
32910264SZhong.Wang@Sun.COM 	}
33010264SZhong.Wang@Sun.COM 
33110264SZhong.Wang@Sun.COM 	/*
33210264SZhong.Wang@Sun.COM 	 * First of all, get related soft state
33310264SZhong.Wang@Sun.COM 	 */
33410264SZhong.Wang@Sun.COM 	ss = ddi_get_soft_state(fcoei_state, (int)getminor(dev));
33510264SZhong.Wang@Sun.COM 	if (ss == NULL) {
33610264SZhong.Wang@Sun.COM 		return (ENXIO);
33710264SZhong.Wang@Sun.COM 	}
33810264SZhong.Wang@Sun.COM 
33910264SZhong.Wang@Sun.COM 	mutex_enter(&ss->ss_ioctl_mutex);
34010264SZhong.Wang@Sun.COM 	if (!(ss->ss_ioctl_flags & FCOEI_IOCTL_FLAG_OPEN)) {
34110264SZhong.Wang@Sun.COM 		/*
34210264SZhong.Wang@Sun.COM 		 * If it's not open, we can exit
34310264SZhong.Wang@Sun.COM 		 */
34410264SZhong.Wang@Sun.COM 
34510264SZhong.Wang@Sun.COM 		mutex_exit(&ss->ss_ioctl_mutex);
34610264SZhong.Wang@Sun.COM 		return (ENODEV);
34710264SZhong.Wang@Sun.COM 	}
34810264SZhong.Wang@Sun.COM 
34910264SZhong.Wang@Sun.COM 	ss->ss_ioctl_flags &= ~FCOEI_IOCTL_FLAG_OPEN;
35010264SZhong.Wang@Sun.COM 	mutex_exit(&ss->ss_ioctl_mutex);
35110264SZhong.Wang@Sun.COM 
35210264SZhong.Wang@Sun.COM 	return (0);
35310264SZhong.Wang@Sun.COM }
35410264SZhong.Wang@Sun.COM 
35510264SZhong.Wang@Sun.COM static int
fcoei_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rval)35610264SZhong.Wang@Sun.COM fcoei_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
35710264SZhong.Wang@Sun.COM     cred_t *credp, int *rval)
35810264SZhong.Wang@Sun.COM {
35910264SZhong.Wang@Sun.COM 	fcoei_soft_state_t	*ss;
36010264SZhong.Wang@Sun.COM 	int			 ret = 0;
36110264SZhong.Wang@Sun.COM 
36210264SZhong.Wang@Sun.COM 	if (drv_priv(credp) != 0) {
36310264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "data: %p, %x", data, mode);
36410264SZhong.Wang@Sun.COM 		return (EPERM);
36510264SZhong.Wang@Sun.COM 	}
36610264SZhong.Wang@Sun.COM 
36710264SZhong.Wang@Sun.COM 	/*
36810264SZhong.Wang@Sun.COM 	 * Get related soft state
36910264SZhong.Wang@Sun.COM 	 */
37010264SZhong.Wang@Sun.COM 	ss = ddi_get_soft_state(fcoei_state, (int32_t)getminor(dev));
37110264SZhong.Wang@Sun.COM 	if (!ss) {
37210264SZhong.Wang@Sun.COM 		return (ENXIO);
37310264SZhong.Wang@Sun.COM 	}
37410264SZhong.Wang@Sun.COM 
37510264SZhong.Wang@Sun.COM 	/*
37610264SZhong.Wang@Sun.COM 	 * Process ioctl
37710264SZhong.Wang@Sun.COM 	 */
37810264SZhong.Wang@Sun.COM 	switch (cmd) {
37910264SZhong.Wang@Sun.COM 
38010264SZhong.Wang@Sun.COM 	default:
38110264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "ioctl-0x%02X", cmd);
38210264SZhong.Wang@Sun.COM 		ret = ENOTTY;
38310264SZhong.Wang@Sun.COM 	}
38410264SZhong.Wang@Sun.COM 
38510264SZhong.Wang@Sun.COM 	/*
38610264SZhong.Wang@Sun.COM 	 * Set return value
38710264SZhong.Wang@Sun.COM 	 */
38810264SZhong.Wang@Sun.COM 	*rval = ret;
38910264SZhong.Wang@Sun.COM 	return (ret);
39010264SZhong.Wang@Sun.COM }
39110264SZhong.Wang@Sun.COM 
39210264SZhong.Wang@Sun.COM /*
39310264SZhong.Wang@Sun.COM  * fcoei_attach_init
39410264SZhong.Wang@Sun.COM  *	init related stuff of the soft state
39510264SZhong.Wang@Sun.COM  *
39610264SZhong.Wang@Sun.COM  * Input:
39710264SZhong.Wang@Sun.COM  *	ss = the soft state that will be processed
39810264SZhong.Wang@Sun.COM  *
39910264SZhong.Wang@Sun.COM  * Return:
40010264SZhong.Wang@Sun.COM  *	if it succeeded or not
40110264SZhong.Wang@Sun.COM  *
40210264SZhong.Wang@Sun.COM  * Comment:
40310264SZhong.Wang@Sun.COM  *	N/A
40410264SZhong.Wang@Sun.COM  */
40510264SZhong.Wang@Sun.COM static int
fcoei_attach_init(fcoei_soft_state_t * ss)40610264SZhong.Wang@Sun.COM fcoei_attach_init(fcoei_soft_state_t *ss)
40710264SZhong.Wang@Sun.COM {
40810264SZhong.Wang@Sun.COM 	fcoe_port_t		*eport;
40910264SZhong.Wang@Sun.COM 	fcoe_client_t		 client_fcoei;
41010264SZhong.Wang@Sun.COM 	char			 taskq_name[32];
41110264SZhong.Wang@Sun.COM 	int			 ret;
41210264SZhong.Wang@Sun.COM 	la_els_logi_t		*els = &ss->ss_els_logi;
41310264SZhong.Wang@Sun.COM 	svc_param_t		*class3_param;
41410264SZhong.Wang@Sun.COM 
41510264SZhong.Wang@Sun.COM 	/*
41610264SZhong.Wang@Sun.COM 	 * Register fcoei to FCOE as its client
41710264SZhong.Wang@Sun.COM 	 */
41810264SZhong.Wang@Sun.COM 	client_fcoei.ect_eport_flags = EPORT_FLAG_INI_MODE |
41910264SZhong.Wang@Sun.COM 	    EPORT_FLAG_IS_DIRECT_P2P;
42010264SZhong.Wang@Sun.COM 	client_fcoei.ect_max_fc_frame_size = FCOE_MAX_FC_FRAME_SIZE;
42110264SZhong.Wang@Sun.COM 	client_fcoei.ect_private_frame_struct_size = sizeof (fcoei_frame_t);
42210264SZhong.Wang@Sun.COM 	fcoei_init_ect_vectors(&client_fcoei);
42310264SZhong.Wang@Sun.COM 	client_fcoei.ect_client_port_struct = ss;
42410264SZhong.Wang@Sun.COM 	client_fcoei.ect_fcoe_ver = FCOE_VER_NOW;
42510264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "version: %x %x", FCOE_VER_NOW, fcoe_ver_now);
42610264SZhong.Wang@Sun.COM 	ret = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip,
42710264SZhong.Wang@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "mac_id", -1);
42810264SZhong.Wang@Sun.COM 	if (ret == -1) {
42910264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "get mac_id failed");
43010264SZhong.Wang@Sun.COM 		return (DDI_FAILURE);
43110264SZhong.Wang@Sun.COM 	} else {
43210264SZhong.Wang@Sun.COM 		client_fcoei.ect_channelid = ret;
43310264SZhong.Wang@Sun.COM 	}
43410264SZhong.Wang@Sun.COM 
43510264SZhong.Wang@Sun.COM 	/*
43610264SZhong.Wang@Sun.COM 	 * It's fcoe's responsiblity to initialize eport's all elements,
43710264SZhong.Wang@Sun.COM 	 * so we needn't do eport initialization
43810264SZhong.Wang@Sun.COM 	 */
43910264SZhong.Wang@Sun.COM 	eport = fcoe_register_client(&client_fcoei);
44010264SZhong.Wang@Sun.COM 	if (eport == NULL) {
44110264SZhong.Wang@Sun.COM 		goto fail_register_client;
44210264SZhong.Wang@Sun.COM 	} else {
44310264SZhong.Wang@Sun.COM 		ss->ss_eport = eport;
44410264SZhong.Wang@Sun.COM 		FCOE_SET_DEFAULT_FPORT_ADDR(eport->eport_efh_dst);
44510264SZhong.Wang@Sun.COM 	}
44610264SZhong.Wang@Sun.COM 
44710264SZhong.Wang@Sun.COM 	/*
44810264SZhong.Wang@Sun.COM 	 * Now it's time to register fca_tran to FCTL
44910264SZhong.Wang@Sun.COM 	 * Remember fc_local_port is transparent to FCA (fcoei)
45010264SZhong.Wang@Sun.COM 	 */
45110264SZhong.Wang@Sun.COM 	ss->ss_fca_tran.fca_version  = FCTL_FCA_MODREV_5;
45210264SZhong.Wang@Sun.COM 	ss->ss_fca_tran.fca_numports = 1;
45310264SZhong.Wang@Sun.COM 	ss->ss_fca_tran.fca_pkt_size = sizeof (fcoei_exchange_t);
45410264SZhong.Wang@Sun.COM 	ss->ss_fca_tran.fca_cmd_max  = 2048;
45510264SZhong.Wang@Sun.COM 
45610264SZhong.Wang@Sun.COM 	/*
45710264SZhong.Wang@Sun.COM 	 * scsi_tran_hba_setup could need these stuff
45810264SZhong.Wang@Sun.COM 	 */
45910264SZhong.Wang@Sun.COM 	ss->ss_fca_tran.fca_dma_lim  = NULL;
46010264SZhong.Wang@Sun.COM 	ss->ss_fca_tran.fca_iblock   = NULL;
46110264SZhong.Wang@Sun.COM 	ss->ss_fca_tran.fca_dma_attr = NULL;
46210264SZhong.Wang@Sun.COM 	ss->ss_fca_tran.fca_acc_attr = NULL;
46310264SZhong.Wang@Sun.COM 
46410264SZhong.Wang@Sun.COM 	/*
46510264SZhong.Wang@Sun.COM 	 * Initialize vectors
46610264SZhong.Wang@Sun.COM 	 */
46710264SZhong.Wang@Sun.COM 	fcoei_init_fcatran_vectors(&ss->ss_fca_tran);
46810264SZhong.Wang@Sun.COM 
46910264SZhong.Wang@Sun.COM 	/*
47010264SZhong.Wang@Sun.COM 	 * fc_fca_attach only sets driver's private, it has nothing to with
47110264SZhong.Wang@Sun.COM 	 * common port object between fcoei and leadville.
47210264SZhong.Wang@Sun.COM 	 * After this attach, fp_attach will be triggered, and it will call
47310264SZhong.Wang@Sun.COM 	 * fca_bind_port to let fcoei to know about common port object.
47410264SZhong.Wang@Sun.COM 	 */
47510264SZhong.Wang@Sun.COM 	if (fc_fca_attach(ss->ss_dip, &ss->ss_fca_tran) != DDI_SUCCESS) {
47610264SZhong.Wang@Sun.COM 		goto fail_fca_attach;
47710264SZhong.Wang@Sun.COM 	}
47810264SZhong.Wang@Sun.COM 
47910264SZhong.Wang@Sun.COM 	/*
48010264SZhong.Wang@Sun.COM 	 * It's time to do ss initialization
48110264SZhong.Wang@Sun.COM 	 */
48210264SZhong.Wang@Sun.COM 	ret = ddi_create_minor_node(ss->ss_dip, "admin",
48310264SZhong.Wang@Sun.COM 	    S_IFCHR, ddi_get_instance(ss->ss_dip), DDI_NT_NEXUS, 0);
48410264SZhong.Wang@Sun.COM 	if (ret != DDI_SUCCESS) {
48510264SZhong.Wang@Sun.COM 		goto fail_minor_node;
48610264SZhong.Wang@Sun.COM 	}
48710264SZhong.Wang@Sun.COM 
48810264SZhong.Wang@Sun.COM 	ss->ss_flags	   = 0;
48910264SZhong.Wang@Sun.COM 	ss->ss_port	   = NULL;
49010264SZhong.Wang@Sun.COM 	/*
49110264SZhong.Wang@Sun.COM 	 * ss->ss_eport has been initialized
49210264SZhong.Wang@Sun.COM 	 */
49310264SZhong.Wang@Sun.COM 
49410264SZhong.Wang@Sun.COM 	ss->ss_sol_oxid_hash = mod_hash_create_idhash(
49510264SZhong.Wang@Sun.COM 	    "fcoei_sol_oxid_hash", FCOEI_SOL_HASH_SIZE,
49610264SZhong.Wang@Sun.COM 	    mod_hash_null_valdtor);
49710264SZhong.Wang@Sun.COM 	ss->ss_unsol_rxid_hash = mod_hash_create_idhash(
49810264SZhong.Wang@Sun.COM 	    "fcoei_unsol_rxid_hash", FCOEI_UNSOL_HASH_SIZE,
49910264SZhong.Wang@Sun.COM 	    mod_hash_null_valdtor);
50010264SZhong.Wang@Sun.COM 	list_create(&ss->ss_comp_xch_list, sizeof (fcoei_exchange_t),
50110264SZhong.Wang@Sun.COM 	    offsetof(fcoei_exchange_t, xch_comp_node));
50210264SZhong.Wang@Sun.COM 	ss->ss_next_sol_oxid   = 0xFFFF;
50310264SZhong.Wang@Sun.COM 	ss->ss_next_unsol_rxid = 0xFFFF;
50410264SZhong.Wang@Sun.COM 
50510264SZhong.Wang@Sun.COM 	mutex_init(&ss->ss_watchdog_mutex, 0, MUTEX_DRIVER, 0);
50610264SZhong.Wang@Sun.COM 	cv_init(&ss->ss_watchdog_cv, NULL, CV_DRIVER, NULL);
50710264SZhong.Wang@Sun.COM 	(void) snprintf(taskq_name, 32, "leadville_fcoei_%d_taskq",
50810264SZhong.Wang@Sun.COM 	    ddi_get_instance(ss->ss_dip));
50910264SZhong.Wang@Sun.COM 	taskq_name[31] = 0;
51010264SZhong.Wang@Sun.COM 	ss->ss_taskq = ddi_taskq_create(ss->ss_dip,
51110264SZhong.Wang@Sun.COM 	    taskq_name, 64, TASKQ_DEFAULTPRI, DDI_SLEEP);
51210264SZhong.Wang@Sun.COM 
51310264SZhong.Wang@Sun.COM 	ss->ss_link_state	  = FC_STATE_OFFLINE;
51410264SZhong.Wang@Sun.COM 	ss->ss_link_speed	  = 0;
51510264SZhong.Wang@Sun.COM 	ss->ss_port_event_counter = 0;
51610264SZhong.Wang@Sun.COM 
51710264SZhong.Wang@Sun.COM 	list_create(&ss->ss_event_list, sizeof (fcoei_event_t),
51810264SZhong.Wang@Sun.COM 	    offsetof(fcoei_event_t, ae_node));
51910264SZhong.Wang@Sun.COM 
52010264SZhong.Wang@Sun.COM 	ss->ss_sol_cnt1   = 0;
52110264SZhong.Wang@Sun.COM 	ss->ss_sol_cnt2   = 0;
52210264SZhong.Wang@Sun.COM 	ss->ss_sol_cnt	   = &ss->ss_sol_cnt1;
52310264SZhong.Wang@Sun.COM 	ss->ss_unsol_cnt1 = 0;
52410264SZhong.Wang@Sun.COM 	ss->ss_unsol_cnt2 = 0;
52510264SZhong.Wang@Sun.COM 	ss->ss_unsol_cnt  = &ss->ss_unsol_cnt1;
52610264SZhong.Wang@Sun.COM 	ss->ss_ioctl_flags = 0;
52710264SZhong.Wang@Sun.COM 
52810264SZhong.Wang@Sun.COM 	mutex_init(&ss->ss_ioctl_mutex, 0, MUTEX_DRIVER, 0);
52910264SZhong.Wang@Sun.COM 
53010264SZhong.Wang@Sun.COM 	bcopy(eport->eport_portwwn, els->nport_ww_name.raw_wwn, 8);
53110264SZhong.Wang@Sun.COM 	bcopy(eport->eport_nodewwn, els->node_ww_name.raw_wwn, 8);
53210264SZhong.Wang@Sun.COM 	els->common_service.fcph_version = 0x2008;
53310264SZhong.Wang@Sun.COM 	els->common_service.btob_credit = 3;
53410264SZhong.Wang@Sun.COM 	els->common_service.cmn_features = 0x8800;
53510264SZhong.Wang@Sun.COM 	els->common_service.conc_sequences = 0xff;
53610264SZhong.Wang@Sun.COM 	els->common_service.relative_offset = 3;
53710264SZhong.Wang@Sun.COM 	els->common_service.e_d_tov = 0x07d0;
53810264SZhong.Wang@Sun.COM 	class3_param = (svc_param_t *)&els->class_3;
53910264SZhong.Wang@Sun.COM 	class3_param->class_opt = 0x8800;
54010264SZhong.Wang@Sun.COM 	class3_param->rcv_size = els->common_service.rx_bufsize = 2048;
54110264SZhong.Wang@Sun.COM 	class3_param->conc_sequences = 0xff;
54210264SZhong.Wang@Sun.COM 	class3_param->open_seq_per_xchng = 1;
54310264SZhong.Wang@Sun.COM 
54410264SZhong.Wang@Sun.COM 	/*
54510264SZhong.Wang@Sun.COM 	 * Fill out RNID Management Information
54610264SZhong.Wang@Sun.COM 	 */
54710264SZhong.Wang@Sun.COM 	bcopy(ss->ss_eport->eport_portwwn, ss->ss_rnid.global_id, 8);
54810264SZhong.Wang@Sun.COM 	ss->ss_rnid.unit_type  = FCOEI_RNID_HBA;
54910264SZhong.Wang@Sun.COM 	ss->ss_rnid.ip_version = FCOEI_RNID_IPV4;
55010264SZhong.Wang@Sun.COM 
55110264SZhong.Wang@Sun.COM 	/*
55210264SZhong.Wang@Sun.COM 	 * Start our watchdog
55310264SZhong.Wang@Sun.COM 	 */
55410264SZhong.Wang@Sun.COM 	(void) ddi_taskq_dispatch(ss->ss_taskq,
55510264SZhong.Wang@Sun.COM 	    fcoei_watchdog, ss, DDI_SLEEP);
55610264SZhong.Wang@Sun.COM 	while (!(ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING)) {
55710264SZhong.Wang@Sun.COM 		delay(50);
55810264SZhong.Wang@Sun.COM 	}
55910264SZhong.Wang@Sun.COM 
56010264SZhong.Wang@Sun.COM 	/*
56110264SZhong.Wang@Sun.COM 	 * Report the device to the system
56210264SZhong.Wang@Sun.COM 	 */
56310264SZhong.Wang@Sun.COM 	ddi_report_dev(ss->ss_dip);
56410264SZhong.Wang@Sun.COM 	return (DDI_SUCCESS);
56510264SZhong.Wang@Sun.COM 
56610264SZhong.Wang@Sun.COM 
56710264SZhong.Wang@Sun.COM fail_minor_node:
56810264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "fail_minor_node");
56910767SZhong.Wang@Sun.COM 	(void) fc_fca_detach(ss->ss_dip);
57010264SZhong.Wang@Sun.COM 
57110264SZhong.Wang@Sun.COM fail_fca_attach:
57210264SZhong.Wang@Sun.COM 	eport->eport_deregister_client(eport);
57310264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "fail_fca_attach");
57410264SZhong.Wang@Sun.COM 
57510264SZhong.Wang@Sun.COM fail_register_client:
57610264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "fail_register_client");
57710264SZhong.Wang@Sun.COM 	return (DDI_FAILURE);
57810264SZhong.Wang@Sun.COM }
57910264SZhong.Wang@Sun.COM 
58010264SZhong.Wang@Sun.COM /*
58110264SZhong.Wang@Sun.COM  * fcoei_detach_uninit
58210264SZhong.Wang@Sun.COM  *	uninit related stuff of the soft state
58310264SZhong.Wang@Sun.COM  *
58410264SZhong.Wang@Sun.COM  * Input:
58510264SZhong.Wang@Sun.COM  *	ss = the soft state that will be processed
58610264SZhong.Wang@Sun.COM  *
58710264SZhong.Wang@Sun.COM  * Return:
58810264SZhong.Wang@Sun.COM  *	if it succeeded or not
58910264SZhong.Wang@Sun.COM  *
59010264SZhong.Wang@Sun.COM  * Comment:
59110264SZhong.Wang@Sun.COM  *	N/A
59210264SZhong.Wang@Sun.COM  */
59310264SZhong.Wang@Sun.COM int
fcoei_detach_uninit(fcoei_soft_state_t * ss)59410264SZhong.Wang@Sun.COM fcoei_detach_uninit(fcoei_soft_state_t *ss)
59510264SZhong.Wang@Sun.COM {
59610264SZhong.Wang@Sun.COM 	/*
59710264SZhong.Wang@Sun.COM 	 * Stop watchdog first
59810264SZhong.Wang@Sun.COM 	 */
59910264SZhong.Wang@Sun.COM 	if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
60010264SZhong.Wang@Sun.COM 		ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG;
60110264SZhong.Wang@Sun.COM 		cv_broadcast(&ss->ss_watchdog_cv);
60210264SZhong.Wang@Sun.COM 	}
60310264SZhong.Wang@Sun.COM 
60410264SZhong.Wang@Sun.COM 	/*
60510264SZhong.Wang@Sun.COM 	 * Destroy the taskq
60610264SZhong.Wang@Sun.COM 	 */
60710264SZhong.Wang@Sun.COM 	ddi_taskq_wait(ss->ss_taskq);
60810264SZhong.Wang@Sun.COM 	ddi_taskq_destroy(ss->ss_taskq);
60910264SZhong.Wang@Sun.COM 
61010264SZhong.Wang@Sun.COM 	/*
61110264SZhong.Wang@Sun.COM 	 * Release all allocated resources
61210264SZhong.Wang@Sun.COM 	 */
61310264SZhong.Wang@Sun.COM 	mutex_destroy(&ss->ss_ioctl_mutex);
61410264SZhong.Wang@Sun.COM 	mutex_destroy(&ss->ss_watchdog_mutex);
61510264SZhong.Wang@Sun.COM 	cv_destroy(&ss->ss_watchdog_cv);
61610264SZhong.Wang@Sun.COM 	mod_hash_destroy_idhash(ss->ss_sol_oxid_hash);
61710264SZhong.Wang@Sun.COM 	mod_hash_destroy_idhash(ss->ss_unsol_rxid_hash);
61810264SZhong.Wang@Sun.COM 	list_destroy(&ss->ss_event_list);
61910264SZhong.Wang@Sun.COM 	ss->ss_eport->eport_deregister_client(ss->ss_eport);
62010264SZhong.Wang@Sun.COM 	ddi_remove_minor_node(ss->ss_dip, NULL);
62110264SZhong.Wang@Sun.COM 
62210264SZhong.Wang@Sun.COM 	/*
62310264SZhong.Wang@Sun.COM 	 * Release itself
62410264SZhong.Wang@Sun.COM 	 */
62510264SZhong.Wang@Sun.COM 	ddi_soft_state_free(fcoei_state, ddi_get_instance(ss->ss_dip));
62610264SZhong.Wang@Sun.COM 	return (FCOE_SUCCESS);
62710264SZhong.Wang@Sun.COM }
62810264SZhong.Wang@Sun.COM 
62910264SZhong.Wang@Sun.COM /*
63010264SZhong.Wang@Sun.COM  * fcoei_watchdog
63110264SZhong.Wang@Sun.COM  *	Perform periodic checking and routine tasks
63210264SZhong.Wang@Sun.COM  *
63310264SZhong.Wang@Sun.COM  * Input:
63410264SZhong.Wang@Sun.COM  *	arg = the soft state that will be processed
63510264SZhong.Wang@Sun.COM  *
63610264SZhong.Wang@Sun.COM  * Return:
63710264SZhong.Wang@Sun.COM  *	N/A
63810264SZhong.Wang@Sun.COM  *
63910264SZhong.Wang@Sun.COM  * Comment:
64010264SZhong.Wang@Sun.COM  *	N/A
64110264SZhong.Wang@Sun.COM  */
64210264SZhong.Wang@Sun.COM static void
fcoei_watchdog(void * arg)64310264SZhong.Wang@Sun.COM fcoei_watchdog(void *arg)
64410264SZhong.Wang@Sun.COM {
64510264SZhong.Wang@Sun.COM 	fcoei_soft_state_t	*ss;
64610264SZhong.Wang@Sun.COM 	clock_t			 tmp_delay;
64710264SZhong.Wang@Sun.COM 	clock_t			 start_clock;
64810264SZhong.Wang@Sun.COM 	clock_t			 last_clock;
64910264SZhong.Wang@Sun.COM 
65010264SZhong.Wang@Sun.COM 	/*
65110264SZhong.Wang@Sun.COM 	 * For debugging
65210264SZhong.Wang@Sun.COM 	 */
65310264SZhong.Wang@Sun.COM 	ss = (fcoei_soft_state_t *)arg;
65410264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "ss %p", ss);
65510264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "sol_hash %p", ss->ss_sol_oxid_hash);
65610264SZhong.Wang@Sun.COM 	FCOEI_LOG(__FUNCTION__, "unsol_hash %p", ss->ss_unsol_rxid_hash);
65710264SZhong.Wang@Sun.COM 	ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING;
65810264SZhong.Wang@Sun.COM 	tmp_delay = FCOE_SEC2TICK(1) / 2;
65910264SZhong.Wang@Sun.COM 	last_clock = CURRENT_CLOCK;
66010264SZhong.Wang@Sun.COM 
66110264SZhong.Wang@Sun.COM 	/*
66210264SZhong.Wang@Sun.COM 	 * If nobody reqeusts to terminate the watchdog, we will work forever
66310264SZhong.Wang@Sun.COM 	 */
66410264SZhong.Wang@Sun.COM 	while (!(ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG)) {
66510264SZhong.Wang@Sun.COM 		/*
66610264SZhong.Wang@Sun.COM 		 * We handle all asynchronous events serially
66710264SZhong.Wang@Sun.COM 		 */
66810264SZhong.Wang@Sun.COM 		fcoei_process_events(ss);
66910264SZhong.Wang@Sun.COM 
67010264SZhong.Wang@Sun.COM 		/*
67110264SZhong.Wang@Sun.COM 		 * To avoid to check timing too freqently, we check
67210264SZhong.Wang@Sun.COM 		 * if we need skip timing stuff.
67310264SZhong.Wang@Sun.COM 		 */
67410264SZhong.Wang@Sun.COM 		start_clock = CURRENT_CLOCK;
67510264SZhong.Wang@Sun.COM 		if ((start_clock - last_clock) < tmp_delay) {
67610264SZhong.Wang@Sun.COM 			goto end_timing;
67710264SZhong.Wang@Sun.COM 		} else {
67810264SZhong.Wang@Sun.COM 			last_clock = start_clock;
67910264SZhong.Wang@Sun.COM 		}
68010264SZhong.Wang@Sun.COM 
68110264SZhong.Wang@Sun.COM 		/*
68210264SZhong.Wang@Sun.COM 		 * It's time to do timeout checking of solicited exchanges
68310264SZhong.Wang@Sun.COM 		 */
68410264SZhong.Wang@Sun.COM 		if (ss->ss_sol_cnt == (&ss->ss_sol_cnt1)) {
68510264SZhong.Wang@Sun.COM 			if (ss->ss_sol_cnt2 == 0) {
68610264SZhong.Wang@Sun.COM 				ss->ss_sol_cnt = &ss->ss_sol_cnt2;
68710264SZhong.Wang@Sun.COM 			} else {
68810264SZhong.Wang@Sun.COM 				mod_hash_walk(ss->ss_sol_oxid_hash,
68910264SZhong.Wang@Sun.COM 				    fcoei_xch_check, ss);
69010264SZhong.Wang@Sun.COM 			}
69110264SZhong.Wang@Sun.COM 		} else {
69210264SZhong.Wang@Sun.COM 			if (ss->ss_sol_cnt1 == 0) {
69310264SZhong.Wang@Sun.COM 				ss->ss_sol_cnt = &ss->ss_sol_cnt1;
69410264SZhong.Wang@Sun.COM 			} else {
69510264SZhong.Wang@Sun.COM 				mod_hash_walk(ss->ss_sol_oxid_hash,
69610264SZhong.Wang@Sun.COM 				    fcoei_xch_check, ss);
69710264SZhong.Wang@Sun.COM 			}
69810264SZhong.Wang@Sun.COM 		}
69910264SZhong.Wang@Sun.COM 
70010264SZhong.Wang@Sun.COM 		/*
70110264SZhong.Wang@Sun.COM 		 * It's time to do timeout checking of unsolicited exchange
70210264SZhong.Wang@Sun.COM 		 */
70310264SZhong.Wang@Sun.COM 		if (ss->ss_unsol_cnt == (&ss->ss_unsol_cnt1)) {
70410264SZhong.Wang@Sun.COM 			if (ss->ss_unsol_cnt2 == 0) {
70510264SZhong.Wang@Sun.COM 				ss->ss_unsol_cnt = &ss->ss_unsol_cnt2;
70610264SZhong.Wang@Sun.COM 			} else {
70710264SZhong.Wang@Sun.COM 				mod_hash_walk(ss->ss_unsol_rxid_hash,
70810264SZhong.Wang@Sun.COM 				    fcoei_xch_check, ss);
70910264SZhong.Wang@Sun.COM 			}
71010264SZhong.Wang@Sun.COM 		} else {
71110264SZhong.Wang@Sun.COM 			if (ss->ss_unsol_cnt1 == 0) {
71210264SZhong.Wang@Sun.COM 				ss->ss_unsol_cnt = &ss->ss_unsol_cnt1;
71310264SZhong.Wang@Sun.COM 			} else {
71410264SZhong.Wang@Sun.COM 				mod_hash_walk(ss->ss_unsol_rxid_hash,
71510264SZhong.Wang@Sun.COM 				    fcoei_xch_check, ss);
71610264SZhong.Wang@Sun.COM 			}
71710264SZhong.Wang@Sun.COM 		}
71810264SZhong.Wang@Sun.COM 
71910264SZhong.Wang@Sun.COM 		/*
72010264SZhong.Wang@Sun.COM 		 * Check if there are exchanges which are ready to complete
72110264SZhong.Wang@Sun.COM 		 */
72210264SZhong.Wang@Sun.COM 		fcoei_handle_comp_xch_list(ss);
72310264SZhong.Wang@Sun.COM 
72410264SZhong.Wang@Sun.COM 	end_timing:
72510264SZhong.Wang@Sun.COM 		/*
72610264SZhong.Wang@Sun.COM 		 * Wait for next cycle
72710264SZhong.Wang@Sun.COM 		 */
72810264SZhong.Wang@Sun.COM 		mutex_enter(&ss->ss_watchdog_mutex);
72910264SZhong.Wang@Sun.COM 		ss->ss_flags |= SS_FLAG_WATCHDOG_IDLE;
73010264SZhong.Wang@Sun.COM 		if (!list_is_empty(&ss->ss_event_list)) {
73110264SZhong.Wang@Sun.COM 			goto skip_wait;
73210264SZhong.Wang@Sun.COM 		}
73310264SZhong.Wang@Sun.COM 
73410264SZhong.Wang@Sun.COM 		(void) cv_timedwait(&ss->ss_watchdog_cv,
73510264SZhong.Wang@Sun.COM 		    &ss->ss_watchdog_mutex, CURRENT_CLOCK +
73610264SZhong.Wang@Sun.COM 		    (clock_t)tmp_delay);
73710264SZhong.Wang@Sun.COM 	skip_wait:
73810264SZhong.Wang@Sun.COM 		ss->ss_flags &= ~SS_FLAG_WATCHDOG_IDLE;
73910264SZhong.Wang@Sun.COM 		mutex_exit(&ss->ss_watchdog_mutex);
74010264SZhong.Wang@Sun.COM 	}
74110264SZhong.Wang@Sun.COM 
74210264SZhong.Wang@Sun.COM 	/*
74310264SZhong.Wang@Sun.COM 	 * Do clear work before exit
74410264SZhong.Wang@Sun.COM 	 */
74510264SZhong.Wang@Sun.COM 	fcoei_clear_watchdog_jobs(ss);
74610264SZhong.Wang@Sun.COM 
74710264SZhong.Wang@Sun.COM 	/*
74810264SZhong.Wang@Sun.COM 	 * Watchdog has stopped
74910264SZhong.Wang@Sun.COM 	 */
75010264SZhong.Wang@Sun.COM 	ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING;
75110264SZhong.Wang@Sun.COM }
75210264SZhong.Wang@Sun.COM 
75310264SZhong.Wang@Sun.COM static void
fcoei_clear_watchdog_jobs(fcoei_soft_state_t * ss)75410264SZhong.Wang@Sun.COM fcoei_clear_watchdog_jobs(fcoei_soft_state_t *ss)
75510264SZhong.Wang@Sun.COM {
75610264SZhong.Wang@Sun.COM 	fcoei_event_t 		*ae;
75710264SZhong.Wang@Sun.COM 	fcoe_frame_t		*frm;
75810264SZhong.Wang@Sun.COM 
75910264SZhong.Wang@Sun.COM 	mutex_enter(&ss->ss_watchdog_mutex);
76010264SZhong.Wang@Sun.COM 	while (!list_is_empty(&ss->ss_event_list)) {
76110264SZhong.Wang@Sun.COM 		ae = (fcoei_event_t *)list_head(&ss->ss_event_list);
76210264SZhong.Wang@Sun.COM 		list_remove(&ss->ss_event_list, ae);
76310264SZhong.Wang@Sun.COM 		switch (ae->ae_type) {
76410264SZhong.Wang@Sun.COM 		case AE_EVENT_SOL_FRAME:
76510264SZhong.Wang@Sun.COM 			frm = (fcoe_frame_t *)ae->ae_obj;
76610264SZhong.Wang@Sun.COM 			frm->frm_eport->eport_release_frame(frm);
76710264SZhong.Wang@Sun.COM 			break;
76810264SZhong.Wang@Sun.COM 
76910264SZhong.Wang@Sun.COM 		case AE_EVENT_UNSOL_FRAME:
77010264SZhong.Wang@Sun.COM 			frm = (fcoe_frame_t *)ae->ae_obj;
77110264SZhong.Wang@Sun.COM 			frm->frm_eport->eport_free_netb(frm->frm_netb);
77210264SZhong.Wang@Sun.COM 			frm->frm_eport->eport_release_frame(frm);
77310264SZhong.Wang@Sun.COM 			break;
77410264SZhong.Wang@Sun.COM 
77510264SZhong.Wang@Sun.COM 		case AE_EVENT_PORT:
77610264SZhong.Wang@Sun.COM 			atomic_add_32(&ss->ss_port_event_counter, -1);
77710264SZhong.Wang@Sun.COM 			/* FALLTHROUGH */
77810264SZhong.Wang@Sun.COM 
77910264SZhong.Wang@Sun.COM 		case AE_EVENT_RESET:
78010264SZhong.Wang@Sun.COM 			kmem_free(ae, sizeof (fcoei_event_t));
78110264SZhong.Wang@Sun.COM 			break;
78210264SZhong.Wang@Sun.COM 
78310264SZhong.Wang@Sun.COM 		case AE_EVENT_EXCHANGE:
78410264SZhong.Wang@Sun.COM 			/* FALLTHROUGH */
78510264SZhong.Wang@Sun.COM 
78610264SZhong.Wang@Sun.COM 		default:
78710264SZhong.Wang@Sun.COM 			break;
78810264SZhong.Wang@Sun.COM 		}
78910264SZhong.Wang@Sun.COM 	}
79010264SZhong.Wang@Sun.COM 
79110264SZhong.Wang@Sun.COM 	mod_hash_clear(ss->ss_unsol_rxid_hash);
79210264SZhong.Wang@Sun.COM 	mod_hash_clear(ss->ss_sol_oxid_hash);
79310264SZhong.Wang@Sun.COM 
79410264SZhong.Wang@Sun.COM 	while (!list_is_empty(&ss->ss_comp_xch_list)) {
79510767SZhong.Wang@Sun.COM 		(void) list_remove_head(&ss->ss_comp_xch_list);
79610264SZhong.Wang@Sun.COM 	}
79710264SZhong.Wang@Sun.COM 	mutex_exit(&ss->ss_watchdog_mutex);
79810264SZhong.Wang@Sun.COM }
79910264SZhong.Wang@Sun.COM 
80010264SZhong.Wang@Sun.COM /*
80110264SZhong.Wang@Sun.COM  * fcoei_process_events
80210264SZhong.Wang@Sun.COM  *	Process the events one by one
80310264SZhong.Wang@Sun.COM  *
80410264SZhong.Wang@Sun.COM  * Input:
80510264SZhong.Wang@Sun.COM  *	ss = the soft state that will be processed
80610264SZhong.Wang@Sun.COM  *
80710264SZhong.Wang@Sun.COM  * Return:
80810264SZhong.Wang@Sun.COM  *	N/A
80910264SZhong.Wang@Sun.COM  *
81010264SZhong.Wang@Sun.COM  * Comment:
81110264SZhong.Wang@Sun.COM  *	N/A
81210264SZhong.Wang@Sun.COM  */
81310264SZhong.Wang@Sun.COM static void
fcoei_process_events(fcoei_soft_state_t * ss)81410264SZhong.Wang@Sun.COM fcoei_process_events(fcoei_soft_state_t *ss)
81510264SZhong.Wang@Sun.COM {
81610264SZhong.Wang@Sun.COM 	fcoei_event_t	*ae = NULL;
81710264SZhong.Wang@Sun.COM 
81810264SZhong.Wang@Sun.COM 	/*
81910264SZhong.Wang@Sun.COM 	 * It's the only place to delete node from ss_event_list, so we needn't
82010264SZhong.Wang@Sun.COM 	 * hold mutex to check if the list is empty.
82110264SZhong.Wang@Sun.COM 	 */
82210264SZhong.Wang@Sun.COM 	ASSERT(!MUTEX_HELD(&ss->ss_watchdog_mutex));
82310264SZhong.Wang@Sun.COM 	while (list_is_empty(&ss->ss_event_list) == B_FALSE) {
82410264SZhong.Wang@Sun.COM 		mutex_enter(&ss->ss_watchdog_mutex);
82510264SZhong.Wang@Sun.COM 		ae = (fcoei_event_t *)list_remove_head(&ss->ss_event_list);
82610264SZhong.Wang@Sun.COM 		mutex_exit(&ss->ss_watchdog_mutex);
82710264SZhong.Wang@Sun.COM 
82810264SZhong.Wang@Sun.COM 		switch (ae->ae_type) {
82910264SZhong.Wang@Sun.COM 		case AE_EVENT_SOL_FRAME:
83010264SZhong.Wang@Sun.COM 			fcoei_handle_sol_frame_done((fcoe_frame_t *)ae->ae_obj);
83110264SZhong.Wang@Sun.COM 			break;
83210264SZhong.Wang@Sun.COM 
83310264SZhong.Wang@Sun.COM 		case AE_EVENT_UNSOL_FRAME:
83410264SZhong.Wang@Sun.COM 			fcoei_process_unsol_frame((fcoe_frame_t *)ae->ae_obj);
83510264SZhong.Wang@Sun.COM 			break;
83610264SZhong.Wang@Sun.COM 
83710264SZhong.Wang@Sun.COM 		case AE_EVENT_EXCHANGE:
83810264SZhong.Wang@Sun.COM 			fcoei_process_event_exchange(ae);
83910264SZhong.Wang@Sun.COM 			break;
84010264SZhong.Wang@Sun.COM 
84110264SZhong.Wang@Sun.COM 		case AE_EVENT_PORT:
84210264SZhong.Wang@Sun.COM 			fcoei_process_event_port(ae);
84310264SZhong.Wang@Sun.COM 			break;
84410264SZhong.Wang@Sun.COM 
84510264SZhong.Wang@Sun.COM 		case AE_EVENT_RESET:
84610264SZhong.Wang@Sun.COM 			fcoei_process_event_reset(ae);
84710264SZhong.Wang@Sun.COM 			break;
84810264SZhong.Wang@Sun.COM 
84910264SZhong.Wang@Sun.COM 		default:
85010264SZhong.Wang@Sun.COM 			FCOEI_LOG(__FUNCTION__, "unsupported events");
85110264SZhong.Wang@Sun.COM 		}
85210264SZhong.Wang@Sun.COM 
85310264SZhong.Wang@Sun.COM 	}
85410264SZhong.Wang@Sun.COM }
85510264SZhong.Wang@Sun.COM 
85610264SZhong.Wang@Sun.COM /*
85710264SZhong.Wang@Sun.COM  * fcoei_handle_tmout_xch_list
85810264SZhong.Wang@Sun.COM  *	Complete every exchange in the timed-out xch list of the soft state
85910264SZhong.Wang@Sun.COM  *
86010264SZhong.Wang@Sun.COM  * Input:
86110264SZhong.Wang@Sun.COM  *	ss = the soft state that need be handled
86210264SZhong.Wang@Sun.COM  *
86310264SZhong.Wang@Sun.COM  * Return:
86410264SZhong.Wang@Sun.COM  *	N/A
86510264SZhong.Wang@Sun.COM  *
86610264SZhong.Wang@Sun.COM  * Comment:
86710264SZhong.Wang@Sun.COM  *	When mod_hash_walk is in progress, we can't change the hashtable.
86810264SZhong.Wang@Sun.COM  *	This is post-walk handling of exchange timing
86910264SZhong.Wang@Sun.COM  */
87010264SZhong.Wang@Sun.COM void
fcoei_handle_comp_xch_list(fcoei_soft_state_t * ss)87110264SZhong.Wang@Sun.COM fcoei_handle_comp_xch_list(fcoei_soft_state_t *ss)
87210264SZhong.Wang@Sun.COM {
87310264SZhong.Wang@Sun.COM 	fcoei_exchange_t	*xch	  = NULL;
87410264SZhong.Wang@Sun.COM 
87510264SZhong.Wang@Sun.COM 	while ((xch = list_remove_head(&ss->ss_comp_xch_list)) != NULL) {
87610264SZhong.Wang@Sun.COM 		fcoei_complete_xch(xch, NULL, xch->xch_fpkt->pkt_state,
87710264SZhong.Wang@Sun.COM 		    xch->xch_fpkt->pkt_reason);
87810264SZhong.Wang@Sun.COM 	}
87910264SZhong.Wang@Sun.COM }
88010264SZhong.Wang@Sun.COM 
88110264SZhong.Wang@Sun.COM /*
88210264SZhong.Wang@Sun.COM  * fcoei_xch_check
88310264SZhong.Wang@Sun.COM  *	Check if the exchange timed out or link is down
88410264SZhong.Wang@Sun.COM  *
88510264SZhong.Wang@Sun.COM  * Input:
88610264SZhong.Wang@Sun.COM  *	key = rxid of the unsolicited exchange
88710264SZhong.Wang@Sun.COM  *	val = the unsolicited exchange
88810264SZhong.Wang@Sun.COM  *	arg = the soft state
88910264SZhong.Wang@Sun.COM  *
89010264SZhong.Wang@Sun.COM  * Return:
89110264SZhong.Wang@Sun.COM  *	MH_WALK_CONTINUE = continue to walk
89210264SZhong.Wang@Sun.COM  *
89310264SZhong.Wang@Sun.COM  * Comment:
89410264SZhong.Wang@Sun.COM  *	We need send ABTS for timed-out for solicited exchange
89510264SZhong.Wang@Sun.COM  *	If it's solicited FLOGI, we need set SS_FLAG_FLOGI_FAILED
89610264SZhong.Wang@Sun.COM  *	If the link is down, we think it has timed out too.
89710264SZhong.Wang@Sun.COM  */
898*12868SRenia.Miao@Sun.COM /* ARGSUSED */
89910264SZhong.Wang@Sun.COM static uint32_t
fcoei_xch_check(mod_hash_key_t key,mod_hash_val_t * val,void * arg)90010264SZhong.Wang@Sun.COM fcoei_xch_check(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
90110264SZhong.Wang@Sun.COM {
90210264SZhong.Wang@Sun.COM 	fcoei_exchange_t	*xch = (fcoei_exchange_t *)val;
90310264SZhong.Wang@Sun.COM 
90410264SZhong.Wang@Sun.COM 	ASSERT(xch->xch_ss == arg);
90510264SZhong.Wang@Sun.COM 	if ((xch->xch_end_tick < CURRENT_CLOCK) &&
90610264SZhong.Wang@Sun.COM 	    (xch->xch_ss->ss_link_state != FC_STATE_OFFLINE)) {
90710264SZhong.Wang@Sun.COM 		if (xch->xch_flags & XCH_FLAG_IN_SOL_HASH) {
90810264SZhong.Wang@Sun.COM 			ASSERT(xch->xch_oxid == CMHK(key));
90910264SZhong.Wang@Sun.COM 			/*
91010264SZhong.Wang@Sun.COM 			 * It's solicited exchange
91110264SZhong.Wang@Sun.COM 			 */
91210264SZhong.Wang@Sun.COM 			fcoei_abts_exchange(xch);
91310264SZhong.Wang@Sun.COM 			if (LA_ELS_FLOGI == ((ls_code_t *)(void *)
91410264SZhong.Wang@Sun.COM 			    xch->xch_fpkt->pkt_cmd)->ls_code) {
91510264SZhong.Wang@Sun.COM 				/*
91610264SZhong.Wang@Sun.COM 				 * It's solicited FLOGI
91710264SZhong.Wang@Sun.COM 				 */
91810264SZhong.Wang@Sun.COM 				xch->xch_ss->ss_flags |= SS_FLAG_FLOGI_FAILED;
91910264SZhong.Wang@Sun.COM 			}
92010264SZhong.Wang@Sun.COM 		}
92110264SZhong.Wang@Sun.COM 
92210264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "oxid-%x/rxid-%x  timed out",
92310264SZhong.Wang@Sun.COM 		    xch->xch_oxid, xch->xch_rxid);
92410264SZhong.Wang@Sun.COM 		xch->xch_flags |= XCH_FLAG_TMOUT;
92510264SZhong.Wang@Sun.COM 		xch->xch_fpkt->pkt_state = FC_PKT_TIMEOUT;
92610264SZhong.Wang@Sun.COM 		xch->xch_fpkt->pkt_reason = FC_REASON_ABORTED;
92710264SZhong.Wang@Sun.COM 		list_insert_tail(&xch->xch_ss->ss_comp_xch_list, xch);
92810264SZhong.Wang@Sun.COM 	} else if (xch->xch_ss->ss_link_state == FC_STATE_OFFLINE) {
92910264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "oxid-%x/rxid-%x  offline complete",
93010264SZhong.Wang@Sun.COM 		    xch->xch_oxid, xch->xch_rxid);
93110264SZhong.Wang@Sun.COM 		xch->xch_flags |= XCH_FLAG_TMOUT;
93210264SZhong.Wang@Sun.COM 		xch->xch_fpkt->pkt_state = FC_PKT_PORT_OFFLINE;
93310264SZhong.Wang@Sun.COM 		xch->xch_fpkt->pkt_reason = FC_REASON_OFFLINE;
93410264SZhong.Wang@Sun.COM 		list_insert_tail(&xch->xch_ss->ss_comp_xch_list, xch);
93510264SZhong.Wang@Sun.COM 	}
93610264SZhong.Wang@Sun.COM 
93710264SZhong.Wang@Sun.COM 	return (MH_WALK_CONTINUE);
93810264SZhong.Wang@Sun.COM }
93910264SZhong.Wang@Sun.COM 
94010264SZhong.Wang@Sun.COM /*
94110264SZhong.Wang@Sun.COM  * fcoei_init_ifm
94210264SZhong.Wang@Sun.COM  *	initialize fcoei_frame
94310264SZhong.Wang@Sun.COM  *
94410264SZhong.Wang@Sun.COM  * Input:
94510264SZhong.Wang@Sun.COM  *	frm = the frame that ifm need link to
94610264SZhong.Wang@Sun.COM  *	xch = the exchange that ifm need link to
94710264SZhong.Wang@Sun.COM  *
94810264SZhong.Wang@Sun.COM  * Return:
94910264SZhong.Wang@Sun.COM  *	N/A
95010264SZhong.Wang@Sun.COM  *
95110264SZhong.Wang@Sun.COM  * Comment:
95210264SZhong.Wang@Sun.COM  *	For solicited frames, it's called after FC frame header initialization
95310264SZhong.Wang@Sun.COM  *	For unsolicited frames, it's called just after the frame enters fcoei
95410264SZhong.Wang@Sun.COM  */
95510264SZhong.Wang@Sun.COM void
fcoei_init_ifm(fcoe_frame_t * frm,fcoei_exchange_t * xch)95610264SZhong.Wang@Sun.COM fcoei_init_ifm(fcoe_frame_t *frm, fcoei_exchange_t *xch)
95710264SZhong.Wang@Sun.COM {
95810264SZhong.Wang@Sun.COM 	FRM2IFM(frm)->ifm_frm = frm;
95910264SZhong.Wang@Sun.COM 	FRM2IFM(frm)->ifm_xch = xch;
96010264SZhong.Wang@Sun.COM 	FRM2IFM(frm)->ifm_rctl = FRM_R_CTL(frm);
96110264SZhong.Wang@Sun.COM }
96210264SZhong.Wang@Sun.COM 
96310264SZhong.Wang@Sun.COM /*
96410264SZhong.Wang@Sun.COM  * fcoei_trigger_fp_attach
96510264SZhong.Wang@Sun.COM  *	Trigger fp_attach for this fcoei port
96610264SZhong.Wang@Sun.COM  *
96710264SZhong.Wang@Sun.COM  * Input:
96810264SZhong.Wang@Sun.COM  *	arg = the soft state that fp will attach
96910264SZhong.Wang@Sun.COM  *
97010264SZhong.Wang@Sun.COM  * Return:
97110264SZhong.Wang@Sun.COM  *	N/A
97210264SZhong.Wang@Sun.COM  *
97310264SZhong.Wang@Sun.COM  * Comment:
97410264SZhong.Wang@Sun.COM  *	N/A
97510264SZhong.Wang@Sun.COM  */
97610264SZhong.Wang@Sun.COM static void
fcoei_trigger_fp_attach(void * arg)97710264SZhong.Wang@Sun.COM fcoei_trigger_fp_attach(void * arg)
97810264SZhong.Wang@Sun.COM {
97910264SZhong.Wang@Sun.COM 	fcoei_soft_state_t	*ss    = (fcoei_soft_state_t *)arg;
98010264SZhong.Wang@Sun.COM 	dev_info_t		*child = NULL;
98110264SZhong.Wang@Sun.COM 	int			 rval  = NDI_FAILURE;
98210264SZhong.Wang@Sun.COM 
98310264SZhong.Wang@Sun.COM 	ndi_devi_alloc_sleep(ss->ss_dip, "fp", DEVI_PSEUDO_NODEID, &child);
98410264SZhong.Wang@Sun.COM 	if (child == NULL) {
98510264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "can't alloc dev_info");
98610264SZhong.Wang@Sun.COM 		return;
98710264SZhong.Wang@Sun.COM 	}
98810264SZhong.Wang@Sun.COM 
98910264SZhong.Wang@Sun.COM 	/*
99010264SZhong.Wang@Sun.COM 	 * fp/fctl need this property
99110264SZhong.Wang@Sun.COM 	 */
99210264SZhong.Wang@Sun.COM 	if (ddi_prop_update_string(DDI_DEV_T_NONE, child,
99310264SZhong.Wang@Sun.COM 	    "bus-addr", "0,0") != DDI_PROP_SUCCESS) {
99410264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "update bus-addr failed");
99510264SZhong.Wang@Sun.COM 		(void) ndi_devi_free(child);
99610264SZhong.Wang@Sun.COM 		return;
99710264SZhong.Wang@Sun.COM 	}
99810264SZhong.Wang@Sun.COM 
99910264SZhong.Wang@Sun.COM 	/*
100010264SZhong.Wang@Sun.COM 	 * If it's physical HBA, fp.conf will register the property.
100110264SZhong.Wang@Sun.COM 	 * fcoei is one software HBA, so we need register it manually
100210264SZhong.Wang@Sun.COM 	 */
100310264SZhong.Wang@Sun.COM 	if (ddi_prop_update_int(DDI_DEV_T_NONE, child,
100410264SZhong.Wang@Sun.COM 	    "port", 0) != DDI_PROP_SUCCESS) {
100510264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "update port failed");
100610264SZhong.Wang@Sun.COM 		(void) ndi_devi_free(child);
100710264SZhong.Wang@Sun.COM 		return;
100810264SZhong.Wang@Sun.COM 	}
100910264SZhong.Wang@Sun.COM 
101010264SZhong.Wang@Sun.COM 	/*
101110264SZhong.Wang@Sun.COM 	 * It will call fp_attach eventually
101210264SZhong.Wang@Sun.COM 	 */
101310264SZhong.Wang@Sun.COM 	rval = ndi_devi_online(child, NDI_ONLINE_ATTACH);
101410264SZhong.Wang@Sun.COM 	ss->ss_flags &= ~SS_FLAG_TRIGGER_FP_ATTACH;
101510264SZhong.Wang@Sun.COM 	if (rval != NDI_SUCCESS) {
101610264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "devi_online: %d", rval);
101710264SZhong.Wang@Sun.COM 	} else {
101810264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "triggered successfully");
101910264SZhong.Wang@Sun.COM 	}
102010264SZhong.Wang@Sun.COM }
102110264SZhong.Wang@Sun.COM 
102210264SZhong.Wang@Sun.COM /*
102310264SZhong.Wang@Sun.COM  * fcoei_abts_exchange
102410264SZhong.Wang@Sun.COM  *	Send ABTS to abort solicited exchange
102510264SZhong.Wang@Sun.COM  *
102610264SZhong.Wang@Sun.COM  * Input:
102710264SZhong.Wang@Sun.COM  *	xch = the exchange that will be aborted
102810264SZhong.Wang@Sun.COM  *
102910264SZhong.Wang@Sun.COM  * Return:
103010264SZhong.Wang@Sun.COM  *	N/A
103110264SZhong.Wang@Sun.COM  *
103210264SZhong.Wang@Sun.COM  * Comment:
103310264SZhong.Wang@Sun.COM  *	ABTS frame uses the same oxid as the exchange
103410264SZhong.Wang@Sun.COM  */
103510264SZhong.Wang@Sun.COM static void
fcoei_abts_exchange(fcoei_exchange_t * xch)103610264SZhong.Wang@Sun.COM fcoei_abts_exchange(fcoei_exchange_t *xch)
103710264SZhong.Wang@Sun.COM {
103810264SZhong.Wang@Sun.COM 	fc_packet_t	*fpkt = xch->xch_fpkt;
103910264SZhong.Wang@Sun.COM 	fcoe_frame_t	*frm  = NULL;
104010264SZhong.Wang@Sun.COM 
104110264SZhong.Wang@Sun.COM 	/*
104210264SZhong.Wang@Sun.COM 	 * BLS_ABTS doesn't contain any other payload except FCFH
104310264SZhong.Wang@Sun.COM 	 */
104410264SZhong.Wang@Sun.COM 	frm = xch->xch_ss->ss_eport->eport_alloc_frame(xch->xch_ss->ss_eport,
104510264SZhong.Wang@Sun.COM 	    FCFH_SIZE, NULL);
104610264SZhong.Wang@Sun.COM 	if (frm == NULL) {
104710264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "can't alloc frame: %p", xch);
104810264SZhong.Wang@Sun.COM 		return;
104910264SZhong.Wang@Sun.COM 	}
105010264SZhong.Wang@Sun.COM 
105110264SZhong.Wang@Sun.COM 	FFM_R_CTL(0x81, frm);
105210264SZhong.Wang@Sun.COM 	FFM_D_ID(fpkt->pkt_cmd_fhdr.d_id, frm);
105310264SZhong.Wang@Sun.COM 	FFM_S_ID(fpkt->pkt_cmd_fhdr.s_id, frm);
105410264SZhong.Wang@Sun.COM 	FFM_F_CTL(0x090000, frm);
105510264SZhong.Wang@Sun.COM 	FFM_SEQ_ID(0x01, frm);
105610264SZhong.Wang@Sun.COM 	FFM_OXID(xch->xch_oxid, frm);
105710264SZhong.Wang@Sun.COM 	FFM_RXID(xch->xch_rxid, frm);
105810264SZhong.Wang@Sun.COM 	fcoei_init_ifm(frm, xch);
105910264SZhong.Wang@Sun.COM 	xch->xch_ss->ss_eport->eport_tx_frame(frm);
106010264SZhong.Wang@Sun.COM }
106110264SZhong.Wang@Sun.COM 
106210264SZhong.Wang@Sun.COM /*
106310264SZhong.Wang@Sun.COM  * fcoei_complete_xch
106410264SZhong.Wang@Sun.COM  *	Complete the exchange
106510264SZhong.Wang@Sun.COM  *
106610264SZhong.Wang@Sun.COM  * Input:
106710264SZhong.Wang@Sun.COM  *	xch = the exchange that will be completed
106810264SZhong.Wang@Sun.COM  *	frm = newly-allocated frame that has not been submitted
106910264SZhong.Wang@Sun.COM  *	pkt_state = LV fpkt state
107010264SZhong.Wang@Sun.COM  *	pkt_reason = LV fpkt reason
107110264SZhong.Wang@Sun.COM  *
107210264SZhong.Wang@Sun.COM  * Return:
107310264SZhong.Wang@Sun.COM  *	N/A
107410264SZhong.Wang@Sun.COM  *
107510264SZhong.Wang@Sun.COM  * Comment:
107610264SZhong.Wang@Sun.COM  *	N/A
107710264SZhong.Wang@Sun.COM  */
107810264SZhong.Wang@Sun.COM void
fcoei_complete_xch(fcoei_exchange_t * xch,fcoe_frame_t * frm,uint8_t pkt_state,uint8_t pkt_reason)107910264SZhong.Wang@Sun.COM fcoei_complete_xch(fcoei_exchange_t *xch, fcoe_frame_t *frm,
108010264SZhong.Wang@Sun.COM     uint8_t pkt_state, uint8_t pkt_reason)
108110264SZhong.Wang@Sun.COM {
108210264SZhong.Wang@Sun.COM 	mod_hash_val_t val;
108310264SZhong.Wang@Sun.COM 
108410264SZhong.Wang@Sun.COM 	if (pkt_state != FC_PKT_SUCCESS) {
108510264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "FHDR: %x/%x/%x, %x/%x/%x",
108610264SZhong.Wang@Sun.COM 		    xch->xch_fpkt->pkt_cmd_fhdr.r_ctl,
108710264SZhong.Wang@Sun.COM 		    xch->xch_fpkt->pkt_cmd_fhdr.f_ctl,
108810264SZhong.Wang@Sun.COM 		    xch->xch_fpkt->pkt_cmd_fhdr.type,
108910264SZhong.Wang@Sun.COM 		    xch->xch_fpkt->pkt_resp_fhdr.r_ctl,
109010264SZhong.Wang@Sun.COM 		    xch->xch_fpkt->pkt_resp_fhdr.f_ctl,
109110264SZhong.Wang@Sun.COM 		    xch->xch_fpkt->pkt_resp_fhdr.type);
109210264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "%p/%p/%x/%x",
109310264SZhong.Wang@Sun.COM 		    xch, frm, pkt_state, pkt_reason);
109410264SZhong.Wang@Sun.COM 	}
109510264SZhong.Wang@Sun.COM 
109610264SZhong.Wang@Sun.COM 	if (frm != NULL) {
109710264SZhong.Wang@Sun.COM 		/*
109810264SZhong.Wang@Sun.COM 		 * It's newly-allocated frame , which we haven't sent out
109910264SZhong.Wang@Sun.COM 		 */
110010264SZhong.Wang@Sun.COM 		xch->xch_ss->ss_eport->eport_free_netb(frm->frm_netb);
110110264SZhong.Wang@Sun.COM 		xch->xch_ss->ss_eport->eport_release_frame(frm);
110210264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "xch: %p, not submitted", xch);
110310264SZhong.Wang@Sun.COM 	}
110410264SZhong.Wang@Sun.COM 
110510264SZhong.Wang@Sun.COM 	/*
110610264SZhong.Wang@Sun.COM 	 * If xch is in hash table, we need remove it
110710264SZhong.Wang@Sun.COM 	 */
110810264SZhong.Wang@Sun.COM 	if (xch->xch_flags & XCH_FLAG_IN_SOL_HASH) {
110910767SZhong.Wang@Sun.COM 		(void) mod_hash_remove(xch->xch_ss->ss_sol_oxid_hash,
111010264SZhong.Wang@Sun.COM 		    FMHK(xch->xch_oxid), &val);
111110264SZhong.Wang@Sun.COM 		ASSERT((fcoei_exchange_t *)val == xch);
111210264SZhong.Wang@Sun.COM 		xch->xch_flags &= ~XCH_FLAG_IN_SOL_HASH;
111310264SZhong.Wang@Sun.COM 	} else if (xch->xch_flags & XCH_FLAG_IN_UNSOL_HASH) {
111410767SZhong.Wang@Sun.COM 		(void) mod_hash_remove(xch->xch_ss->ss_unsol_rxid_hash,
111510264SZhong.Wang@Sun.COM 		    FMHK(xch->xch_rxid), &val);
111610264SZhong.Wang@Sun.COM 		ASSERT((fcoei_exchange_t *)val == xch);
111710264SZhong.Wang@Sun.COM 		xch->xch_flags &= ~XCH_FLAG_IN_UNSOL_HASH;
111810264SZhong.Wang@Sun.COM 	} else {
111910264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "xch not in any hash: %p", xch);
112010264SZhong.Wang@Sun.COM 	}
112110264SZhong.Wang@Sun.COM 
112210264SZhong.Wang@Sun.COM 	xch->xch_fpkt->pkt_state = pkt_state;
112310264SZhong.Wang@Sun.COM 	xch->xch_fpkt->pkt_reason = pkt_reason;
112410264SZhong.Wang@Sun.COM 	if (xch->xch_fpkt->pkt_tran_flags & FC_TRAN_NO_INTR) {
112510264SZhong.Wang@Sun.COM 		FCOEI_LOG(__FUNCTION__, "polled xch is done: %p", xch);
112610264SZhong.Wang@Sun.COM 		sema_v(&xch->xch_sema);
112710264SZhong.Wang@Sun.COM 	} else {
112810264SZhong.Wang@Sun.COM 		xch->xch_fpkt->pkt_comp(xch->xch_fpkt);
112910264SZhong.Wang@Sun.COM 	}
113010264SZhong.Wang@Sun.COM }
1131