1fcf3ce44SJohn Forte /*
2fcf3ce44SJohn Forte  * CDDL HEADER START
3fcf3ce44SJohn Forte  *
4fcf3ce44SJohn Forte  * The contents of this file are subject to the terms of the
5fcf3ce44SJohn Forte  * Common Development and Distribution License (the "License").
6fcf3ce44SJohn Forte  * You may not use this file except in compliance with the License.
7fcf3ce44SJohn Forte  *
8fcf3ce44SJohn Forte  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fcf3ce44SJohn Forte  * or http://www.opensolaris.org/os/licensing.
10fcf3ce44SJohn Forte  * See the License for the specific language governing permissions
11fcf3ce44SJohn Forte  * and limitations under the License.
12fcf3ce44SJohn Forte  *
13fcf3ce44SJohn Forte  * When distributing Covered Code, include this CDDL HEADER in each
14fcf3ce44SJohn Forte  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fcf3ce44SJohn Forte  * If applicable, add the following below this CDDL HEADER, with the
16fcf3ce44SJohn Forte  * fields enclosed by brackets "[]" replaced with your own identifying
17fcf3ce44SJohn Forte  * information: Portions Copyright [yyyy] [name of copyright owner]
18fcf3ce44SJohn Forte  *
19fcf3ce44SJohn Forte  * CDDL HEADER END
20fcf3ce44SJohn Forte  */
21fcf3ce44SJohn Forte /*
2240b706cfSMilan Jurik  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23fcf3ce44SJohn Forte  * Use is subject to license terms.
24fcf3ce44SJohn Forte  */
25fcf3ce44SJohn Forte 
26fcf3ce44SJohn Forte /*
27fcf3ce44SJohn Forte  * fcsm - ULP Module for Fibre Channel SAN Management
28fcf3ce44SJohn Forte  */
29fcf3ce44SJohn Forte 
30fcf3ce44SJohn Forte #include <sys/types.h>
31fcf3ce44SJohn Forte #include <sys/file.h>
32fcf3ce44SJohn Forte #include <sys/kmem.h>
33fcf3ce44SJohn Forte #include <sys/scsi/scsi.h>
34fcf3ce44SJohn Forte #include <sys/var.h>
35fcf3ce44SJohn Forte #include <sys/byteorder.h>
36fcf3ce44SJohn Forte #include <sys/fibre-channel/fc.h>
37fcf3ce44SJohn Forte #include <sys/fibre-channel/impl/fc_ulpif.h>
38fcf3ce44SJohn Forte #include <sys/fibre-channel/ulp/fcsm.h>
39fcf3ce44SJohn Forte 
40fcf3ce44SJohn Forte /* Definitions */
417ff83669SZhong Wang #define	FCSM_VERSION		"20090729-1.28"
42fcf3ce44SJohn Forte #define	FCSM_NAME_VERSION	"SunFC FCSM v" FCSM_VERSION
43fcf3ce44SJohn Forte 
44fcf3ce44SJohn Forte /* Global Variables */
45fcf3ce44SJohn Forte static char		fcsm_name[] = "FCSM";
46fcf3ce44SJohn Forte static void		*fcsm_state = NULL;
47fcf3ce44SJohn Forte static kmutex_t		fcsm_global_mutex;
48fcf3ce44SJohn Forte static uint32_t		fcsm_flag = FCSM_IDLE;
49fcf3ce44SJohn Forte static dev_info_t	*fcsm_dip = NULL;
50fcf3ce44SJohn Forte static fcsm_t		*fcsm_port_head = NULL;
51fcf3ce44SJohn Forte static kmem_cache_t	*fcsm_job_cache = NULL;
52fcf3ce44SJohn Forte static int		fcsm_num_attaching = 0;
53fcf3ce44SJohn Forte static int		fcsm_num_detaching = 0;
54fcf3ce44SJohn Forte static int		fcsm_detached = 0;
55fcf3ce44SJohn Forte 
56fcf3ce44SJohn Forte static int		fcsm_max_cmd_retries = FCSM_MAX_CMD_RETRIES;
57fcf3ce44SJohn Forte static int		fcsm_retry_interval = FCSM_RETRY_INTERVAL;
58fcf3ce44SJohn Forte static int		fcsm_retry_ticker = FCSM_RETRY_TICKER;
59fcf3ce44SJohn Forte static int		fcsm_offline_ticker = FCSM_OFFLINE_TICKER;
60fcf3ce44SJohn Forte static int		fcsm_max_job_retries = FCSM_MAX_JOB_RETRIES;
61fcf3ce44SJohn Forte static clock_t		fcsm_retry_ticks;
62fcf3ce44SJohn Forte static clock_t		fcsm_offline_ticks;
63fcf3ce44SJohn Forte 
64fcf3ce44SJohn Forte 
65fcf3ce44SJohn Forte 
66fcf3ce44SJohn Forte #ifdef DEBUG
6798ea43c5SYuri Pankov uint32_t		fcsm_debug = 0;
68fcf3ce44SJohn Forte #endif
69fcf3ce44SJohn Forte 
70fcf3ce44SJohn Forte 
71fcf3ce44SJohn Forte /* Character/Block entry points */
72fcf3ce44SJohn Forte struct cb_ops	fcsm_cb_ops = {
73fcf3ce44SJohn Forte 	fcsm_open,	/* open */
74fcf3ce44SJohn Forte 	fcsm_close,	/* close */
75fcf3ce44SJohn Forte 	nodev,		/* strategy */
76fcf3ce44SJohn Forte 	nodev,		/* print */
77fcf3ce44SJohn Forte 	nodev,		/* dump */
78fcf3ce44SJohn Forte 	nodev,		/* read */
79fcf3ce44SJohn Forte 	nodev,		/* write */
80fcf3ce44SJohn Forte 	fcsm_ioctl,	/* ioctl */
81fcf3ce44SJohn Forte 	nodev,		/* devmap */
82fcf3ce44SJohn Forte 	nodev,		/* mmap */
83fcf3ce44SJohn Forte 	nodev,		/* segmap */
84fcf3ce44SJohn Forte 	nochpoll,	/* poll */
85fcf3ce44SJohn Forte 	ddi_prop_op,
86fcf3ce44SJohn Forte 	NULL,		/* streams info */
87fcf3ce44SJohn Forte 	D_NEW | D_MP,
88fcf3ce44SJohn Forte 	CB_REV,
89fcf3ce44SJohn Forte 	nodev,		/* aread */
90fcf3ce44SJohn Forte 	nodev		/* awrite */
91fcf3ce44SJohn Forte };
92fcf3ce44SJohn Forte 
93fcf3ce44SJohn Forte struct dev_ops fcsm_ops = {
94fcf3ce44SJohn Forte 	DEVO_REV,
95fcf3ce44SJohn Forte 	0,		/* refcnt */
96fcf3ce44SJohn Forte 	fcsm_getinfo,	/* get info */
97fcf3ce44SJohn Forte 	nulldev,	/* identify (obsolete) */
98fcf3ce44SJohn Forte 	nulldev,	/* probe (not required for self-identifying devices) */
99fcf3ce44SJohn Forte 	fcsm_attach,	/* attach */
100fcf3ce44SJohn Forte 	fcsm_detach,	/* detach */
101fcf3ce44SJohn Forte 	nodev,		/* reset */
102fcf3ce44SJohn Forte 	&fcsm_cb_ops,	/* char/block entry points structure for leaf drivers */
103fcf3ce44SJohn Forte 	NULL,		/* bus operations for nexus driver */
104fcf3ce44SJohn Forte 	NULL		/* power management */
105fcf3ce44SJohn Forte };
106fcf3ce44SJohn Forte 
107fcf3ce44SJohn Forte 
108fcf3ce44SJohn Forte struct modldrv modldrv = {
109fcf3ce44SJohn Forte 	&mod_driverops,
110fcf3ce44SJohn Forte 	FCSM_NAME_VERSION,
111fcf3ce44SJohn Forte 	&fcsm_ops
112fcf3ce44SJohn Forte };
113fcf3ce44SJohn Forte 
114fcf3ce44SJohn Forte struct modlinkage modlinkage = {
115fcf3ce44SJohn Forte 	MODREV_1,
116fcf3ce44SJohn Forte 	&modldrv,
117fcf3ce44SJohn Forte 	NULL
118fcf3ce44SJohn Forte };
119fcf3ce44SJohn Forte 
120fcf3ce44SJohn Forte static fc_ulp_modinfo_t fcsm_modinfo = {
121fcf3ce44SJohn Forte 	&fcsm_modinfo,		/* ulp_handle */
122fcf3ce44SJohn Forte 	FCTL_ULP_MODREV_4,	/* ulp_rev */
123fcf3ce44SJohn Forte 	FC_TYPE_FC_SERVICES,	/* ulp_type */
124fcf3ce44SJohn Forte 	fcsm_name,		/* ulp_name */
125fcf3ce44SJohn Forte 	0,			/* ulp_statec_mask: get all statec callbacks */
126fcf3ce44SJohn Forte 	fcsm_port_attach,	/* ulp_port_attach */
127fcf3ce44SJohn Forte 	fcsm_port_detach,	/* ulp_port_detach */
128fcf3ce44SJohn Forte 	fcsm_port_ioctl,	/* ulp_port_ioctl */
129fcf3ce44SJohn Forte 	fcsm_els_cb,		/* ulp_els_callback */
130fcf3ce44SJohn Forte 	fcsm_data_cb,		/* ulp_data_callback */
131fcf3ce44SJohn Forte 	fcsm_statec_cb		/* ulp_statec_callback */
132fcf3ce44SJohn Forte };
133fcf3ce44SJohn Forte 
134fcf3ce44SJohn Forte struct fcsm_xlat_pkt_state {
135fcf3ce44SJohn Forte 	uchar_t	xlat_state;
136fcf3ce44SJohn Forte 	int	xlat_rval;
137fcf3ce44SJohn Forte } fcsm_xlat_pkt_state [] = {
138fcf3ce44SJohn Forte 	{ FC_PKT_SUCCESS,		FC_SUCCESS },
139fcf3ce44SJohn Forte 	{ FC_PKT_REMOTE_STOP,		FC_FAILURE },
140fcf3ce44SJohn Forte 	{ FC_PKT_LOCAL_RJT,		FC_TRANSPORT_ERROR },
141fcf3ce44SJohn Forte 	{ FC_PKT_NPORT_RJT,		FC_PREJECT },
142fcf3ce44SJohn Forte 	{ FC_PKT_FABRIC_RJT,		FC_FREJECT },
143fcf3ce44SJohn Forte 	{ FC_PKT_LOCAL_BSY,		FC_TRAN_BUSY },
144fcf3ce44SJohn Forte 	{ FC_PKT_TRAN_BSY,		FC_TRAN_BUSY },
145fcf3ce44SJohn Forte 	{ FC_PKT_NPORT_BSY,		FC_PBUSY },
146fcf3ce44SJohn Forte 	{ FC_PKT_FABRIC_BSY,		FC_FBUSY },
147fcf3ce44SJohn Forte 	{ FC_PKT_LS_RJT,		FC_PREJECT },
148fcf3ce44SJohn Forte 	{ FC_PKT_BA_RJT,		FC_PREJECT },
149fcf3ce44SJohn Forte 	{ FC_PKT_TIMEOUT,		FC_FAILURE },
150fcf3ce44SJohn Forte 	{ FC_PKT_FS_RJT,		FC_FAILURE },
151fcf3ce44SJohn Forte 	{ FC_PKT_TRAN_ERROR,		FC_TRANSPORT_ERROR },
152fcf3ce44SJohn Forte 	{ FC_PKT_FAILURE,		FC_FAILURE },
153fcf3ce44SJohn Forte 	{ FC_PKT_PORT_OFFLINE,		FC_OFFLINE },
154fcf3ce44SJohn Forte 	{ FC_PKT_ELS_IN_PROGRESS,	FC_FAILURE }
155fcf3ce44SJohn Forte };
156fcf3ce44SJohn Forte 
157fcf3ce44SJohn Forte struct fcsm_xlat_port_state {
158fcf3ce44SJohn Forte 	uint32_t	xlat_pstate;
159fcf3ce44SJohn Forte 	caddr_t		xlat_state_str;
160fcf3ce44SJohn Forte } fcsm_xlat_port_state [] = {
161fcf3ce44SJohn Forte 	{ FC_STATE_OFFLINE,		"OFFLINE" },
162fcf3ce44SJohn Forte 	{ FC_STATE_ONLINE,		"ONLINE" },
163fcf3ce44SJohn Forte 	{ FC_STATE_LOOP,		"LOOP" },
164fcf3ce44SJohn Forte 	{ FC_STATE_NAMESERVICE,		"NAMESERVICE" },
165fcf3ce44SJohn Forte 	{ FC_STATE_RESET,		"RESET" },
167fcf3ce44SJohn Forte 	{ FC_STATE_LIP,			"LIP" },
168fcf3ce44SJohn Forte 	{ FC_STATE_LIP_LBIT_SET,	"LIP_LBIT_SET" },
169fcf3ce44SJohn Forte 	{ FC_STATE_DEVICE_CHANGE,	"DEVICE_CHANGE" },
171fcf3ce44SJohn Forte };
172fcf3ce44SJohn Forte 
173fcf3ce44SJohn Forte struct fcsm_xlat_topology {
174fcf3ce44SJohn Forte 	uint32_t	xlat_top;
175fcf3ce44SJohn Forte 	caddr_t		xlat_top_str;
176fcf3ce44SJohn Forte } fcsm_xlat_topology [] = {
177fcf3ce44SJohn Forte 	{ FC_TOP_UNKNOWN,	"UNKNOWN" },
178fcf3ce44SJohn Forte 	{ FC_TOP_PRIVATE_LOOP,	"Private Loop" },
179fcf3ce44SJohn Forte 	{ FC_TOP_PUBLIC_LOOP,	"Public Loop" },
180fcf3ce44SJohn Forte 	{ FC_TOP_FABRIC,	"Fabric" },
181fcf3ce44SJohn Forte 	{ FC_TOP_PT_PT,		"Point-to-Point" },
182fcf3ce44SJohn Forte 	{ FC_TOP_NO_NS,		"NO_NS" }
183fcf3ce44SJohn Forte };
184fcf3ce44SJohn Forte 
185fcf3ce44SJohn Forte struct fcsm_xlat_dev_type {
186fcf3ce44SJohn Forte 	uint32_t	xlat_type;
187fcf3ce44SJohn Forte 	caddr_t		xlat_str;
188fcf3ce44SJohn Forte } fcsm_xlat_dev_type [] = {
189fcf3ce44SJohn Forte 	{ PORT_DEVICE_NOCHANGE,		"No Change" },
190fcf3ce44SJohn Forte 	{ PORT_DEVICE_NEW,		"New" },
191fcf3ce44SJohn Forte 	{ PORT_DEVICE_OLD,		"Old" },
192fcf3ce44SJohn Forte 	{ PORT_DEVICE_CHANGED,		"Changed" },
193fcf3ce44SJohn Forte 	{ PORT_DEVICE_DELETE,		"Delete" },
194fcf3ce44SJohn Forte 	{ PORT_DEVICE_USER_LOGIN,	"User Login" },
195fcf3ce44SJohn Forte 	{ PORT_DEVICE_USER_LOGOUT,	"User Logout" },
196fcf3ce44SJohn Forte 	{ PORT_DEVICE_USER_CREATE,	"User Create" },
197fcf3ce44SJohn Forte 	{ PORT_DEVICE_USER_DELETE,	"User Delete" }
198fcf3ce44SJohn Forte };
199fcf3ce44SJohn Forte 
200fcf3ce44SJohn Forte int
_init(void)201fcf3ce44SJohn Forte _init(void)
202fcf3ce44SJohn Forte {
203fcf3ce44SJohn Forte 	int		rval;
204fcf3ce44SJohn Forte 
205fcf3ce44SJohn Forte 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_init"));
206fcf3ce44SJohn Forte 
207fcf3ce44SJohn Forte 	fcsm_retry_ticks = drv_usectohz(fcsm_retry_ticker * 1000 * 1000);
208fcf3ce44SJohn Forte 	fcsm_offline_ticks = drv_usectohz(fcsm_offline_ticker * 1000 * 1000);
209fcf3ce44SJohn Forte 
210fcf3ce44SJohn Forte 	if (rval = ddi_soft_state_init(&fcsm_state, sizeof (fcsm_t),
211fcf3ce44SJohn Forte 	    FCSM_INIT_INSTANCES)) {
212fcf3ce44SJohn Forte 		fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
213fcf3ce44SJohn Forte 		    "_init: ddi_soft_state_init failed");
214fcf3ce44SJohn Forte 		return (ENOMEM);
215fcf3ce44SJohn Forte 	}
216fcf3ce44SJohn Forte 
217fcf3ce44SJohn Forte 	mutex_init(&fcsm_global_mutex, NULL, MUTEX_DRIVER, NULL);
218fcf3ce44SJohn Forte 
219fcf3ce44SJohn Forte 	fcsm_job_cache = kmem_cache_create("fcsm_job_cache",
220fcf3ce44SJohn Forte 	    sizeof (fcsm_job_t), 8, fcsm_job_cache_constructor,
221fcf3ce44SJohn Forte 	    fcsm_job_cache_destructor, NULL, NULL, NULL, 0);
222fcf3ce44SJohn Forte 
223fcf3ce44SJohn Forte 	if (fcsm_job_cache == NULL) {
224fcf3ce44SJohn Forte 		mutex_destroy(&fcsm_global_mutex);
225fcf3ce44SJohn Forte 		ddi_soft_state_fini(&fcsm_state);
226fcf3ce44SJohn Forte 		return (ENOMEM);
227fcf3ce44SJohn Forte 	}
228fcf3ce44SJohn Forte 
229fcf3ce44SJohn Forte 	/*
230fcf3ce44SJohn Forte 	 * Now call fc_ulp_add to add this ULP in the transport layer
231fcf3ce44SJohn Forte 	 * database. This will cause 'ulp_port_attach' callback function
232fcf3ce44SJohn Forte 	 * to be called.
233fcf3ce44SJohn Forte 	 */
234fcf3ce44SJohn Forte 	rval = fc_ulp_add(&fcsm_modinfo);
235fcf3ce44SJohn Forte 	if (rval != 0) {
236fcf3ce44SJohn Forte 		switch (rval) {
237fcf3ce44SJohn Forte 		case FC_ULP_SAMEMODULE:
238fcf3ce44SJohn Forte 			fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
239fcf3ce44SJohn Forte 			    "_init: FC SAN Management module is already "
240fcf3ce44SJohn Forte 			    "registered with transport layer");
241fcf3ce44SJohn Forte 			rval = EEXIST;
242fcf3ce44SJohn Forte 			break;
243fcf3ce44SJohn Forte 
244fcf3ce44SJohn Forte 		case FC_ULP_SAMETYPE:
245fcf3ce44SJohn Forte 			fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
246fcf3ce44SJohn Forte 			    "_init: Another module with same type 0x%x is "
247fcf3ce44SJohn Forte 			    "already registered with transport layer",
248fcf3ce44SJohn Forte 			    fcsm_modinfo.ulp_type);
249fcf3ce44SJohn Forte 			rval = EEXIST;
250fcf3ce44SJohn Forte 			break;
251fcf3ce44SJohn Forte 
252fcf3ce44SJohn Forte 		case FC_BADULP:
253fcf3ce44SJohn Forte 			fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
254fcf3ce44SJohn Forte 			    "_init: Please upgrade this module. Current "
255fcf3ce44SJohn Forte 			    "version 0x%x is not the most recent version",
256fcf3ce44SJohn Forte 			    fcsm_modinfo.ulp_rev);
257fcf3ce44SJohn Forte 			rval = EIO;
258fcf3ce44SJohn Forte 			break;
259fcf3ce44SJohn Forte 		default:
260fcf3ce44SJohn Forte 			fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
261fcf3ce44SJohn Forte 			    "_init: fc_ulp_add failed with status 0x%x", rval);
262fcf3ce44SJohn Forte 			rval = EIO;
263fcf3ce44SJohn Forte 			break;
264fcf3ce44SJohn Forte 		}
265fcf3ce44SJohn Forte 		kmem_cache_destroy(fcsm_job_cache);
266fcf3ce44SJohn Forte 		mutex_destroy(&fcsm_global_mutex);
267fcf3ce44SJohn Forte 		ddi_soft_state_fini(&fcsm_state);
268fcf3ce44SJohn Forte 		return (rval);
269fcf3ce44SJohn Forte 	}
270fcf3ce44SJohn Forte 
271fcf3ce44SJohn Forte 	if ((rval = mod_install(&modlinkage)) != 0) {
273fcf3ce44SJohn Forte 		    "_init: mod_install failed with status 0x%x", rval));
274fcf3ce44SJohn Forte 		(void) fc_ulp_remove(&fcsm_modinfo);
275fcf3ce44SJohn Forte 		kmem_cache_destroy(fcsm_job_cache);
276fcf3ce44SJohn Forte 		mutex_destroy(&fcsm_global_mutex);
277fcf3ce44SJohn Forte 		ddi_soft_state_fini(&fcsm_state);
278fcf3ce44SJohn Forte 		return (rval);
279fcf3ce44SJohn Forte 	}
280fcf3ce44SJohn Forte 
281fcf3ce44SJohn Forte 	return (rval);
282fcf3ce44SJohn Forte }
283fcf3ce44SJohn Forte 
284fcf3ce44SJohn Forte int
_fini(void)285fcf3ce44SJohn Forte _fini(void)
286fcf3ce44SJohn Forte {
287fcf3ce44SJohn Forte 	int	rval;
2887ff83669SZhong Wang #ifdef	DEBUG
289fcf3ce44SJohn Forte 	int	status;
290fcf3ce44SJohn Forte #endif /* DEBUG */
291fcf3ce44SJohn Forte 
292fcf3ce44SJohn Forte 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_fini"));
293fcf3ce44SJohn Forte 
294fcf3ce44SJohn Forte 	/*
295fcf3ce44SJohn Forte 	 * don't start cleaning up until we know that the module remove
296fcf3ce44SJohn Forte 	 * has worked  -- if this works, then we know that each instance
297fcf3ce44SJohn Forte 	 * has successfully been DDI_DETACHed
298fcf3ce44SJohn Forte 	 */
299fcf3ce44SJohn Forte 	if ((rval = mod_remove(&modlinkage)) != 0) {
300fcf3ce44SJohn Forte 		return (rval);
301fcf3ce44SJohn Forte 	}
302fcf3ce44SJohn Forte 
303fcf3ce44SJohn Forte #ifdef DEBUG
304fcf3ce44SJohn Forte 	status = fc_ulp_remove(&fcsm_modinfo);
305fcf3ce44SJohn Forte 	if (status != 0) {
307fcf3ce44SJohn Forte 		    "_fini: fc_ulp_remove failed with status 0x%x", status));
308fcf3ce44SJohn Forte 	}
309fcf3ce44SJohn Forte #else
310fcf3ce44SJohn Forte 	(void) fc_ulp_remove(&fcsm_modinfo);
311fcf3ce44SJohn Forte #endif /* DEBUG */
312fcf3ce44SJohn Forte 
313fcf3ce44SJohn Forte 	fcsm_detached = 0;
314fcf3ce44SJohn Forte 
315fcf3ce44SJohn Forte 	/*
316fcf3ce44SJohn Forte 	 * It is possible to modunload fcsm manually, which will cause
317fcf3ce44SJohn Forte 	 * a bypass of all the port_detach functionality.  We may need
318fcf3ce44SJohn Forte 	 * to force that code path to be executed to properly clean up
319fcf3ce44SJohn Forte 	 * in that case.
320fcf3ce44SJohn Forte 	 */
321fcf3ce44SJohn Forte 	fcsm_force_port_detach_all();
322fcf3ce44SJohn Forte 
323fcf3ce44SJohn Forte 	kmem_cache_destroy(fcsm_job_cache);
324fcf3ce44SJohn Forte 	mutex_destroy(&fcsm_global_mutex);
325fcf3ce44SJohn Forte 	ddi_soft_state_fini(&fcsm_state);
326fcf3ce44SJohn Forte 
327fcf3ce44SJohn Forte 	return (rval);
328fcf3ce44SJohn Forte }
329fcf3ce44SJohn Forte 
330fcf3ce44SJohn Forte 
331fcf3ce44SJohn Forte int
_info(struct modinfo * modinfop)332fcf3ce44SJohn Forte _info(struct modinfo *modinfop)
333fcf3ce44SJohn Forte {
334fcf3ce44SJohn Forte 	return (mod_info(&modlinkage, modinfop));
335fcf3ce44SJohn Forte }
336fcf3ce44SJohn Forte 
337fcf3ce44SJohn Forte /* ARGSUSED */
338fcf3ce44SJohn Forte static int
fcsm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)339fcf3ce44SJohn Forte fcsm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
340fcf3ce44SJohn Forte {
341fcf3ce44SJohn Forte 	int rval = DDI_FAILURE;
342fcf3ce44SJohn Forte 
344fcf3ce44SJohn Forte 	    "attach: cmd 0x%x", cmd));
345fcf3ce44SJohn Forte 
346fcf3ce44SJohn Forte 	switch (cmd) {
347fcf3ce44SJohn Forte 	case DDI_ATTACH:
348fcf3ce44SJohn Forte 		mutex_enter(&fcsm_global_mutex);
349fcf3ce44SJohn Forte 		if (fcsm_dip != NULL) {
350fcf3ce44SJohn Forte 			mutex_exit(&fcsm_global_mutex);
352fcf3ce44SJohn Forte 			    "attach: duplicate attach of fcsm!!"));
353fcf3ce44SJohn Forte 			break;
354fcf3ce44SJohn Forte 		}
355fcf3ce44SJohn Forte 
356fcf3ce44SJohn Forte 		fcsm_dip = dip;
357fcf3ce44SJohn Forte 
358fcf3ce44SJohn Forte 		/*
359fcf3ce44SJohn Forte 		 * The detach routine cleans up all the port instances
360fcf3ce44SJohn Forte 		 * i.e. it detaches all ports.
361fcf3ce44SJohn Forte 		 * If _fini never got called after detach, then
362fcf3ce44SJohn Forte 		 * perform an fc_ulp_remove() followed by fc_ulp_add()
363fcf3ce44SJohn Forte 		 * to ensure that port_attach callbacks are called
364fcf3ce44SJohn Forte 		 * again.
365fcf3ce44SJohn Forte 		 */
366fcf3ce44SJohn Forte 		if (fcsm_detached) {
367fcf3ce44SJohn Forte 			int status;
368fcf3ce44SJohn Forte 
370fcf3ce44SJohn Forte 			    "attach: rebinding to transport driver"));
371fcf3ce44SJohn Forte 
372fcf3ce44SJohn Forte 			mutex_exit(&fcsm_global_mutex);
373fcf3ce44SJohn Forte 
374fcf3ce44SJohn Forte 			(void) fc_ulp_remove(&fcsm_modinfo);
375fcf3ce44SJohn Forte 
376fcf3ce44SJohn Forte 			/*
377fcf3ce44SJohn Forte 			 * Reset the detached flag, so that ports can attach
378fcf3ce44SJohn Forte 			 */
379fcf3ce44SJohn Forte 			mutex_enter(&fcsm_global_mutex);
380fcf3ce44SJohn Forte 			fcsm_detached = 0;
381fcf3ce44SJohn Forte 			mutex_exit(&fcsm_global_mutex);
382fcf3ce44SJohn Forte 
383fcf3ce44SJohn Forte 			status = fc_ulp_add(&fcsm_modinfo);
384fcf3ce44SJohn Forte 
385fcf3ce44SJohn Forte 			if (status != 0) {
386fcf3ce44SJohn Forte 				/*
387fcf3ce44SJohn Forte 				 * ULP add failed. So set the
388fcf3ce44SJohn Forte 				 * detached flag again
389fcf3ce44SJohn Forte 				 */
390fcf3ce44SJohn Forte 				mutex_enter(&fcsm_global_mutex);
391fcf3ce44SJohn Forte 				fcsm_detached = 1;
392fcf3ce44SJohn Forte 				mutex_exit(&fcsm_global_mutex);
393fcf3ce44SJohn Forte 
394fcf3ce44SJohn Forte 				switch (status) {
395fcf3ce44SJohn Forte 				case FC_ULP_SAMEMODULE:
396fcf3ce44SJohn Forte 					fcsm_display(CE_WARN, SM_LOG, NULL,
397fcf3ce44SJohn Forte 					    NULL, "attach: FC SAN Management "
398fcf3ce44SJohn Forte 					    "module is already "
399fcf3ce44SJohn Forte 					    "registered with transport layer");
400fcf3ce44SJohn Forte 					break;
401fcf3ce44SJohn Forte 
402fcf3ce44SJohn Forte 				case FC_ULP_SAMETYPE:
403fcf3ce44SJohn Forte 					fcsm_display(CE_WARN, SM_LOG, NULL,
404fcf3ce44SJohn Forte 					    NULL, "attach: Another module with "
405fcf3ce44SJohn Forte 					    "same type 0x%x is already "
406fcf3ce44SJohn Forte 					    "registered with transport layer",
407fcf3ce44SJohn Forte 					    fcsm_modinfo.ulp_type);
408fcf3ce44SJohn Forte 					break;
409fcf3ce44SJohn Forte 
410fcf3ce44SJohn Forte 				case FC_BADULP:
411fcf3ce44SJohn Forte 					fcsm_display(CE_WARN, SM_LOG, NULL,
412fcf3ce44SJohn Forte 					    NULL, "attach: Please upgrade this "
413fcf3ce44SJohn Forte 					    "module. Current version 0x%x is "
414fcf3ce44SJohn Forte 					    "not the most recent version",
415fcf3ce44SJohn Forte 					    fcsm_modinfo.ulp_rev);
416fcf3ce44SJohn Forte 					break;
417fcf3ce44SJohn Forte 				default:
418fcf3ce44SJohn Forte 					fcsm_display(CE_WARN, SM_LOG, NULL,
419fcf3ce44SJohn Forte 					    NULL, "attach: fc_ulp_add failed "
420fcf3ce44SJohn Forte 					    "with status 0x%x", status);
421fcf3ce44SJohn Forte 					break;
422fcf3ce44SJohn Forte 				}
423fcf3ce44SJohn Forte 
424fcf3ce44SJohn Forte 				/* Return failure */
425fcf3ce44SJohn Forte 				break;
426fcf3ce44SJohn Forte 			}
427fcf3ce44SJohn Forte 
428fcf3ce44SJohn Forte 			mutex_enter(&fcsm_global_mutex);
429fcf3ce44SJohn Forte 		}
430fcf3ce44SJohn Forte 
431fcf3ce44SJohn Forte 		/* Create a minor node */
432fcf3ce44SJohn Forte 		if (ddi_create_minor_node(fcsm_dip, "fcsm", S_IFCHR,
433*483e494aSToomas Soome 		    0, DDI_PSEUDO, 0) == DDI_SUCCESS) {
434fcf3ce44SJohn Forte 			/* Announce presence of the device */
435fcf3ce44SJohn Forte 			mutex_exit(&fcsm_global_mutex);
436fcf3ce44SJohn Forte 			ddi_report_dev(dip);
437fcf3ce44SJohn Forte 			rval = DDI_SUCCESS;
438fcf3ce44SJohn Forte 		} else {
439fcf3ce44SJohn Forte 			fcsm_dip = NULL;
440fcf3ce44SJohn Forte 			mutex_exit(&fcsm_global_mutex);
441fcf3ce44SJohn Forte 			fcsm_display(CE_WARN, SM_LOG_AND_CONSOLE,
442fcf3ce44SJohn Forte 			    NULL, NULL, "attach: create minor node failed");
443fcf3ce44SJohn Forte 		}
444fcf3ce44SJohn Forte 		break;
445fcf3ce44SJohn Forte 
446fcf3ce44SJohn Forte 	case DDI_RESUME:
447fcf3ce44SJohn Forte 		rval = DDI_SUCCESS;
448fcf3ce44SJohn Forte 		break;
449fcf3ce44SJohn Forte 
450fcf3ce44SJohn Forte 	default:
452fcf3ce44SJohn Forte 		    "attach: unknown cmd 0x%x dip 0x%p", cmd, dip));
453fcf3ce44SJohn Forte 		break;
454fcf3ce44SJohn Forte 	}
455fcf3ce44SJohn Forte 
456fcf3ce44SJohn Forte 	return (rval);
457fcf3ce44SJohn Forte }
458fcf3ce44SJohn Forte 
459fcf3ce44SJohn Forte /* ARGSUSED */
460fcf3ce44SJohn Forte static int
fcsm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)461fcf3ce44SJohn Forte fcsm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
462fcf3ce44SJohn Forte {
463fcf3ce44SJohn Forte 	int	instance;
464fcf3ce44SJohn Forte 	int	rval = DDI_SUCCESS;
465fcf3ce44SJohn Forte 
466fcf3ce44SJohn Forte 	instance = getminor((dev_t)arg);
467fcf3ce44SJohn Forte 
468fcf3ce44SJohn Forte 	switch (cmd) {
469fcf3ce44SJohn Forte 	case DDI_INFO_DEVT2INSTANCE:
470fcf3ce44SJohn Forte 		*result = (void *)(long)instance; /* minor number is instance */
471fcf3ce44SJohn Forte 		break;
472fcf3ce44SJohn Forte 
473fcf3ce44SJohn Forte 	case DDI_INFO_DEVT2DEVINFO:
474fcf3ce44SJohn Forte 		mutex_enter(&fcsm_global_mutex);
475fcf3ce44SJohn Forte 		*result = (void *)fcsm_dip;
476fcf3ce44SJohn Forte 		mutex_exit(&fcsm_global_mutex);
477fcf3ce44SJohn Forte 		break;
478fcf3ce44SJohn Forte 
479fcf3ce44SJohn Forte 	default:
480fcf3ce44SJohn Forte 		rval = DDI_FAILURE;
481fcf3ce44SJohn Forte 		break;
482fcf3ce44SJohn Forte 	}
483fcf3ce44SJohn Forte 
484fcf3ce44SJohn Forte 	return (rval);
485fcf3ce44SJohn Forte }
486fcf3ce44SJohn Forte 
487fcf3ce44SJohn Forte 
488fcf3ce44SJohn Forte /* ARGSUSED */
489fcf3ce44SJohn Forte static int
fcsm_port_attach(opaque_t ulph,fc_ulp_port_info_t * pinfo,fc_attach_cmd_t cmd,uint32_t s_id)490fcf3ce44SJohn Forte fcsm_port_attach(opaque_t ulph, fc_ulp_port_info_t *pinfo,
491fcf3ce44SJohn Forte     fc_attach_cmd_t cmd, uint32_t s_id)
492fcf3ce44SJohn Forte {
493fcf3ce44SJohn Forte 	int	instance;
494fcf3ce44SJohn Forte 	int	rval = FC_FAILURE;
495fcf3ce44SJohn Forte 
496fcf3ce44SJohn Forte 	instance = ddi_get_instance(pinfo->port_dip);
497fcf3ce44SJohn Forte 
498fcf3ce44SJohn Forte 	/*
499fcf3ce44SJohn Forte 	 * Set the attaching flag, so that fcsm_detach will fail, if
500fcf3ce44SJohn Forte 	 * port attach is in progress.
501fcf3ce44SJohn Forte 	 */
502fcf3ce44SJohn Forte 	mutex_enter(&fcsm_global_mutex);
503fcf3ce44SJohn Forte 	if (fcsm_detached) {
504fcf3ce44SJohn Forte 		mutex_exit(&fcsm_global_mutex);
505fcf3ce44SJohn Forte 
507fcf3ce44SJohn Forte 		    "port_attach: end. detach in progress. failing attach "
508fcf3ce44SJohn Forte 		    "instance 0x%x", instance));
509fcf3ce44SJohn Forte 		return (((cmd == FC_CMD_POWER_UP) || (cmd == FC_CMD_RESUME)) ?
510fcf3ce44SJohn Forte 		    FC_FAILURE_SILENT : FC_FAILURE);
511fcf3ce44SJohn Forte 	}
512fcf3ce44SJohn Forte 
513fcf3ce44SJohn Forte 	fcsm_num_attaching++;
514fcf3ce44SJohn Forte 	mutex_exit(&fcsm_global_mutex);
515fcf3ce44SJohn Forte 
516fcf3ce44SJohn Forte 	switch (cmd) {
517fcf3ce44SJohn Forte 	case FC_CMD_ATTACH:
518fcf3ce44SJohn Forte 		if (fcsm_handle_port_attach(pinfo, s_id, instance)
519fcf3ce44SJohn Forte 		    != DDI_SUCCESS) {
520fcf3ce44SJohn Forte 			ASSERT(ddi_get_soft_state(fcsm_state,
521fcf3ce44SJohn Forte 			    instance) == NULL);
522fcf3ce44SJohn Forte 			break;
523fcf3ce44SJohn Forte 		}
524fcf3ce44SJohn Forte 		rval = FC_SUCCESS;
525fcf3ce44SJohn Forte 		break;
526fcf3ce44SJohn Forte 
527fcf3ce44SJohn Forte 	case FC_CMD_RESUME:
528fcf3ce44SJohn Forte 	case FC_CMD_POWER_UP: {
529fcf3ce44SJohn Forte 		fcsm_t	*fcsm;
530fcf3ce44SJohn Forte 		char fcsm_pathname[MAXPATHLEN];
531fcf3ce44SJohn Forte 
533fcf3ce44SJohn Forte 		    "port_attach: cmd 0x%x instance 0x%x", cmd, instance));
534fcf3ce44SJohn Forte 
535fcf3ce44SJohn Forte 		/* Get the soft state structure */
536fcf3ce44SJohn Forte 		if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
538fcf3ce44SJohn Forte 			    "port_attach: instance 0x%x, cmd 0x%x "
539fcf3ce44SJohn Forte 			    "get softstate failed", instance, cmd));
540fcf3ce44SJohn Forte 			break;
541fcf3ce44SJohn Forte 		}
542fcf3ce44SJohn Forte 
543fcf3ce44SJohn Forte 		ASSERT(fcsm->sm_instance == instance);
544fcf3ce44SJohn Forte 
545fcf3ce44SJohn Forte 		/* If this instance is not attached, then return failure */
546fcf3ce44SJohn Forte 		mutex_enter(&fcsm->sm_mutex);
547fcf3ce44SJohn Forte 		if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
548fcf3ce44SJohn Forte 			mutex_exit(&fcsm->sm_mutex);
549fcf3ce44SJohn Forte 			fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
550fcf3ce44SJohn Forte 			    "port_detach: port is not attached");
551fcf3ce44SJohn Forte 			break;
552fcf3ce44SJohn Forte 		}
553fcf3ce44SJohn Forte 		mutex_exit(&fcsm->sm_mutex);
554fcf3ce44SJohn Forte 
555fcf3ce44SJohn Forte 		if (fcsm_handle_port_resume(ulph, pinfo, cmd, s_id, fcsm) !=
556fcf3ce44SJohn Forte 		    DDI_SUCCESS) {
557fcf3ce44SJohn Forte 			break;
558fcf3ce44SJohn Forte 		}
559fcf3ce44SJohn Forte 
560fcf3ce44SJohn Forte 		(void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
561fcf3ce44SJohn Forte 		fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
562fcf3ce44SJohn Forte 		    "attached to path %s", fcsm_pathname);
563fcf3ce44SJohn Forte 		rval = FC_SUCCESS;
564fcf3ce44SJohn Forte 		break;
565fcf3ce44SJohn Forte 	}
566fcf3ce44SJohn Forte 
567fcf3ce44SJohn Forte 	default:
569fcf3ce44SJohn Forte 		    "port_attach: unknown cmd 0x%x for port 0x%x",
570fcf3ce44SJohn Forte 		    cmd, instance));
571fcf3ce44SJohn Forte 		break;
572fcf3ce44SJohn Forte 	}
573fcf3ce44SJohn Forte 
574fcf3ce44SJohn Forte 	mutex_enter(&fcsm_global_mutex);
575fcf3ce44SJohn Forte 	fcsm_num_attaching--;
576fcf3ce44SJohn Forte 	mutex_exit(&fcsm_global_mutex);
577fcf3ce44SJohn Forte 	return (rval);
578fcf3ce44SJohn Forte }
579fcf3ce44SJohn Forte 
580fcf3ce44SJohn Forte 
581fcf3ce44SJohn Forte static int
fcsm_handle_port_attach(fc_ulp_port_info_t * pinfo,uint32_t s_id,int instance)582fcf3ce44SJohn Forte fcsm_handle_port_attach(fc_ulp_port_info_t *pinfo, uint32_t s_id, int instance)
583fcf3ce44SJohn Forte {
584fcf3ce44SJohn Forte 	fcsm_t		*fcsm;
585fcf3ce44SJohn Forte 	kthread_t	*thread;
586fcf3ce44SJohn Forte 	char		name[32];
587fcf3ce44SJohn Forte 	char fcsm_pathname[MAXPATHLEN];
588fcf3ce44SJohn Forte 
589fcf3ce44SJohn Forte 	/* Allocate a soft state structure for the port */
590fcf3ce44SJohn Forte 	if (ddi_soft_state_zalloc(fcsm_state, instance) != DDI_SUCCESS) {
591fcf3ce44SJohn Forte 		fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
592fcf3ce44SJohn Forte 		    "port_attach: instance 0x%x, soft state alloc failed",
593fcf3ce44SJohn Forte 		    instance);
594fcf3ce44SJohn Forte 		return (DDI_FAILURE);
595fcf3ce44SJohn Forte 	}
596fcf3ce44SJohn Forte 
597fcf3ce44SJohn Forte 	if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
598fcf3ce44SJohn Forte 		fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
599fcf3ce44SJohn Forte 		    "port_attach: instance 0x%x, get soft state failed",
600fcf3ce44SJohn Forte 		    instance);
601fcf3ce44SJohn Forte 		ddi_soft_state_free(fcsm_state, instance);
602fcf3ce44SJohn Forte 		return (DDI_FAILURE);
603fcf3ce44SJohn Forte 	}
604fcf3ce44SJohn Forte 
605fcf3ce44SJohn Forte 
606fcf3ce44SJohn Forte 	/* Initialize the mutex */
607fcf3ce44SJohn Forte 	mutex_init(&fcsm->sm_mutex, NULL, MUTEX_DRIVER, NULL);
608fcf3ce44SJohn Forte 	cv_init(&fcsm->sm_job_cv, NULL, CV_DRIVER, NULL);
609fcf3ce44SJohn Forte 
610fcf3ce44SJohn Forte 	mutex_enter(&fcsm->sm_mutex);
611fcf3ce44SJohn Forte 	fcsm->sm_flags		|= FCSM_ATTACHING;
612fcf3ce44SJohn Forte 	fcsm->sm_sid		= s_id;
613fcf3ce44SJohn Forte 	fcsm->sm_instance	= instance;
614fcf3ce44SJohn Forte 	fcsm->sm_port_state	= pinfo->port_state;
615fcf3ce44SJohn Forte 
616fcf3ce44SJohn Forte 	/*
617fcf3ce44SJohn Forte 	 * Make a copy of the port_information structure, since fctl
618fcf3ce44SJohn Forte 	 * uses a temporary structure.
619fcf3ce44SJohn Forte 	 */
620fcf3ce44SJohn Forte 	fcsm->sm_port_info	= *pinfo;	/* Structure copy !!! */
621fcf3ce44SJohn Forte 	mutex_exit(&fcsm->sm_mutex);
622fcf3ce44SJohn Forte 
623fcf3ce44SJohn Forte 
624fcf3ce44SJohn Forte 	(void) sprintf(name, "fcsm%d_cmd_cache", fcsm->sm_instance);
625fcf3ce44SJohn Forte 	fcsm->sm_cmd_cache = kmem_cache_create(name,
626fcf3ce44SJohn Forte 	    sizeof (fcsm_cmd_t) + pinfo->port_fca_pkt_size, 8,
627fcf3ce44SJohn Forte 	    fcsm_cmd_cache_constructor, fcsm_cmd_cache_destructor,
628fcf3ce44SJohn Forte 	    NULL, (void *)fcsm, NULL, 0);
629fcf3ce44SJohn Forte 	if (fcsm->sm_cmd_cache == NULL) {
630fcf3ce44SJohn Forte 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
631fcf3ce44SJohn Forte 		    "port_attach: pkt cache create failed");
632fcf3ce44SJohn Forte 		cv_destroy(&fcsm->sm_job_cv);
633fcf3ce44SJohn Forte 		mutex_destroy(&fcsm->sm_mutex);
634fcf3ce44SJohn Forte 		ddi_soft_state_free(fcsm_state, instance);
635fcf3ce44SJohn Forte 		return (DDI_FAILURE);
636fcf3ce44SJohn Forte 	}
637fcf3ce44SJohn Forte 
638fcf3ce44SJohn Forte 	thread = thread_create((caddr_t)NULL, 0, fcsm_job_thread,
639fcf3ce44SJohn Forte 	    (caddr_t)fcsm, 0, &p0, TS_RUN, v.v_maxsyspri-2);
640fcf3ce44SJohn Forte 	if (thread == NULL) {
641fcf3ce44SJohn Forte 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
642fcf3ce44SJohn Forte 		    "port_attach: job thread create failed");
643fcf3ce44SJohn Forte 		kmem_cache_destroy(fcsm->sm_cmd_cache);
644fcf3ce44SJohn Forte 		cv_destroy(&fcsm->sm_job_cv);
645fcf3ce44SJohn Forte 		mutex_destroy(&fcsm->sm_mutex);
646fcf3ce44SJohn Forte 		ddi_soft_state_free(fcsm_state, instance);
647fcf3ce44SJohn Forte 		return (DDI_FAILURE);
648fcf3ce44SJohn Forte 	}
649fcf3ce44SJohn Forte 
650fcf3ce44SJohn Forte 	fcsm->sm_thread = thread;
651fcf3ce44SJohn Forte 
652fcf3ce44SJohn Forte 	/* Add this structure to fcsm global linked list */
653fcf3ce44SJohn Forte 	mutex_enter(&fcsm_global_mutex);
654fcf3ce44SJohn Forte 	if (fcsm_port_head == NULL) {
655fcf3ce44SJohn Forte 		fcsm_port_head = fcsm;
656fcf3ce44SJohn Forte 	} else {
657fcf3ce44SJohn Forte 		fcsm->sm_next = fcsm_port_head;
658fcf3ce44SJohn Forte 		fcsm_port_head = fcsm;
659fcf3ce44SJohn Forte 	}
660fcf3ce44SJohn Forte 	mutex_exit(&fcsm_global_mutex);
661fcf3ce44SJohn Forte 
662fcf3ce44SJohn Forte 	mutex_enter(&fcsm->sm_mutex);
663fcf3ce44SJohn Forte 	fcsm->sm_flags &= ~FCSM_ATTACHING;
664fcf3ce44SJohn Forte 	fcsm->sm_flags |= FCSM_ATTACHED;
665fcf3ce44SJohn Forte 	fcsm->sm_port_top = pinfo->port_flags;
666fcf3ce44SJohn Forte 	fcsm->sm_port_state = pinfo->port_state;
6677ff83669SZhong Wang 	if (pinfo->port_acc_attr == NULL) {
6687ff83669SZhong Wang 		/*
6697ff83669SZhong Wang 		 * The corresponding FCA doesn't support DMA at all
6707ff83669SZhong Wang 		 */
6717ff83669SZhong Wang 		fcsm->sm_flags |= FCSM_USING_NODMA_FCA;
6727ff83669SZhong Wang 	}
673fcf3ce44SJohn Forte 	mutex_exit(&fcsm->sm_mutex);
674fcf3ce44SJohn Forte 
675fcf3ce44SJohn Forte 	(void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
676fcf3ce44SJohn Forte 	fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
677fcf3ce44SJohn Forte 	    "attached to path %s", fcsm_pathname);
678fcf3ce44SJohn Forte 
679fcf3ce44SJohn Forte 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
680fcf3ce44SJohn Forte 	    "port_attach: state <%s>(0x%x) topology <%s>(0x%x)",
681fcf3ce44SJohn Forte 	    fcsm_port_state_to_str(FC_PORT_STATE_MASK(pinfo->port_state)),
682fcf3ce44SJohn Forte 	    pinfo->port_state,
683fcf3ce44SJohn Forte 	    fcsm_topology_to_str(pinfo->port_flags), pinfo->port_flags));
684fcf3ce44SJohn Forte 
685fcf3ce44SJohn Forte 	return (DDI_SUCCESS);
686fcf3ce44SJohn Forte }
687fcf3ce44SJohn Forte 
688fcf3ce44SJohn Forte static int
fcsm_handle_port_resume(opaque_t ulph,fc_ulp_port_info_t * pinfo,fc_attach_cmd_t cmd,uint32_t s_id,fcsm_t * fcsm)689fcf3ce44SJohn Forte fcsm_handle_port_resume(opaque_t ulph, fc_ulp_port_info_t *pinfo,
690fcf3ce44SJohn Forte     fc_attach_cmd_t cmd, uint32_t s_id, fcsm_t *fcsm)
691fcf3ce44SJohn Forte {
692fcf3ce44SJohn Forte 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
693fcf3ce44SJohn Forte 	    "port_resume: cmd 0x%x", cmd));
694fcf3ce44SJohn Forte 
695fcf3ce44SJohn Forte 	mutex_enter(&fcsm->sm_mutex);
696fcf3ce44SJohn Forte 
697fcf3ce44SJohn Forte 	switch (cmd) {
698fcf3ce44SJohn Forte 	case FC_CMD_RESUME:
699fcf3ce44SJohn Forte 		ASSERT(!(fcsm->sm_flags & FCSM_POWER_DOWN));
700fcf3ce44SJohn Forte 		fcsm->sm_flags &= ~FCSM_SUSPENDED;
701fcf3ce44SJohn Forte 		break;
702fcf3ce44SJohn Forte 
703fcf3ce44SJohn Forte 	case FC_CMD_POWER_UP:
704fcf3ce44SJohn Forte 		/* If port is suspended, then no need to resume */
705fcf3ce44SJohn Forte 		fcsm->sm_flags &= ~FCSM_POWER_DOWN;
706fcf3ce44SJohn Forte 		if (fcsm->sm_flags & FCSM_SUSPENDED) {
707fcf3ce44SJohn Forte 			mutex_exit(&fcsm->sm_mutex);
708fcf3ce44SJohn Forte 			return (DDI_SUCCESS);
709fcf3ce44SJohn Forte 		}
710fcf3ce44SJohn Forte 		break;
711fcf3ce44SJohn Forte 	default:
712fcf3ce44SJohn Forte 		mutex_exit(&fcsm->sm_mutex);
713fcf3ce44SJohn Forte 		return (DDI_FAILURE);
714fcf3ce44SJohn Forte 	}
715fcf3ce44SJohn Forte 
716fcf3ce44SJohn Forte 	fcsm->sm_sid = s_id;
717fcf3ce44SJohn Forte 
718fcf3ce44SJohn Forte 	/*
719fcf3ce44SJohn Forte 	 * Make a copy of the new port_information structure
720fcf3ce44SJohn Forte 	 */
721fcf3ce44SJohn Forte 	fcsm->sm_port_info	= *pinfo;	/* Structure copy !!! */
722fcf3ce44SJohn Forte 	mutex_exit(&fcsm->sm_mutex);
723fcf3ce44SJohn Forte 
724fcf3ce44SJohn Forte 	fcsm_resume_port(fcsm);
725fcf3ce44SJohn Forte 
726fcf3ce44SJohn Forte 	/*
727fcf3ce44SJohn Forte 	 * Invoke state change processing.
728fcf3ce44SJohn Forte 	 * This will ensure that
729fcf3ce44SJohn Forte 	 *    - offline timer is started if new port state changed to offline.
730fcf3ce44SJohn Forte 	 *    - MGMT_SERVER_LOGIN flag is reset.
731fcf3ce44SJohn Forte 	 *    - Port topology is updated.
732fcf3ce44SJohn Forte 	 */
733fcf3ce44SJohn Forte 	fcsm_statec_cb(ulph, (opaque_t)pinfo->port_handle, pinfo->port_state,
734fcf3ce44SJohn Forte 	    pinfo->port_flags, NULL, 0, s_id);
735fcf3ce44SJohn Forte 
736fcf3ce44SJohn Forte 	return (DDI_SUCCESS);
737fcf3ce44SJohn Forte }
738fcf3ce44SJohn Forte 
739fcf3ce44SJohn Forte 
740fcf3ce44SJohn Forte /* ARGSUSED */
741fcf3ce44SJohn Forte static int
fcsm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)742fcf3ce44SJohn Forte fcsm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
743fcf3ce44SJohn Forte {
744fcf3ce44SJohn Forte 	int	rval = DDI_SUCCESS;
745fcf3ce44SJohn Forte 
746fcf3ce44SJohn Forte 	switch (cmd) {
747fcf3ce44SJohn Forte 	case DDI_DETACH: {
748fcf3ce44SJohn Forte 		fcsm_t	*fcsm;
749fcf3ce44SJohn Forte 
751fcf3ce44SJohn Forte 		    "detach: start. cmd <DETACH>", cmd));
752fcf3ce44SJohn Forte 
753fcf3ce44SJohn Forte 		mutex_enter(&fcsm_global_mutex);
754fcf3ce44SJohn Forte 
755fcf3ce44SJohn Forte 		/*
756fcf3ce44SJohn Forte 		 * If port attach/detach in progress, then wait for 5 seconds
757fcf3ce44SJohn Forte 		 * for them to complete.
758fcf3ce44SJohn Forte 		 */
759fcf3ce44SJohn Forte 		if (fcsm_num_attaching || fcsm_num_detaching) {
760fcf3ce44SJohn Forte 			int count;
761fcf3ce44SJohn Forte 
763fcf3ce44SJohn Forte 			    "detach: wait for port attach/detach to complete"));
764fcf3ce44SJohn Forte 
765fcf3ce44SJohn Forte 			count = 0;
766fcf3ce44SJohn Forte 			while ((count++ <= 30) &&
767fcf3ce44SJohn Forte 			    (fcsm_num_attaching || fcsm_num_detaching)) {
768fcf3ce44SJohn Forte 				mutex_exit(&fcsm_global_mutex);
769fcf3ce44SJohn Forte 				delay(drv_usectohz(1000000));
770fcf3ce44SJohn Forte 				mutex_enter(&fcsm_global_mutex);
771fcf3ce44SJohn Forte 			}
772fcf3ce44SJohn Forte 
773fcf3ce44SJohn Forte 			/* Port attach/detach still in prog, so fail detach */
774fcf3ce44SJohn Forte 			if (fcsm_num_attaching || fcsm_num_detaching) {
775fcf3ce44SJohn Forte 				mutex_exit(&fcsm_global_mutex);
776fcf3ce44SJohn Forte 				FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL,
777fcf3ce44SJohn Forte 				    NULL, "detach: Failing detach. port "
778fcf3ce44SJohn Forte 				    "attach/detach in progress"));
779fcf3ce44SJohn Forte 				rval = DDI_FAILURE;
780fcf3ce44SJohn Forte 				break;
781fcf3ce44SJohn Forte 			}
782fcf3ce44SJohn Forte 		}
783fcf3ce44SJohn Forte 
784fcf3ce44SJohn Forte 		if (fcsm_port_head == NULL) {
785fcf3ce44SJohn Forte 			/* Not much do, Succeed to detach. */
786fcf3ce44SJohn Forte 			ddi_remove_minor_node(fcsm_dip, NULL);
787fcf3ce44SJohn Forte 			fcsm_dip = NULL;
788fcf3ce44SJohn Forte 			fcsm_detached = 0;
789fcf3ce44SJohn Forte 			mutex_exit(&fcsm_global_mutex);
790fcf3ce44SJohn Forte 			break;
791fcf3ce44SJohn Forte 		}
792fcf3ce44SJohn Forte 
793fcf3ce44SJohn Forte 		/*
794fcf3ce44SJohn Forte 		 * Check to see, if any ports are active.
795fcf3ce44SJohn Forte 		 * If not, then set the DETACHING flag to indicate
796fcf3ce44SJohn Forte 		 * that they are being detached.
797fcf3ce44SJohn Forte 		 */
798fcf3ce44SJohn Forte 		fcsm = fcsm_port_head;
799fcf3ce44SJohn Forte 		while (fcsm != NULL) {
800fcf3ce44SJohn Forte 
801fcf3ce44SJohn Forte 			mutex_enter(&fcsm->sm_mutex);
802fcf3ce44SJohn Forte 			if (!(fcsm->sm_flags & FCSM_ATTACHED) ||
803fcf3ce44SJohn Forte 			    fcsm->sm_ncmds || fcsm->sm_cb_count) {
804fcf3ce44SJohn Forte 				/* port is busy. We can't detach */
805fcf3ce44SJohn Forte 				mutex_exit(&fcsm->sm_mutex);
806fcf3ce44SJohn Forte 				break;
807fcf3ce44SJohn Forte 			}
808fcf3ce44SJohn Forte 
809fcf3ce44SJohn Forte 			fcsm->sm_flags |= FCSM_DETACHING;
810fcf3ce44SJohn Forte 			mutex_exit(&fcsm->sm_mutex);
811fcf3ce44SJohn Forte 
812fcf3ce44SJohn Forte 			fcsm = fcsm->sm_next;
813fcf3ce44SJohn Forte 		}
814fcf3ce44SJohn Forte 
815fcf3ce44SJohn Forte 		/*
816fcf3ce44SJohn Forte 		 * If all ports could not be marked for detaching,
817fcf3ce44SJohn Forte 		 * then clear the flags and fail the detach.
818fcf3ce44SJohn Forte 		 * Also if a port attach is currently in progress
819fcf3ce44SJohn Forte 		 * then fail the detach.
820fcf3ce44SJohn Forte 		 */
821fcf3ce44SJohn Forte 		if (fcsm != NULL || fcsm_num_attaching || fcsm_num_detaching) {
822fcf3ce44SJohn Forte 			/*
823fcf3ce44SJohn Forte 			 * Some ports were busy, so can't detach.
824fcf3ce44SJohn Forte 			 * Clear the DETACHING flag and return failure
825fcf3ce44SJohn Forte 			 */
826fcf3ce44SJohn Forte 			fcsm = fcsm_port_head;
827fcf3ce44SJohn Forte 			while (fcsm != NULL) {
828fcf3ce44SJohn Forte 				mutex_enter(&fcsm->sm_mutex);
829fcf3ce44SJohn Forte 				if (fcsm->sm_flags & FCSM_DETACHING) {
830fcf3ce44SJohn Forte 					fcsm->sm_flags &= ~FCSM_DETACHING;
831fcf3ce44SJohn Forte 				}
832fcf3ce44SJohn Forte 				mutex_exit(&fcsm->sm_mutex);
833fcf3ce44SJohn Forte 
834fcf3ce44SJohn Forte 				fcsm = fcsm->sm_next;
835fcf3ce44SJohn Forte 			}
836fcf3ce44SJohn Forte 			mutex_exit(&fcsm_global_mutex);
837fcf3ce44SJohn Forte 			return (DDI_FAILURE);
838fcf3ce44SJohn Forte 		} else {
839fcf3ce44SJohn Forte 			fcsm_detached = 1;
840fcf3ce44SJohn Forte 			/*
841fcf3ce44SJohn Forte 			 * Mark all the detaching ports as detached, as we
842fcf3ce44SJohn Forte 			 * will be detaching them
843fcf3ce44SJohn Forte 			 */
844fcf3ce44SJohn Forte 			fcsm = fcsm_port_head;
845fcf3ce44SJohn Forte 			while (fcsm != NULL) {
846fcf3ce44SJohn Forte 				mutex_enter(&fcsm->sm_mutex);
847fcf3ce44SJohn Forte 				fcsm->sm_flags &= ~FCSM_DETACHING;
848fcf3ce44SJohn Forte 				fcsm->sm_flags |= FCSM_DETACHED;
849fcf3ce44SJohn Forte 				mutex_exit(&fcsm->sm_mutex);
850fcf3ce44SJohn Forte 
851fcf3ce44SJohn Forte 				fcsm = fcsm->sm_next;
852fcf3ce44SJohn Forte 			}
853fcf3ce44SJohn Forte 		}
854fcf3ce44SJohn Forte 		mutex_exit(&fcsm_global_mutex);
855fcf3ce44SJohn Forte 
856fcf3ce44SJohn Forte 
857fcf3ce44SJohn Forte 		/*
858fcf3ce44SJohn Forte 		 * Go ahead and detach the ports
859fcf3ce44SJohn Forte 		 */
860fcf3ce44SJohn Forte 		mutex_enter(&fcsm_global_mutex);
861fcf3ce44SJohn Forte 		while (fcsm_port_head != NULL) {
862fcf3ce44SJohn Forte 			fcsm = fcsm_port_head;
863fcf3ce44SJohn Forte 			mutex_exit(&fcsm_global_mutex);
864fcf3ce44SJohn Forte 
865fcf3ce44SJohn Forte 			/*
866fcf3ce44SJohn Forte 			 * Call fcsm_cleanup_port(). This cleansup and
867fcf3ce44SJohn Forte 			 * removes the fcsm structure from global linked list
868fcf3ce44SJohn Forte 			 */
869fcf3ce44SJohn Forte 			fcsm_cleanup_port(fcsm);
870fcf3ce44SJohn Forte 
871fcf3ce44SJohn Forte 			/*
872fcf3ce44SJohn Forte 			 * Soft state cleanup done.
873fcf3ce44SJohn Forte 			 * Remember that fcsm struct doesn't exist anymore.
874fcf3ce44SJohn Forte 			 */
875fcf3ce44SJohn Forte 
876fcf3ce44SJohn Forte 			mutex_enter(&fcsm_global_mutex);
877fcf3ce44SJohn Forte 		}
878fcf3ce44SJohn Forte 
879fcf3ce44SJohn Forte 		ddi_remove_minor_node(fcsm_dip, NULL);
880fcf3ce44SJohn Forte 		fcsm_dip = NULL;
881fcf3ce44SJohn Forte 		mutex_exit(&fcsm_global_mutex);
882fcf3ce44SJohn Forte 		break;
883fcf3ce44SJohn Forte 	}
884fcf3ce44SJohn Forte 
885fcf3ce44SJohn Forte 	case DDI_SUSPEND:
886fcf3ce44SJohn Forte 		rval = DDI_SUCCESS;
887fcf3ce44SJohn Forte 		break;
888fcf3ce44SJohn Forte 
889fcf3ce44SJohn Forte 	default:
891fcf3ce44SJohn Forte 		    "detach: unknown cmd 0x%x", cmd));
892fcf3ce44SJohn Forte 		rval = DDI_FAILURE;
893fcf3ce44SJohn Forte 		break;
894fcf3ce44SJohn Forte 	}
895fcf3ce44SJohn Forte 
897fcf3ce44SJohn Forte 	    "detach: end. cmd 0x%x, rval 0x%x", cmd, rval));
898fcf3ce44SJohn Forte 
899fcf3ce44SJohn Forte 	return (rval);
900fcf3ce44SJohn Forte }
901fcf3ce44SJohn Forte 
902fcf3ce44SJohn Forte 
903fcf3ce44SJohn Forte /* ARGSUSED */
904fcf3ce44SJohn Forte static void
fcsm_force_port_detach_all(void)905fcf3ce44SJohn Forte fcsm_force_port_detach_all(void)
906fcf3ce44SJohn Forte {
907fcf3ce44SJohn Forte 	fcsm_t	*fcsm;
908fcf3ce44SJohn Forte 
909fcf3ce44SJohn Forte 	fcsm = fcsm_port_head;
910fcf3ce44SJohn Forte 
911fcf3ce44SJohn Forte 	while (fcsm) {
912fcf3ce44SJohn Forte 		fcsm_cleanup_port(fcsm);
913fcf3ce44SJohn Forte 		/*
914fcf3ce44SJohn Forte 		 * fcsm_cleanup_port will remove the current fcsm structure
915fcf3ce44SJohn Forte 		 * from the list, which will cause fcsm_port_head to point
916fcf3ce44SJohn Forte 		 * to what would have been the next structure on the list.
917fcf3ce44SJohn Forte 		 */
918fcf3ce44SJohn Forte 		fcsm = fcsm_port_head;
919fcf3ce44SJohn Forte 	}
920fcf3ce44SJohn Forte }
921fcf3ce44SJohn Forte 
922fcf3ce44SJohn Forte 
923fcf3ce44SJohn Forte /* ARGSUSED */
924fcf3ce44SJohn Forte static int
fcsm_port_detach(opaque_t ulph,fc_ulp_port_info_t * pinfo,fc_detach_cmd_t cmd)925fcf3ce44SJohn Forte fcsm_port_detach(opaque_t ulph, fc_ulp_port_info_t *pinfo, fc_detach_cmd_t cmd)
926fcf3ce44SJohn Forte {
927fcf3ce44SJohn Forte 	int	instance;
928fcf3ce44SJohn Forte 	int	rval = FC_FAILURE;
929fcf3ce44SJohn Forte 	fcsm_t	*fcsm;
930fcf3ce44SJohn Forte 
931fcf3ce44SJohn Forte 	instance = ddi_get_instance(pinfo->port_dip);
932fcf3ce44SJohn Forte 
933fcf3ce44SJohn Forte 	mutex_enter(&fcsm_global_mutex);
934fcf3ce44SJohn Forte 	if (fcsm_detached) {
935fcf3ce44SJohn Forte 		mutex_exit(&fcsm_global_mutex);
936fcf3ce44SJohn Forte 
938fcf3ce44SJohn Forte 		    "port_detach: end. instance 0x%x, fcsm is detached",
939fcf3ce44SJohn Forte 		    instance));
940fcf3ce44SJohn Forte 		return (FC_SUCCESS);
941fcf3ce44SJohn Forte 	}
942fcf3ce44SJohn Forte 	fcsm_num_detaching++;	/* Set the flag */
943fcf3ce44SJohn Forte 	mutex_exit(&fcsm_global_mutex);
944fcf3ce44SJohn Forte 
945fcf3ce44SJohn Forte 	/* Get the soft state structure */
946fcf3ce44SJohn Forte 	if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
948fcf3ce44SJohn Forte 		    "port_detach: instance 0x%x, cmd 0x%x get softstate failed",
949fcf3ce44SJohn Forte 		    instance, cmd));
950fcf3ce44SJohn Forte 		mutex_enter(&fcsm_global_mutex);
951fcf3ce44SJohn Forte 		fcsm_num_detaching--;
952fcf3ce44SJohn Forte 		mutex_exit(&fcsm_global_mutex);
953fcf3ce44SJohn Forte 		return (rval);
954fcf3ce44SJohn Forte 	}
955fcf3ce44SJohn Forte 
956fcf3ce44SJohn Forte 	ASSERT(fcsm->sm_instance == instance);
957fcf3ce44SJohn Forte 
958fcf3ce44SJohn Forte 	/* If this instance is not attached, then fail the detach */
959fcf3ce44SJohn Forte 	mutex_enter(&fcsm->sm_mutex);
960fcf3ce44SJohn Forte 	if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
961fcf3ce44SJohn Forte 		mutex_exit(&fcsm->sm_mutex);
962fcf3ce44SJohn Forte 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
963fcf3ce44SJohn Forte 		    "port_detach: port is not attached");
964fcf3ce44SJohn Forte 		mutex_enter(&fcsm_global_mutex);
965fcf3ce44SJohn Forte 		fcsm_num_detaching--;
966fcf3ce44SJohn Forte 		mutex_exit(&fcsm_global_mutex);
967fcf3ce44SJohn Forte 		return (rval);
968fcf3ce44SJohn Forte 	}
969fcf3ce44SJohn Forte 	mutex_exit(&fcsm->sm_mutex);
970fcf3ce44SJohn Forte 
971fcf3ce44SJohn Forte 	/*
972fcf3ce44SJohn Forte 	 * If fcsm has been detached, then all instance has already been
973fcf3ce44SJohn Forte 	 * detached or are being detached. So succeed this detach.
974fcf3ce44SJohn Forte 	 */
975fcf3ce44SJohn Forte 
976fcf3ce44SJohn Forte 	switch (cmd) {
9777ff83669SZhong Wang 	case FC_CMD_DETACH:
9787ff83669SZhong Wang 	case FC_CMD_SUSPEND:
9797ff83669SZhong Wang 	case FC_CMD_POWER_DOWN:
9807ff83669SZhong Wang 		break;
981fcf3ce44SJohn Forte 
9827ff83669SZhong Wang 	default:
9837ff83669SZhong Wang 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
9847ff83669SZhong Wang 		    "port_detach: port unknown cmd 0x%x", cmd));
9857ff83669SZhong Wang 		mutex_enter(&fcsm_global_mutex);
9867ff83669SZhong Wang 		fcsm_num_detaching--;
9877ff83669SZhong Wang 		mutex_exit(&fcsm_global_mutex);
9887ff83669SZhong Wang 		return (rval);
989fcf3ce44SJohn Forte 	};
990fcf3ce44SJohn Forte 
991fcf3ce44SJohn Forte 	if (fcsm_handle_port_detach(pinfo, fcsm, cmd) == DDI_SUCCESS) {
992fcf3ce44SJohn Forte 		rval = FC_SUCCESS;
993fcf3ce44SJohn Forte 	}
994fcf3ce44SJohn Forte 
995fcf3ce44SJohn Forte 	mutex_enter(&fcsm_global_mutex);
996fcf3ce44SJohn Forte 	fcsm_num_detaching--;
997fcf3ce44SJohn Forte 	mutex_exit(&fcsm_global_mutex);
998fcf3ce44SJohn Forte 
999fcf3ce44SJohn Forte 	/* If it was a detach, then fcsm state structure no longer exists */
1001fcf3ce44SJohn Forte 	    "port_detach: end. cmd 0x%x rval 0x%x", cmd, rval));
1002fcf3ce44SJohn Forte 	return (rval);
1003fcf3ce44SJohn Forte }
1004fcf3ce44SJohn Forte 
1005fcf3ce44SJohn Forte 
1006fcf3ce44SJohn Forte static int
fcsm_handle_port_detach(fc_ulp_port_info_t * pinfo,fcsm_t * fcsm,fc_detach_cmd_t cmd)1007fcf3ce44SJohn Forte fcsm_handle_port_detach(fc_ulp_port_info_t *pinfo, fcsm_t *fcsm,
1008fcf3ce44SJohn Forte     fc_detach_cmd_t cmd)
1009fcf3ce44SJohn Forte {
1010fcf3ce44SJohn Forte 	uint32_t	flag;
1011fcf3ce44SJohn Forte 	int		count;
1012fcf3ce44SJohn Forte #ifdef DEBUG
1013fcf3ce44SJohn Forte 	char		pathname[MAXPATHLEN];
1014fcf3ce44SJohn Forte #endif /* DEBUG */
1015fcf3ce44SJohn Forte 
1016fcf3ce44SJohn Forte 	/*
1017fcf3ce44SJohn Forte 	 * If port is already powered down OR suspended and there is nothing
1018fcf3ce44SJohn Forte 	 * else to do then just return.
1019fcf3ce44SJohn Forte 	 * Otherwise, set the flag, so that no more new activity will be
1020fcf3ce44SJohn Forte 	 * initiated on this port.
1021fcf3ce44SJohn Forte 	 */
1022fcf3ce44SJohn Forte 	mutex_enter(&fcsm->sm_mutex);
1023fcf3ce44SJohn Forte 
1024fcf3ce44SJohn Forte 	switch (cmd) {
10257ff83669SZhong Wang 	case FC_CMD_DETACH:
10267ff83669SZhong Wang 		flag = FCSM_DETACHING;
10277ff83669SZhong Wang 		break;
1028fcf3ce44SJohn Forte 
10297ff83669SZhong Wang 	case FC_CMD_SUSPEND:
10307ff83669SZhong Wang 	case FC_CMD_POWER_DOWN:
10317ff83669SZhong Wang 		((cmd == FC_CMD_SUSPEND) ? (flag = FCSM_SUSPENDED) :
10327ff83669SZhong Wang 		    (flag = FCSM_POWER_DOWN));
10337ff83669SZhong Wang 		if (fcsm->sm_flags &
10347ff83669SZhong Wang 		    (FCSM_POWER_DOWN | FCSM_SUSPENDED)) {
10357ff83669SZhong Wang 			fcsm->sm_flags |= flag;
1036fcf3ce44SJohn Forte 			mutex_exit(&fcsm->sm_mutex);
10377ff83669SZhong Wang 			return (DDI_SUCCESS);
10387ff83669SZhong Wang 		}
10397ff83669SZhong Wang 		break;
10407ff83669SZhong Wang 
10417ff83669SZhong Wang 	default:
10427ff83669SZhong Wang 		mutex_exit(&fcsm->sm_mutex);
10437ff83669SZhong Wang 		return (DDI_FAILURE);
1044fcf3ce44SJohn Forte 	};
1045fcf3ce44SJohn Forte 
1046fcf3ce44SJohn Forte 	fcsm->sm_flags |= flag;
1047fcf3ce44SJohn Forte 
1048fcf3ce44SJohn Forte 	/*
1049fcf3ce44SJohn Forte 	 * If some commands are pending OR callback in progress, then
1050fcf3ce44SJohn Forte 	 * wait for some finite amount of time for their completion.
1051fcf3ce44SJohn Forte 	 * TODO: add more checks here to check for cmd timeout, offline
1052fcf3ce44SJohn Forte 	 * timeout and other (??) threads.
1053fcf3ce44SJohn Forte 	 */
1054fcf3ce44SJohn Forte 	count = 0;
1055fcf3ce44SJohn Forte 	while ((count++ <= 30) && (fcsm->sm_ncmds || fcsm->sm_cb_count)) {
1056fcf3ce44SJohn Forte 		mutex_exit(&fcsm->sm_mutex);
1057fcf3ce44SJohn Forte 		delay(drv_usectohz(1000000));
1058fcf3ce44SJohn Forte 		mutex_enter(&fcsm->sm_mutex);
1059fcf3ce44SJohn Forte 	}
1060fcf3ce44SJohn Forte 	if (fcsm->sm_ncmds || fcsm->sm_cb_count) {
1061fcf3ce44SJohn Forte 		fcsm->sm_flags &= ~flag;
1062fcf3ce44SJohn Forte 		mutex_exit(&fcsm->sm_mutex);
1063fcf3ce44SJohn Forte 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
1064fcf3ce44SJohn Forte 		    "port_detach: Failing suspend, port is busy");
1065fcf3ce44SJohn Forte 		return (DDI_FAILURE);
1066fcf3ce44SJohn Forte 	}
1067fcf3ce44SJohn Forte 	if (flag == FCSM_DETACHING) {
1068fcf3ce44SJohn Forte 		fcsm->sm_flags &= ~FCSM_DETACHING;
1069fcf3ce44SJohn Forte 		fcsm->sm_flags |= FCSM_DETACHED;
1070fcf3ce44SJohn Forte 	}
1071fcf3ce44SJohn Forte 
1072fcf3ce44SJohn Forte 	mutex_exit(&fcsm->sm_mutex);
1073fcf3ce44SJohn Forte 
1074fcf3ce44SJohn Forte 	FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
1075fcf3ce44SJohn Forte 	    "port_detach: cmd 0x%x pathname <%s>",
1076fcf3ce44SJohn Forte 	    cmd, ddi_pathname(pinfo->port_dip, pathname)));
1077fcf3ce44SJohn Forte 
1078fcf3ce44SJohn Forte 	if (cmd == FC_CMD_DETACH) {
1079fcf3ce44SJohn Forte 		fcsm_cleanup_port(fcsm);
1080fcf3ce44SJohn Forte 		/*
1081fcf3ce44SJohn Forte 		 * Soft state cleanup done.
1082fcf3ce44SJohn Forte 		 * Always remember that fcsm struct doesn't exist anymore.
1083fcf3ce44SJohn Forte 		 */
1084fcf3ce44SJohn Forte 	} else {
1085fcf3ce44SJohn Forte 		fcsm_suspend_port(fcsm);
1086fcf3ce44SJohn Forte 	}
1087fcf3ce44SJohn Forte 
1088fcf3ce44SJohn Forte 	return (DDI_SUCCESS);
1089fcf3ce44SJohn Forte }
1090fcf3ce44SJohn Forte 
1091fcf3ce44SJohn Forte static void
fcsm_suspend_port(fcsm_t * fcsm)1092fcf3ce44SJohn Forte fcsm_suspend_port(fcsm_t *fcsm)
1093fcf3ce44SJohn Forte {
1094fcf3ce44SJohn Forte 	mutex_enter(&fcsm->sm_mutex);
1095fcf3ce44SJohn Forte 
1096fcf3ce44SJohn Forte 	if (fcsm->sm_offline_tid != NULL) {
1097fcf3ce44SJohn Forte 		timeout_id_t	tid;
1098fcf3ce44SJohn Forte 
1099fcf3ce44SJohn Forte 		tid = fcsm->sm_offline_tid;
1100fcf3ce44SJohn Forte 		fcsm->sm_offline_tid = (timeout_id_t)NULL;
1101fcf3ce44SJohn Forte 		mutex_exit(&fcsm->sm_mutex);
1102fcf3ce44SJohn Forte 		(void) untimeout(tid);
1103fcf3ce44SJohn Forte 		mutex_enter(&fcsm->sm_mutex);
1104fcf3ce44SJohn Forte 		fcsm->sm_flags |= FCSM_RESTORE_OFFLINE_TIMEOUT;
1105fcf3ce44SJohn Forte 	}
1106fcf3ce44SJohn Forte 
1107fcf3ce44SJohn Forte 	if (fcsm->sm_retry_tid != NULL) {
1108fcf3ce44SJohn Forte 		timeout_id_t	tid;
1109fcf3ce44SJohn Forte 
1110fcf3ce44SJohn Forte 		tid = fcsm->sm_retry_tid;
1111fcf3ce44SJohn Forte 		fcsm->sm_retry_tid = (timeout_id_t)NULL;