xref: /illumos-gate/usr/src/uts/common/io/fibre-channel/ulp/fcsm.c (revision 6e375c8351497b82ffa4f33cbf61d712999b4605)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * fcsm - ULP Module for Fibre Channel SAN Management
28  */
29 
30 #include <sys/types.h>
31 #include <sys/file.h>
32 #include <sys/kmem.h>
33 #include <sys/scsi/scsi.h>
34 #include <sys/var.h>
35 #include <sys/byteorder.h>
36 
37 #include <sys/fibre-channel/fc.h>
38 #include <sys/fibre-channel/impl/fc_ulpif.h>
39 #include <sys/fibre-channel/ulp/fcsm.h>
40 
41 /* Definitions */
42 #define	FCSM_VERSION		"1.26"
43 #define	FCSM_NAME_VERSION	"SunFC FCSM v" FCSM_VERSION
44 
45 
46 
47 /* Global Variables */
48 static char		fcsm_name[] = "FCSM";
49 static void		*fcsm_state = NULL;
50 static kmutex_t		fcsm_global_mutex;
51 static uint32_t		fcsm_flag = FCSM_IDLE;
52 static dev_info_t	*fcsm_dip = NULL;
53 static fcsm_t		*fcsm_port_head = NULL;
54 static kmem_cache_t	*fcsm_job_cache = NULL;
55 static int		fcsm_num_attaching = 0;
56 static int		fcsm_num_detaching = 0;
57 static int		fcsm_detached = 0;
58 
59 static int		fcsm_max_cmd_retries = FCSM_MAX_CMD_RETRIES;
60 static int		fcsm_retry_interval = FCSM_RETRY_INTERVAL;
61 static int		fcsm_retry_ticker = FCSM_RETRY_TICKER;
62 static int		fcsm_offline_ticker = FCSM_OFFLINE_TICKER;
63 static int		fcsm_max_job_retries = FCSM_MAX_JOB_RETRIES;
64 static clock_t		fcsm_retry_ticks;
65 static clock_t		fcsm_offline_ticks;
66 
67 
68 
69 #ifdef DEBUG
70 uint32_t		fcsm_debug = (SMDL_TRACE | SMDL_IO |
71 			    SMDL_ERR | SMDL_INFO);
72 #endif
73 
74 
75 /* Character/Block entry points */
76 struct cb_ops	fcsm_cb_ops = {
77 	fcsm_open,	/* open */
78 	fcsm_close,	/* close */
79 	nodev,		/* strategy */
80 	nodev,		/* print */
81 	nodev,		/* dump */
82 	nodev,		/* read */
83 	nodev,		/* write */
84 	fcsm_ioctl,	/* ioctl */
85 	nodev,		/* devmap */
86 	nodev,		/* mmap */
87 	nodev,		/* segmap */
88 	nochpoll,	/* poll */
89 	ddi_prop_op,
90 	NULL,		/* streams info */
91 	D_NEW | D_MP,
92 	CB_REV,
93 	nodev,		/* aread */
94 	nodev		/* awrite */
95 };
96 
97 struct dev_ops fcsm_ops = {
98 	DEVO_REV,
99 	0,		/* refcnt */
100 	fcsm_getinfo,	/* get info */
101 	nulldev,	/* identify (obsolete) */
102 	nulldev,	/* probe (not required for self-identifying devices) */
103 	fcsm_attach,	/* attach */
104 	fcsm_detach,	/* detach */
105 	nodev,		/* reset */
106 	&fcsm_cb_ops,	/* char/block entry points structure for leaf drivers */
107 	NULL,		/* bus operations for nexus driver */
108 	NULL		/* power management */
109 };
110 
111 
112 struct modldrv modldrv = {
113 	&mod_driverops,
114 	FCSM_NAME_VERSION,
115 	&fcsm_ops
116 };
117 
118 struct modlinkage modlinkage = {
119 	MODREV_1,
120 	&modldrv,
121 	NULL
122 };
123 
124 static fc_ulp_modinfo_t fcsm_modinfo = {
125 	&fcsm_modinfo,		/* ulp_handle */
126 	FCTL_ULP_MODREV_4,	/* ulp_rev */
127 	FC_TYPE_FC_SERVICES,	/* ulp_type */
128 	fcsm_name,		/* ulp_name */
129 	0,			/* ulp_statec_mask: get all statec callbacks */
130 	fcsm_port_attach,	/* ulp_port_attach */
131 	fcsm_port_detach,	/* ulp_port_detach */
132 	fcsm_port_ioctl,	/* ulp_port_ioctl */
133 	fcsm_els_cb,		/* ulp_els_callback */
134 	fcsm_data_cb,		/* ulp_data_callback */
135 	fcsm_statec_cb		/* ulp_statec_callback */
136 };
137 
138 struct fcsm_xlat_pkt_state {
139 	uchar_t	xlat_state;
140 	int	xlat_rval;
141 } fcsm_xlat_pkt_state [] = {
142 	{ FC_PKT_SUCCESS,		FC_SUCCESS },
143 	{ FC_PKT_REMOTE_STOP,		FC_FAILURE },
144 	{ FC_PKT_LOCAL_RJT,		FC_TRANSPORT_ERROR },
145 	{ FC_PKT_NPORT_RJT,		FC_PREJECT },
146 	{ FC_PKT_FABRIC_RJT,		FC_FREJECT },
147 	{ FC_PKT_LOCAL_BSY,		FC_TRAN_BUSY },
148 	{ FC_PKT_TRAN_BSY,		FC_TRAN_BUSY },
149 	{ FC_PKT_NPORT_BSY,		FC_PBUSY },
150 	{ FC_PKT_FABRIC_BSY,		FC_FBUSY },
151 	{ FC_PKT_LS_RJT,		FC_PREJECT },
152 	{ FC_PKT_BA_RJT,		FC_PREJECT },
153 	{ FC_PKT_TIMEOUT,		FC_FAILURE },
154 	{ FC_PKT_FS_RJT,		FC_FAILURE },
155 	{ FC_PKT_TRAN_ERROR,		FC_TRANSPORT_ERROR },
156 	{ FC_PKT_FAILURE,		FC_FAILURE },
157 	{ FC_PKT_PORT_OFFLINE,		FC_OFFLINE },
158 	{ FC_PKT_ELS_IN_PROGRESS,	FC_FAILURE }
159 };
160 
161 struct fcsm_xlat_port_state {
162 	uint32_t	xlat_pstate;
163 	caddr_t		xlat_state_str;
164 } fcsm_xlat_port_state [] = {
165 	{ FC_STATE_OFFLINE,		"OFFLINE" },
166 	{ FC_STATE_ONLINE,		"ONLINE" },
167 	{ FC_STATE_LOOP,		"LOOP" },
168 	{ FC_STATE_NAMESERVICE,		"NAMESERVICE" },
169 	{ FC_STATE_RESET,		"RESET" },
170 	{ FC_STATE_RESET_REQUESTED,	"RESET_REQUESTED" },
171 	{ FC_STATE_LIP,			"LIP" },
172 	{ FC_STATE_LIP_LBIT_SET,	"LIP_LBIT_SET" },
173 	{ FC_STATE_DEVICE_CHANGE,	"DEVICE_CHANGE" },
174 	{ FC_STATE_TARGET_PORT_RESET,	"TARGET_PORT_RESET" }
175 };
176 
177 struct fcsm_xlat_topology {
178 	uint32_t	xlat_top;
179 	caddr_t		xlat_top_str;
180 } fcsm_xlat_topology [] = {
181 	{ FC_TOP_UNKNOWN,	"UNKNOWN" },
182 	{ FC_TOP_PRIVATE_LOOP,	"Private Loop" },
183 	{ FC_TOP_PUBLIC_LOOP,	"Public Loop" },
184 	{ FC_TOP_FABRIC,	"Fabric" },
185 	{ FC_TOP_PT_PT,		"Point-to-Point" },
186 	{ FC_TOP_NO_NS,		"NO_NS" }
187 };
188 
189 struct fcsm_xlat_dev_type {
190 	uint32_t	xlat_type;
191 	caddr_t		xlat_str;
192 } fcsm_xlat_dev_type [] = {
193 	{ PORT_DEVICE_NOCHANGE,		"No Change" },
194 	{ PORT_DEVICE_NEW,		"New" },
195 	{ PORT_DEVICE_OLD,		"Old" },
196 	{ PORT_DEVICE_CHANGED,		"Changed" },
197 	{ PORT_DEVICE_DELETE,		"Delete" },
198 	{ PORT_DEVICE_USER_LOGIN,	"User Login" },
199 	{ PORT_DEVICE_USER_LOGOUT,	"User Logout" },
200 	{ PORT_DEVICE_USER_CREATE,	"User Create" },
201 	{ PORT_DEVICE_USER_DELETE,	"User Delete" }
202 };
203 
204 int
205 _init(void)
206 {
207 	int		rval;
208 
209 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_init"));
210 
211 	fcsm_retry_ticks = drv_usectohz(fcsm_retry_ticker * 1000 * 1000);
212 	fcsm_offline_ticks = drv_usectohz(fcsm_offline_ticker * 1000 * 1000);
213 
214 	if (rval = ddi_soft_state_init(&fcsm_state, sizeof (fcsm_t),
215 	    FCSM_INIT_INSTANCES)) {
216 		fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
217 		    "_init: ddi_soft_state_init failed");
218 		return (ENOMEM);
219 	}
220 
221 	mutex_init(&fcsm_global_mutex, NULL, MUTEX_DRIVER, NULL);
222 
223 	fcsm_job_cache = kmem_cache_create("fcsm_job_cache",
224 	    sizeof (fcsm_job_t), 8, fcsm_job_cache_constructor,
225 	    fcsm_job_cache_destructor, NULL, NULL, NULL, 0);
226 
227 	if (fcsm_job_cache == NULL) {
228 		mutex_destroy(&fcsm_global_mutex);
229 		ddi_soft_state_fini(&fcsm_state);
230 		return (ENOMEM);
231 	}
232 
233 	/*
234 	 * Now call fc_ulp_add to add this ULP in the transport layer
235 	 * database. This will cause 'ulp_port_attach' callback function
236 	 * to be called.
237 	 */
238 	rval = fc_ulp_add(&fcsm_modinfo);
239 	if (rval != 0) {
240 		switch (rval) {
241 		case FC_ULP_SAMEMODULE:
242 			fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
243 			    "_init: FC SAN Management module is already "
244 			    "registered with transport layer");
245 			rval = EEXIST;
246 			break;
247 
248 		case FC_ULP_SAMETYPE:
249 			fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
250 			    "_init: Another module with same type 0x%x is "
251 			    "already registered with transport layer",
252 			    fcsm_modinfo.ulp_type);
253 			rval = EEXIST;
254 			break;
255 
256 		case FC_BADULP:
257 			fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
258 			    "_init: Please upgrade this module. Current "
259 			    "version 0x%x is not the most recent version",
260 			    fcsm_modinfo.ulp_rev);
261 			rval = EIO;
262 			break;
263 		default:
264 			fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
265 			    "_init: fc_ulp_add failed with status 0x%x", rval);
266 			rval = EIO;
267 			break;
268 		}
269 		kmem_cache_destroy(fcsm_job_cache);
270 		mutex_destroy(&fcsm_global_mutex);
271 		ddi_soft_state_fini(&fcsm_state);
272 		return (rval);
273 	}
274 
275 	if ((rval = mod_install(&modlinkage)) != 0) {
276 		FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
277 		    "_init: mod_install failed with status 0x%x", rval));
278 		(void) fc_ulp_remove(&fcsm_modinfo);
279 		kmem_cache_destroy(fcsm_job_cache);
280 		mutex_destroy(&fcsm_global_mutex);
281 		ddi_soft_state_fini(&fcsm_state);
282 		return (rval);
283 	}
284 
285 	return (rval);
286 }
287 
288 int
289 _fini(void)
290 {
291 	int	rval;
292 #ifdef  DEBUG
293 	int	status;
294 #endif /* DEBUG */
295 
296 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_fini"));
297 
298 	/*
299 	 * don't start cleaning up until we know that the module remove
300 	 * has worked  -- if this works, then we know that each instance
301 	 * has successfully been DDI_DETACHed
302 	 */
303 	if ((rval = mod_remove(&modlinkage)) != 0) {
304 		return (rval);
305 	}
306 
307 #ifdef DEBUG
308 	status = fc_ulp_remove(&fcsm_modinfo);
309 	if (status != 0) {
310 		FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
311 		    "_fini: fc_ulp_remove failed with status 0x%x", status));
312 	}
313 #else
314 	(void) fc_ulp_remove(&fcsm_modinfo);
315 #endif /* DEBUG */
316 
317 	fcsm_detached = 0;
318 
319 	/*
320 	 * It is possible to modunload fcsm manually, which will cause
321 	 * a bypass of all the port_detach functionality.  We may need
322 	 * to force that code path to be executed to properly clean up
323 	 * in that case.
324 	 */
325 	fcsm_force_port_detach_all();
326 
327 	kmem_cache_destroy(fcsm_job_cache);
328 	mutex_destroy(&fcsm_global_mutex);
329 	ddi_soft_state_fini(&fcsm_state);
330 
331 	return (rval);
332 }
333 
334 
335 int
336 _info(struct modinfo *modinfop)
337 {
338 	return (mod_info(&modlinkage, modinfop));
339 }
340 
341 /* ARGSUSED */
342 static int
343 fcsm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
344 {
345 	int rval = DDI_FAILURE;
346 
347 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
348 	    "attach: cmd 0x%x", cmd));
349 
350 	switch (cmd) {
351 	case DDI_ATTACH:
352 		mutex_enter(&fcsm_global_mutex);
353 		if (fcsm_dip != NULL) {
354 			mutex_exit(&fcsm_global_mutex);
355 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
356 			    "attach: duplicate attach of fcsm!!"));
357 			break;
358 		}
359 
360 		fcsm_dip = dip;
361 
362 		/*
363 		 * The detach routine cleans up all the port instances
364 		 * i.e. it detaches all ports.
365 		 * If _fini never got called after detach, then
366 		 * perform an fc_ulp_remove() followed by fc_ulp_add()
367 		 * to ensure that port_attach callbacks are called
368 		 * again.
369 		 */
370 		if (fcsm_detached) {
371 			int status;
372 
373 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
374 			    "attach: rebinding to transport driver"));
375 
376 			mutex_exit(&fcsm_global_mutex);
377 
378 			(void) fc_ulp_remove(&fcsm_modinfo);
379 
380 			/*
381 			 * Reset the detached flag, so that ports can attach
382 			 */
383 			mutex_enter(&fcsm_global_mutex);
384 			fcsm_detached = 0;
385 			mutex_exit(&fcsm_global_mutex);
386 
387 			status = fc_ulp_add(&fcsm_modinfo);
388 
389 			if (status != 0) {
390 				/*
391 				 * ULP add failed. So set the
392 				 * detached flag again
393 				 */
394 				mutex_enter(&fcsm_global_mutex);
395 				fcsm_detached = 1;
396 				mutex_exit(&fcsm_global_mutex);
397 
398 				switch (status) {
399 				case FC_ULP_SAMEMODULE:
400 					fcsm_display(CE_WARN, SM_LOG, NULL,
401 					    NULL, "attach: FC SAN Management "
402 					    "module is already "
403 					    "registered with transport layer");
404 					break;
405 
406 				case FC_ULP_SAMETYPE:
407 					fcsm_display(CE_WARN, SM_LOG, NULL,
408 					    NULL, "attach: Another module with "
409 					    "same type 0x%x is already "
410 					    "registered with transport layer",
411 					    fcsm_modinfo.ulp_type);
412 					break;
413 
414 				case FC_BADULP:
415 					fcsm_display(CE_WARN, SM_LOG, NULL,
416 					    NULL, "attach: Please upgrade this "
417 					    "module. Current version 0x%x is "
418 					    "not the most recent version",
419 					    fcsm_modinfo.ulp_rev);
420 					break;
421 				default:
422 					fcsm_display(CE_WARN, SM_LOG, NULL,
423 					    NULL, "attach: fc_ulp_add failed "
424 					    "with status 0x%x", status);
425 					break;
426 				}
427 
428 				/* Return failure */
429 				break;
430 			}
431 
432 			mutex_enter(&fcsm_global_mutex);
433 		}
434 
435 		/* Create a minor node */
436 		if (ddi_create_minor_node(fcsm_dip, "fcsm", S_IFCHR,
437 		    NULL, DDI_PSEUDO, 0) == DDI_SUCCESS) {
438 			/* Announce presence of the device */
439 			mutex_exit(&fcsm_global_mutex);
440 			ddi_report_dev(dip);
441 			rval = DDI_SUCCESS;
442 		} else {
443 			fcsm_dip = NULL;
444 			mutex_exit(&fcsm_global_mutex);
445 			fcsm_display(CE_WARN, SM_LOG_AND_CONSOLE,
446 			    NULL, NULL, "attach: create minor node failed");
447 		}
448 		break;
449 
450 	case DDI_RESUME:
451 		rval = DDI_SUCCESS;
452 		break;
453 
454 	default:
455 		FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
456 		    "attach: unknown cmd 0x%x dip 0x%p", cmd, dip));
457 		break;
458 	}
459 
460 	return (rval);
461 }
462 
463 /* ARGSUSED */
464 static int
465 fcsm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
466 {
467 	int	instance;
468 	int	rval = DDI_SUCCESS;
469 
470 	instance = getminor((dev_t)arg);
471 
472 	switch (cmd) {
473 	case DDI_INFO_DEVT2INSTANCE:
474 		*result = (void *)(long)instance; /* minor number is instance */
475 		break;
476 
477 	case DDI_INFO_DEVT2DEVINFO:
478 		mutex_enter(&fcsm_global_mutex);
479 		*result = (void *)fcsm_dip;
480 		mutex_exit(&fcsm_global_mutex);
481 		break;
482 
483 	default:
484 		rval = DDI_FAILURE;
485 		break;
486 	}
487 
488 	return (rval);
489 }
490 
491 
492 /* ARGSUSED */
493 static int
494 fcsm_port_attach(opaque_t ulph, fc_ulp_port_info_t *pinfo,
495     fc_attach_cmd_t cmd, uint32_t s_id)
496 {
497 	int	instance;
498 	int	rval = FC_FAILURE;
499 
500 	instance = ddi_get_instance(pinfo->port_dip);
501 
502 	/*
503 	 * Set the attaching flag, so that fcsm_detach will fail, if
504 	 * port attach is in progress.
505 	 */
506 	mutex_enter(&fcsm_global_mutex);
507 	if (fcsm_detached) {
508 		mutex_exit(&fcsm_global_mutex);
509 
510 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
511 		    "port_attach: end. detach in progress. failing attach "
512 		    "instance 0x%x", instance));
513 		return (((cmd == FC_CMD_POWER_UP) || (cmd == FC_CMD_RESUME)) ?
514 		    FC_FAILURE_SILENT : FC_FAILURE);
515 	}
516 
517 	fcsm_num_attaching++;
518 	mutex_exit(&fcsm_global_mutex);
519 
520 	switch (cmd) {
521 	case FC_CMD_ATTACH:
522 		if (fcsm_handle_port_attach(pinfo, s_id, instance)
523 		    != DDI_SUCCESS) {
524 			ASSERT(ddi_get_soft_state(fcsm_state,
525 			    instance) == NULL);
526 			break;
527 		}
528 		rval = FC_SUCCESS;
529 		break;
530 
531 	case FC_CMD_RESUME:
532 	case FC_CMD_POWER_UP: {
533 		fcsm_t	*fcsm;
534 		char fcsm_pathname[MAXPATHLEN];
535 
536 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
537 		    "port_attach: cmd 0x%x instance 0x%x", cmd, instance));
538 
539 		/* Get the soft state structure */
540 		if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
541 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
542 			    "port_attach: instance 0x%x, cmd 0x%x "
543 			    "get softstate failed", instance, cmd));
544 			break;
545 		}
546 
547 		ASSERT(fcsm->sm_instance == instance);
548 
549 		/* If this instance is not attached, then return failure */
550 		mutex_enter(&fcsm->sm_mutex);
551 		if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
552 			mutex_exit(&fcsm->sm_mutex);
553 			fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
554 			    "port_detach: port is not attached");
555 			break;
556 		}
557 		mutex_exit(&fcsm->sm_mutex);
558 
559 		if (fcsm_handle_port_resume(ulph, pinfo, cmd, s_id, fcsm) !=
560 		    DDI_SUCCESS) {
561 			break;
562 		}
563 
564 		(void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
565 		fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
566 		    "attached to path %s", fcsm_pathname);
567 		rval = FC_SUCCESS;
568 		break;
569 	}
570 
571 	default:
572 		FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
573 		    "port_attach: unknown cmd 0x%x for port 0x%x",
574 		    cmd, instance));
575 		break;
576 	}
577 
578 	mutex_enter(&fcsm_global_mutex);
579 	fcsm_num_attaching--;
580 	mutex_exit(&fcsm_global_mutex);
581 	return (rval);
582 }
583 
584 
585 static int
586 fcsm_handle_port_attach(fc_ulp_port_info_t *pinfo, uint32_t s_id, int instance)
587 {
588 	fcsm_t		*fcsm;
589 	kthread_t	*thread;
590 	char		name[32];
591 	char fcsm_pathname[MAXPATHLEN];
592 
593 	/* Allocate a soft state structure for the port */
594 	if (ddi_soft_state_zalloc(fcsm_state, instance) != DDI_SUCCESS) {
595 		fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
596 		    "port_attach: instance 0x%x, soft state alloc failed",
597 		    instance);
598 		return (DDI_FAILURE);
599 	}
600 
601 	if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
602 		fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
603 		    "port_attach: instance 0x%x, get soft state failed",
604 		    instance);
605 		ddi_soft_state_free(fcsm_state, instance);
606 		return (DDI_FAILURE);
607 	}
608 
609 
610 	/* Initialize the mutex */
611 	mutex_init(&fcsm->sm_mutex, NULL, MUTEX_DRIVER, NULL);
612 	cv_init(&fcsm->sm_job_cv, NULL, CV_DRIVER, NULL);
613 
614 	mutex_enter(&fcsm->sm_mutex);
615 	fcsm->sm_flags		|= FCSM_ATTACHING;
616 	fcsm->sm_sid		= s_id;
617 	fcsm->sm_instance	= instance;
618 	fcsm->sm_port_state	= pinfo->port_state;
619 
620 	/*
621 	 * Make a copy of the port_information structure, since fctl
622 	 * uses a temporary structure.
623 	 */
624 	fcsm->sm_port_info	= *pinfo;	/* Structure copy !!! */
625 	mutex_exit(&fcsm->sm_mutex);
626 
627 
628 	(void) sprintf(name, "fcsm%d_cmd_cache", fcsm->sm_instance);
629 	fcsm->sm_cmd_cache = kmem_cache_create(name,
630 	    sizeof (fcsm_cmd_t) + pinfo->port_fca_pkt_size, 8,
631 	    fcsm_cmd_cache_constructor, fcsm_cmd_cache_destructor,
632 	    NULL, (void *)fcsm, NULL, 0);
633 	if (fcsm->sm_cmd_cache == NULL) {
634 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
635 		    "port_attach: pkt cache create failed");
636 		cv_destroy(&fcsm->sm_job_cv);
637 		mutex_destroy(&fcsm->sm_mutex);
638 		ddi_soft_state_free(fcsm_state, instance);
639 		return (DDI_FAILURE);
640 	}
641 
642 	thread = thread_create((caddr_t)NULL, 0, fcsm_job_thread,
643 	    (caddr_t)fcsm, 0, &p0, TS_RUN, v.v_maxsyspri-2);
644 	if (thread == NULL) {
645 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
646 		    "port_attach: job thread create failed");
647 		kmem_cache_destroy(fcsm->sm_cmd_cache);
648 		cv_destroy(&fcsm->sm_job_cv);
649 		mutex_destroy(&fcsm->sm_mutex);
650 		ddi_soft_state_free(fcsm_state, instance);
651 		return (DDI_FAILURE);
652 	}
653 
654 	fcsm->sm_thread = thread;
655 
656 	/* Add this structure to fcsm global linked list */
657 	mutex_enter(&fcsm_global_mutex);
658 	if (fcsm_port_head == NULL) {
659 		fcsm_port_head = fcsm;
660 	} else {
661 		fcsm->sm_next = fcsm_port_head;
662 		fcsm_port_head = fcsm;
663 	}
664 	mutex_exit(&fcsm_global_mutex);
665 
666 	mutex_enter(&fcsm->sm_mutex);
667 	fcsm->sm_flags &= ~FCSM_ATTACHING;
668 	fcsm->sm_flags |= FCSM_ATTACHED;
669 	fcsm->sm_port_top = pinfo->port_flags;
670 	fcsm->sm_port_state = pinfo->port_state;
671 	mutex_exit(&fcsm->sm_mutex);
672 
673 	(void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
674 	fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
675 	    "attached to path %s", fcsm_pathname);
676 
677 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
678 	    "port_attach: state <%s>(0x%x) topology <%s>(0x%x)",
679 	    fcsm_port_state_to_str(FC_PORT_STATE_MASK(pinfo->port_state)),
680 	    pinfo->port_state,
681 	    fcsm_topology_to_str(pinfo->port_flags), pinfo->port_flags));
682 
683 	return (DDI_SUCCESS);
684 }
685 
686 static int
687 fcsm_handle_port_resume(opaque_t ulph, fc_ulp_port_info_t *pinfo,
688     fc_attach_cmd_t cmd, uint32_t s_id, fcsm_t *fcsm)
689 {
690 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
691 	    "port_resume: cmd 0x%x", cmd));
692 
693 	mutex_enter(&fcsm->sm_mutex);
694 
695 	switch (cmd) {
696 	case FC_CMD_RESUME:
697 		ASSERT(!(fcsm->sm_flags & FCSM_POWER_DOWN));
698 		fcsm->sm_flags &= ~FCSM_SUSPENDED;
699 		break;
700 
701 	case FC_CMD_POWER_UP:
702 		/* If port is suspended, then no need to resume */
703 		fcsm->sm_flags &= ~FCSM_POWER_DOWN;
704 		if (fcsm->sm_flags & FCSM_SUSPENDED) {
705 			mutex_exit(&fcsm->sm_mutex);
706 			return (DDI_SUCCESS);
707 		}
708 		break;
709 	default:
710 		mutex_exit(&fcsm->sm_mutex);
711 		return (DDI_FAILURE);
712 	}
713 
714 	fcsm->sm_sid = s_id;
715 
716 	/*
717 	 * Make a copy of the new port_information structure
718 	 */
719 	fcsm->sm_port_info	= *pinfo;	/* Structure copy !!! */
720 	mutex_exit(&fcsm->sm_mutex);
721 
722 	fcsm_resume_port(fcsm);
723 
724 	/*
725 	 * Invoke state change processing.
726 	 * This will ensure that
727 	 *    - offline timer is started if new port state changed to offline.
728 	 *    - MGMT_SERVER_LOGIN flag is reset.
729 	 *    - Port topology is updated.
730 	 */
731 	fcsm_statec_cb(ulph, (opaque_t)pinfo->port_handle, pinfo->port_state,
732 	    pinfo->port_flags, NULL, 0, s_id);
733 
734 	return (DDI_SUCCESS);
735 }
736 
737 
738 /* ARGSUSED */
739 static int
740 fcsm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
741 {
742 	int	rval = DDI_SUCCESS;
743 
744 	switch (cmd) {
745 	case DDI_DETACH: {
746 		fcsm_t	*fcsm;
747 
748 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
749 		    "detach: start. cmd <DETACH>", cmd));
750 
751 		mutex_enter(&fcsm_global_mutex);
752 
753 		/*
754 		 * If port attach/detach in progress, then wait for 5 seconds
755 		 * for them to complete.
756 		 */
757 		if (fcsm_num_attaching || fcsm_num_detaching) {
758 			int count;
759 
760 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
761 			    "detach: wait for port attach/detach to complete"));
762 
763 			count = 0;
764 			while ((count++ <= 30) &&
765 			    (fcsm_num_attaching || fcsm_num_detaching)) {
766 				mutex_exit(&fcsm_global_mutex);
767 				delay(drv_usectohz(1000000));
768 				mutex_enter(&fcsm_global_mutex);
769 			}
770 
771 			/* Port attach/detach still in prog, so fail detach */
772 			if (fcsm_num_attaching || fcsm_num_detaching) {
773 				mutex_exit(&fcsm_global_mutex);
774 				FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL,
775 				    NULL, "detach: Failing detach. port "
776 				    "attach/detach in progress"));
777 				rval = DDI_FAILURE;
778 				break;
779 			}
780 		}
781 
782 		if (fcsm_port_head == NULL) {
783 			/* Not much do, Succeed to detach. */
784 			ddi_remove_minor_node(fcsm_dip, NULL);
785 			fcsm_dip = NULL;
786 			fcsm_detached = 0;
787 			mutex_exit(&fcsm_global_mutex);
788 			break;
789 		}
790 
791 		/*
792 		 * Check to see, if any ports are active.
793 		 * If not, then set the DETACHING flag to indicate
794 		 * that they are being detached.
795 		 */
796 		fcsm = fcsm_port_head;
797 		while (fcsm != NULL) {
798 
799 			mutex_enter(&fcsm->sm_mutex);
800 			if (!(fcsm->sm_flags & FCSM_ATTACHED) ||
801 			    fcsm->sm_ncmds || fcsm->sm_cb_count) {
802 				/* port is busy. We can't detach */
803 				mutex_exit(&fcsm->sm_mutex);
804 				break;
805 			}
806 
807 			fcsm->sm_flags |= FCSM_DETACHING;
808 			mutex_exit(&fcsm->sm_mutex);
809 
810 			fcsm = fcsm->sm_next;
811 		}
812 
813 		/*
814 		 * If all ports could not be marked for detaching,
815 		 * then clear the flags and fail the detach.
816 		 * Also if a port attach is currently in progress
817 		 * then fail the detach.
818 		 */
819 		if (fcsm != NULL || fcsm_num_attaching || fcsm_num_detaching) {
820 			/*
821 			 * Some ports were busy, so can't detach.
822 			 * Clear the DETACHING flag and return failure
823 			 */
824 			fcsm = fcsm_port_head;
825 			while (fcsm != NULL) {
826 				mutex_enter(&fcsm->sm_mutex);
827 				if (fcsm->sm_flags & FCSM_DETACHING) {
828 					fcsm->sm_flags &= ~FCSM_DETACHING;
829 				}
830 				mutex_exit(&fcsm->sm_mutex);
831 
832 				fcsm = fcsm->sm_next;
833 			}
834 			mutex_exit(&fcsm_global_mutex);
835 			return (DDI_FAILURE);
836 		} else {
837 			fcsm_detached = 1;
838 			/*
839 			 * Mark all the detaching ports as detached, as we
840 			 * will be detaching them
841 			 */
842 			fcsm = fcsm_port_head;
843 			while (fcsm != NULL) {
844 				mutex_enter(&fcsm->sm_mutex);
845 				fcsm->sm_flags &= ~FCSM_DETACHING;
846 				fcsm->sm_flags |= FCSM_DETACHED;
847 				mutex_exit(&fcsm->sm_mutex);
848 
849 				fcsm = fcsm->sm_next;
850 			}
851 		}
852 		mutex_exit(&fcsm_global_mutex);
853 
854 
855 		/*
856 		 * Go ahead and detach the ports
857 		 */
858 		mutex_enter(&fcsm_global_mutex);
859 		while (fcsm_port_head != NULL) {
860 			fcsm = fcsm_port_head;
861 			mutex_exit(&fcsm_global_mutex);
862 
863 			/*
864 			 * Call fcsm_cleanup_port(). This cleansup and
865 			 * removes the fcsm structure from global linked list
866 			 */
867 			fcsm_cleanup_port(fcsm);
868 
869 			/*
870 			 * Soft state cleanup done.
871 			 * Remember that fcsm struct doesn't exist anymore.
872 			 */
873 
874 			mutex_enter(&fcsm_global_mutex);
875 		}
876 
877 		ddi_remove_minor_node(fcsm_dip, NULL);
878 		fcsm_dip = NULL;
879 		mutex_exit(&fcsm_global_mutex);
880 		break;
881 	}
882 
883 	case DDI_SUSPEND:
884 		rval = DDI_SUCCESS;
885 		break;
886 
887 	default:
888 		FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
889 		    "detach: unknown cmd 0x%x", cmd));
890 		rval = DDI_FAILURE;
891 		break;
892 	}
893 
894 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
895 	    "detach: end. cmd 0x%x, rval 0x%x", cmd, rval));
896 
897 	return (rval);
898 }
899 
900 
901 /* ARGSUSED */
902 static void
903 fcsm_force_port_detach_all(void)
904 {
905 	fcsm_t	*fcsm;
906 
907 	fcsm = fcsm_port_head;
908 
909 	while (fcsm) {
910 		fcsm_cleanup_port(fcsm);
911 		/*
912 		 * fcsm_cleanup_port will remove the current fcsm structure
913 		 * from the list, which will cause fcsm_port_head to point
914 		 * to what would have been the next structure on the list.
915 		 */
916 		fcsm = fcsm_port_head;
917 	}
918 }
919 
920 
921 /* ARGSUSED */
922 static int
923 fcsm_port_detach(opaque_t ulph, fc_ulp_port_info_t *pinfo, fc_detach_cmd_t cmd)
924 {
925 	int	instance;
926 	int	rval = FC_FAILURE;
927 	fcsm_t	*fcsm;
928 
929 	instance = ddi_get_instance(pinfo->port_dip);
930 
931 	mutex_enter(&fcsm_global_mutex);
932 	if (fcsm_detached) {
933 		mutex_exit(&fcsm_global_mutex);
934 
935 		FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
936 		    "port_detach: end. instance 0x%x, fcsm is detached",
937 		    instance));
938 		return (FC_SUCCESS);
939 	}
940 	fcsm_num_detaching++;	/* Set the flag */
941 	mutex_exit(&fcsm_global_mutex);
942 
943 	/* Get the soft state structure */
944 	if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
945 		FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
946 		    "port_detach: instance 0x%x, cmd 0x%x get softstate failed",
947 		    instance, cmd));
948 		mutex_enter(&fcsm_global_mutex);
949 		fcsm_num_detaching--;
950 		mutex_exit(&fcsm_global_mutex);
951 		return (rval);
952 	}
953 
954 	ASSERT(fcsm->sm_instance == instance);
955 
956 	/* If this instance is not attached, then fail the detach */
957 	mutex_enter(&fcsm->sm_mutex);
958 	if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
959 		mutex_exit(&fcsm->sm_mutex);
960 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
961 		    "port_detach: port is not attached");
962 		mutex_enter(&fcsm_global_mutex);
963 		fcsm_num_detaching--;
964 		mutex_exit(&fcsm_global_mutex);
965 		return (rval);
966 	}
967 	mutex_exit(&fcsm->sm_mutex);
968 
969 	/*
970 	 * If fcsm has been detached, then all instance has already been
971 	 * detached or are being detached. So succeed this detach.
972 	 */
973 
974 	switch (cmd) {
975 		case FC_CMD_DETACH:
976 		case FC_CMD_SUSPEND:
977 		case FC_CMD_POWER_DOWN:
978 			break;
979 
980 		default:
981 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
982 			    "port_detach: port unknown cmd 0x%x", cmd));
983 			mutex_enter(&fcsm_global_mutex);
984 			fcsm_num_detaching--;
985 			mutex_exit(&fcsm_global_mutex);
986 			return (rval);
987 	};
988 
989 	if (fcsm_handle_port_detach(pinfo, fcsm, cmd) == DDI_SUCCESS) {
990 		rval = FC_SUCCESS;
991 	}
992 
993 	mutex_enter(&fcsm_global_mutex);
994 	fcsm_num_detaching--;
995 	mutex_exit(&fcsm_global_mutex);
996 
997 	/* If it was a detach, then fcsm state structure no longer exists */
998 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
999 	    "port_detach: end. cmd 0x%x rval 0x%x", cmd, rval));
1000 	return (rval);
1001 }
1002 
1003 
1004 static int
1005 fcsm_handle_port_detach(fc_ulp_port_info_t *pinfo, fcsm_t *fcsm,
1006     fc_detach_cmd_t cmd)
1007 {
1008 	uint32_t	flag;
1009 	int		count;
1010 #ifdef DEBUG
1011 	char		pathname[MAXPATHLEN];
1012 #endif /* DEBUG */
1013 
1014 	/*
1015 	 * If port is already powered down OR suspended and there is nothing
1016 	 * else to do then just return.
1017 	 * Otherwise, set the flag, so that no more new activity will be
1018 	 * initiated on this port.
1019 	 */
1020 	mutex_enter(&fcsm->sm_mutex);
1021 
1022 	switch (cmd) {
1023 		case FC_CMD_DETACH:
1024 			flag = FCSM_DETACHING;
1025 			break;
1026 
1027 		case FC_CMD_SUSPEND:
1028 		case FC_CMD_POWER_DOWN:
1029 			(cmd == FC_CMD_SUSPEND) ? (flag = FCSM_SUSPENDED) : \
1030 			    (flag = FCSM_POWER_DOWN);
1031 			if (fcsm->sm_flags &
1032 			    (FCSM_POWER_DOWN | FCSM_SUSPENDED)) {
1033 				fcsm->sm_flags |= flag;
1034 				mutex_exit(&fcsm->sm_mutex);
1035 				return (DDI_SUCCESS);
1036 			}
1037 			break;
1038 
1039 		default:
1040 			mutex_exit(&fcsm->sm_mutex);
1041 			return (DDI_FAILURE);
1042 	};
1043 
1044 	fcsm->sm_flags |= flag;
1045 
1046 	/*
1047 	 * If some commands are pending OR callback in progress, then
1048 	 * wait for some finite amount of time for their completion.
1049 	 * TODO: add more checks here to check for cmd timeout, offline
1050 	 * timeout and other (??) threads.
1051 	 */
1052 	count = 0;
1053 	while ((count++ <= 30) && (fcsm->sm_ncmds || fcsm->sm_cb_count)) {
1054 		mutex_exit(&fcsm->sm_mutex);
1055 		delay(drv_usectohz(1000000));
1056 		mutex_enter(&fcsm->sm_mutex);
1057 	}
1058 	if (fcsm->sm_ncmds || fcsm->sm_cb_count) {
1059 		fcsm->sm_flags &= ~flag;
1060 		mutex_exit(&fcsm->sm_mutex);
1061 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
1062 		    "port_detach: Failing suspend, port is busy");
1063 		return (DDI_FAILURE);
1064 	}
1065 	if (flag == FCSM_DETACHING) {
1066 		fcsm->sm_flags &= ~FCSM_DETACHING;
1067 		fcsm->sm_flags |= FCSM_DETACHED;
1068 	}
1069 
1070 	mutex_exit(&fcsm->sm_mutex);
1071 
1072 	FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
1073 	    "port_detach: cmd 0x%x pathname <%s>",
1074 	    cmd, ddi_pathname(pinfo->port_dip, pathname)));
1075 
1076 	if (cmd == FC_CMD_DETACH) {
1077 		fcsm_cleanup_port(fcsm);
1078 		/*
1079 		 * Soft state cleanup done.
1080 		 * Always remember that fcsm struct doesn't exist anymore.
1081 		 */
1082 	} else {
1083 		fcsm_suspend_port(fcsm);
1084 	}
1085 
1086 	return (DDI_SUCCESS);
1087 }
1088 
1089 static void
1090 fcsm_suspend_port(fcsm_t *fcsm)
1091 {
1092 	mutex_enter(&fcsm->sm_mutex);
1093 
1094 	if (fcsm->sm_offline_tid != NULL) {
1095 		timeout_id_t	tid;
1096 
1097 		tid = fcsm->sm_offline_tid;
1098 		fcsm->sm_offline_tid = (timeout_id_t)NULL;
1099 		mutex_exit(&fcsm->sm_mutex);
1100 		(void) untimeout(tid);
1101 		mutex_enter(&fcsm->sm_mutex);
1102 		fcsm->sm_flags |= FCSM_RESTORE_OFFLINE_TIMEOUT;
1103 	}
1104 
1105 	if (fcsm->sm_retry_tid != NULL) {
1106 		timeout_id_t	tid;
1107 
1108 		tid = fcsm->sm_retry_tid;
1109 		fcsm->sm_retry_tid = (timeout_id_t)NULL;
1110 		mutex_exit(&fcsm->sm_mutex);
1111 		(void) untimeout(tid);
1112 		mutex_enter(&fcsm->sm_mutex);
1113 		fcsm->sm_flags |= FCSM_RESTORE_RETRY_TIMEOUT;
1114 	}
1115 
1116 	mutex_exit(&fcsm->sm_mutex);
1117 }
1118 
1119 static void
1120 fcsm_resume_port(fcsm_t *fcsm)
1121 {
1122 	mutex_enter(&fcsm->sm_mutex);
1123 
1124 	if (fcsm->sm_flags & FCSM_RESTORE_OFFLINE_TIMEOUT) {
1125 		fcsm->sm_flags &= ~FCSM_RESTORE_OFFLINE_TIMEOUT;
1126 
1127 		/*
1128 		 * If port if offline, link is not marked down and offline
1129 		 * timer is not already running, then restart offline timer.
1130 		 */
1131 		if (!(fcsm->sm_flags & FCSM_LINK_DOWN) &&
1132 		    fcsm->sm_offline_tid == NULL &&
1133 		    (fcsm->sm_flags & FCSM_PORT_OFFLINE)) {
1134 			fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
1135 			    (caddr_t)fcsm, fcsm_offline_ticks);
1136 		}
1137 	}
1138 
1139 	if (fcsm->sm_flags & FCSM_RESTORE_RETRY_TIMEOUT) {
1140 		fcsm->sm_flags &= ~FCSM_RESTORE_RETRY_TIMEOUT;
1141 
1142 		/*
1143 		 * If retry queue is not suspended and some cmds are waiting
1144 		 * to be retried, then restart the retry timer
1145 		 */
1146 		if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1147 			fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1148 			    (caddr_t)fcsm, fcsm_retry_ticks);
1149 		}
1150 	}
1151 	mutex_exit(&fcsm->sm_mutex);
1152 }
1153 
1154 static void
1155 fcsm_cleanup_port(fcsm_t *fcsm)
1156 {
1157 	fcsm_t		*curr, *prev;
1158 	int		status;
1159 	fcsm_job_t	*job;
1160 
1161 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1162 	    "fcsm_cleanup_port: entered"));
1163 
1164 	/*
1165 	 * Kill the job thread
1166 	 */
1167 	job = fcsm_alloc_job(KM_SLEEP);
1168 	ASSERT(job != NULL);
1169 	fcsm_init_job(job, fcsm->sm_instance, FCSM_JOB_THREAD_SHUTDOWN,
1170 	    FCSM_JOBFLAG_SYNC, NULL, NULL, NULL, NULL);
1171 
1172 	status = fcsm_process_job(job, 0);
1173 	ASSERT(status == FC_SUCCESS);
1174 
1175 	ASSERT(job->job_result == FC_SUCCESS);
1176 	fcsm_dealloc_job(job);
1177 
1178 	/*
1179 	 * We got here after ensuring the no commands are pending or active.
1180 	 * Therefore retry timeout thread should NOT be running.
1181 	 * Kill the offline timeout thread if currently running.
1182 	 */
1183 	mutex_enter(&fcsm->sm_mutex);
1184 
1185 	ASSERT(fcsm->sm_retry_tid == NULL);
1186 
1187 	if (fcsm->sm_offline_tid != NULL) {
1188 		timeout_id_t	tid;
1189 
1190 		tid = fcsm->sm_offline_tid;
1191 		fcsm->sm_offline_tid = (timeout_id_t)NULL;
1192 		mutex_exit(&fcsm->sm_mutex);
1193 		(void) untimeout(tid);
1194 	} else {
1195 		mutex_exit(&fcsm->sm_mutex);
1196 	}
1197 
1198 	/* Remove from the fcsm state structure from global linked list */
1199 	mutex_enter(&fcsm_global_mutex);
1200 	curr = fcsm_port_head;
1201 	prev = NULL;
1202 	while (curr != fcsm && curr != NULL) {
1203 		prev = curr;
1204 		curr = curr->sm_next;
1205 	}
1206 	ASSERT(curr != NULL);
1207 
1208 	if (prev == NULL) {
1209 		fcsm_port_head = curr->sm_next;
1210 	} else {
1211 		prev->sm_next = curr->sm_next;
1212 	}
1213 	mutex_exit(&fcsm_global_mutex);
1214 
1215 	if (fcsm->sm_cmd_cache != NULL) {
1216 		kmem_cache_destroy(fcsm->sm_cmd_cache);
1217 	}
1218 	cv_destroy(&fcsm->sm_job_cv);
1219 	mutex_destroy(&fcsm->sm_mutex);
1220 
1221 	/* Free the fcsm state structure */
1222 	ddi_soft_state_free(fcsm_state, fcsm->sm_instance);
1223 }
1224 
1225 
1226 /* ARGSUSED */
1227 static void
1228 fcsm_statec_cb(opaque_t ulph, opaque_t port_handle, uint32_t port_state,
1229     uint32_t port_top, fc_portmap_t *devlist, uint32_t dev_cnt,
1230     uint32_t port_sid)
1231 {
1232 	fcsm_t		*fcsm;
1233 	timeout_id_t	offline_tid, retry_tid;
1234 
1235 	mutex_enter(&fcsm_global_mutex);
1236 	if (fcsm_detached) {
1237 		mutex_exit(&fcsm_global_mutex);
1238 		return;
1239 	}
1240 
1241 	fcsm = ddi_get_soft_state(fcsm_state,
1242 	    fc_ulp_get_port_instance(port_handle));
1243 	if (fcsm == NULL) {
1244 		mutex_exit(&fcsm_global_mutex);
1245 		FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
1246 		    "statec_cb: instance 0x%x not found",
1247 		    fc_ulp_get_port_instance(port_handle)));
1248 		return;
1249 	}
1250 	mutex_enter(&fcsm->sm_mutex);
1251 	ASSERT(fcsm->sm_instance == fc_ulp_get_port_instance(port_handle));
1252 	if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
1253 		mutex_exit(&fcsm->sm_mutex);
1254 		mutex_exit(&fcsm_global_mutex);
1255 		FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, fcsm, NULL,
1256 		    "statec_cb: port not attached"));
1257 		return;
1258 	}
1259 
1260 	ASSERT(fcsm->sm_cb_count >= 0);
1261 
1262 	fcsm->sm_cb_count++;
1263 	mutex_exit(&fcsm->sm_mutex);
1264 	mutex_exit(&fcsm_global_mutex);
1265 
1266 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1267 	    "statec_cb: state <%s>(0x%x) topology <%s>(0x%x) dev_cnt %d",
1268 	    fcsm_port_state_to_str(FC_PORT_STATE_MASK(port_state)), port_state,
1269 	    fcsm_topology_to_str(port_top), port_top, dev_cnt));
1270 
1271 	fcsm_disp_devlist(fcsm, devlist, dev_cnt);
1272 
1273 	mutex_enter(&fcsm->sm_mutex);
1274 
1275 	/*
1276 	 * Reset the Mgmt server Login flag, so that login is performed again.
1277 	 */
1278 	fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
1279 
1280 	fcsm->sm_sid = port_sid;
1281 	fcsm->sm_port_top = port_top;
1282 	fcsm->sm_port_state = port_state;
1283 
1284 	switch (port_state) {
1285 	case FC_STATE_OFFLINE:
1286 	case FC_STATE_RESET:
1287 	case FC_STATE_RESET_REQUESTED:
1288 		fcsm->sm_flags |= FCSM_PORT_OFFLINE;
1289 		break;
1290 
1291 	case FC_STATE_ONLINE:
1292 	case FC_STATE_LOOP:
1293 	case FC_STATE_LIP:
1294 	case FC_STATE_LIP_LBIT_SET:
1295 		fcsm->sm_flags &= ~FCSM_PORT_OFFLINE;
1296 		fcsm->sm_flags &= ~FCSM_LINK_DOWN;
1297 		break;
1298 
1299 	case FC_STATE_NAMESERVICE:
1300 	case FC_STATE_DEVICE_CHANGE:
1301 	case FC_STATE_TARGET_PORT_RESET:
1302 	default:
1303 		/* Do nothing */
1304 		break;
1305 	}
1306 
1307 	offline_tid = retry_tid = NULL;
1308 	if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
1309 		/*
1310 		 * Port is offline.
1311 		 * Suspend cmd processing and start offline timeout thread.
1312 		 */
1313 		if (fcsm->sm_offline_tid == NULL) {
1314 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1315 			    "statec_cb: schedule offline timeout thread"));
1316 			fcsm->sm_flags |= FCSM_CMD_RETRY_Q_SUSPENDED;
1317 			/* Stop the cmd retry thread */
1318 			retry_tid = fcsm->sm_retry_tid;
1319 			fcsm->sm_retry_tid = (timeout_id_t)NULL;
1320 
1321 			fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
1322 			    (caddr_t)fcsm, fcsm_offline_ticks);
1323 		}
1324 
1325 	} else {
1326 		/*
1327 		 * Port is online.
1328 		 * Cancel offline timeout thread and resume command processing.
1329 		 */
1330 		if (fcsm->sm_offline_tid) {
1331 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1332 			    "statec_cb: cancel offline timeout thread"));
1333 			offline_tid = fcsm->sm_offline_tid;
1334 			fcsm->sm_offline_tid = (timeout_id_t)NULL;
1335 		}
1336 
1337 		fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
1338 		/* Start retry thread if needed */
1339 		if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1340 			fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1341 			    (caddr_t)fcsm, fcsm_retry_ticks);
1342 		}
1343 	}
1344 
1345 	mutex_exit(&fcsm->sm_mutex);
1346 
1347 	if (offline_tid != NULL) {
1348 		(void) untimeout(offline_tid);
1349 	}
1350 
1351 	if (retry_tid != NULL) {
1352 		(void) untimeout(retry_tid);
1353 	}
1354 
1355 	mutex_enter(&fcsm->sm_mutex);
1356 	fcsm->sm_cb_count--;
1357 	ASSERT(fcsm->sm_cb_count >= 0);
1358 	mutex_exit(&fcsm->sm_mutex);
1359 }
1360 
1361 
1362 static void
1363 fcsm_offline_timeout(void *handle)
1364 {
1365 	fcsm_t	*fcsm = (fcsm_t *)handle;
1366 
1367 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1368 	    "offline_timeout"));
1369 
1370 	mutex_enter(&fcsm->sm_mutex);
1371 	if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
1372 		fcsm->sm_flags |= FCSM_LINK_DOWN;
1373 	}
1374 	fcsm->sm_offline_tid = (timeout_id_t)NULL;
1375 	fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
1376 
1377 	/* Start the retry thread if needed */
1378 	if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1379 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1380 		    "offline_timeout: reschedule cmd retry thread"));
1381 		ASSERT(fcsm->sm_retry_tid == NULL);
1382 		fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1383 		    (caddr_t)fcsm, fcsm_retry_ticks);
1384 	}
1385 	mutex_exit(&fcsm->sm_mutex);
1386 }
1387 
1388 /* ARGSUSED */
1389 static int
1390 fcsm_els_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
1391     uint32_t claimed)
1392 {
1393 	return (FC_UNCLAIMED);
1394 }
1395 
1396 
1397 /* ARGSUSED */
1398 static int
1399 fcsm_data_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
1400     uint32_t claimed)
1401 {
1402 	return (FC_UNCLAIMED);
1403 }
1404 
1405 
1406 /* ARGSUSED */
1407 static int
1408 fcsm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1409     int *rval_p)
1410 {
1411 	int retval = 0;
1412 
1413 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: start"));
1414 
1415 	mutex_enter(&fcsm_global_mutex);
1416 	if (!(fcsm_flag & FCSM_OPEN)) {
1417 		mutex_exit(&fcsm_global_mutex);
1418 		return (ENXIO);
1419 	}
1420 	mutex_exit(&fcsm_global_mutex);
1421 
1422 	/* Allow only root to talk */
1423 	if (drv_priv(credp)) {
1424 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1425 		    "ioctl: end (disallowing underprivileged user)"));
1426 		return (EPERM);
1427 	}
1428 
1429 	switch (cmd) {
1430 
1431 	case FCSMIO_CMD: {
1432 		fcio_t	fcio;
1433 		int	status;
1434 #ifdef	_MULTI_DATAMODEL
1435 		switch (ddi_model_convert_from(mode & FMODELS)) {
1436 		case DDI_MODEL_ILP32: {
1437 			struct fcio32 fcio32;
1438 
1439 			if (status = ddi_copyin((void *)arg, (void *)&fcio32,
1440 			    sizeof (struct fcio32), mode)) {
1441 				retval = EFAULT;
1442 				break;
1443 			}
1444 			fcio.fcio_xfer = fcio32.fcio_xfer;
1445 			fcio.fcio_cmd = fcio32.fcio_cmd;
1446 			fcio.fcio_flags = fcio32.fcio_flags;
1447 			fcio.fcio_cmd_flags = fcio32.fcio_cmd_flags;
1448 			fcio.fcio_ilen = (size_t)fcio32.fcio_ilen;
1449 			fcio.fcio_ibuf = (caddr_t)(long)fcio32.fcio_ibuf;
1450 			fcio.fcio_olen = (size_t)fcio32.fcio_olen;
1451 			fcio.fcio_obuf = (caddr_t)(long)fcio32.fcio_obuf;
1452 			fcio.fcio_alen = (size_t)fcio32.fcio_alen;
1453 			fcio.fcio_abuf = (caddr_t)(long)fcio32.fcio_abuf;
1454 			fcio.fcio_errno = fcio32.fcio_errno;
1455 			break;
1456 		}
1457 
1458 		case DDI_MODEL_NONE:
1459 			if (status = ddi_copyin((void *)arg, (void *)&fcio,
1460 			    sizeof (fcio_t), mode)) {
1461 				retval = EFAULT;
1462 			}
1463 			break;
1464 		}
1465 #else	/* _MULTI_DATAMODEL */
1466 		if (status = ddi_copyin((void *)arg, (void *)&fcio,
1467 		    sizeof (fcio_t), mode)) {
1468 			retval = EFAULT;
1469 			break;
1470 		}
1471 #endif	/* _MULTI_DATAMODEL */
1472 		if (!status) {
1473 			retval = fcsm_fciocmd(arg, mode, credp, &fcio);
1474 		}
1475 		break;
1476 	}
1477 
1478 	default:
1479 		retval = ENOTTY;
1480 		break;
1481 	}
1482 
1483 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: end"));
1484 	return (retval);
1485 }
1486 
1487 /* ARGSUSED */
1488 static int
1489 fcsm_port_ioctl(opaque_t ulph, opaque_t port_handle, dev_t dev, int cmd,
1490     intptr_t arg, int mode, cred_t *credp, int *rval, uint32_t claimed)
1491 {
1492 	return (FC_UNCLAIMED);
1493 }
1494 
1495 
1496 /* ARGSUSED */
1497 static int
1498 fcsm_fciocmd(intptr_t arg, int mode, cred_t *credp, fcio_t *fcio)
1499 {
1500 	int  retval = 0;
1501 
1502 	switch (fcio->fcio_cmd) {
1503 	case  FCSMIO_CT_CMD: {
1504 		fcsm_t		*fcsm;
1505 		caddr_t		user_ibuf, user_obuf;
1506 		caddr_t		req_iu, rsp_iu, abuf;
1507 		int		status, instance, count;
1508 
1509 		if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
1510 		    (fcio->fcio_ilen == 0) || (fcio->fcio_ibuf == 0) ||
1511 		    (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0) ||
1512 		    (fcio->fcio_alen == 0) || (fcio->fcio_abuf == 0) ||
1513 		    (fcio->fcio_flags != 0) || (fcio->fcio_cmd_flags != 0) ||
1514 		    (fcio->fcio_ilen > FCSM_MAX_CT_SIZE) ||
1515 		    (fcio->fcio_olen > FCSM_MAX_CT_SIZE) ||
1516 		    (fcio->fcio_alen > MAXPATHLEN)) {
1517 			retval = EINVAL;
1518 			break;
1519 		}
1520 
1521 		/*
1522 		 * Get the destination port for which this ioctl
1523 		 * is targeted. The abuf will have the fp_minor
1524 		 * number.
1525 		 */
1526 		abuf = kmem_zalloc(fcio->fcio_alen, KM_SLEEP);
1527 		ASSERT(abuf != NULL);
1528 		if (ddi_copyin(fcio->fcio_abuf, abuf, fcio->fcio_alen, mode)) {
1529 			retval = EFAULT;
1530 			kmem_free(abuf, fcio->fcio_alen);
1531 			break;
1532 		}
1533 
1534 		instance = *((int *)abuf);
1535 		kmem_free(abuf, fcio->fcio_alen);
1536 
1537 		if (instance < 0) {
1538 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
1539 			    "fciocmd: instance 0x%x, invalid instance",
1540 			    instance));
1541 			retval = ENXIO;
1542 			break;
1543 		}
1544 
1545 		/*
1546 		 * We confirmed that path corresponds to our port driver
1547 		 * and a valid instance.
1548 		 * If this port instance is not yet attached, then wait
1549 		 * for a finite time for attach to complete
1550 		 */
1551 		fcsm = ddi_get_soft_state(fcsm_state, instance);
1552 		count = 0;
1553 		while (count++ <= 30) {
1554 			if (fcsm != NULL) {
1555 				mutex_enter(&fcsm->sm_mutex);
1556 				if (fcsm->sm_flags & FCSM_ATTACHED) {
1557 					mutex_exit(&fcsm->sm_mutex);
1558 					break;
1559 				}
1560 				mutex_exit(&fcsm->sm_mutex);
1561 			}
1562 			if (count == 1) {
1563 				FCSM_DEBUG(SMDL_TRACE,
1564 				    (CE_WARN, SM_LOG, NULL, NULL,
1565 				    "fciocmd: instance 0x%x, "
1566 				    "wait for port attach", instance));
1567 			}
1568 			delay(drv_usectohz(1000000));
1569 			fcsm = ddi_get_soft_state(fcsm_state, instance);
1570 		}
1571 		if (count > 30) {
1572 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
1573 			    "fciocmd: instance 0x%x, port not attached",
1574 			    instance));
1575 			retval = ENXIO;
1576 			break;
1577 		}
1578 
1579 		req_iu = kmem_zalloc(fcio->fcio_ilen, KM_SLEEP);
1580 		rsp_iu = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
1581 		ASSERT((req_iu != NULL) && (rsp_iu != NULL));
1582 
1583 		if (ddi_copyin(fcio->fcio_ibuf, req_iu,
1584 		    fcio->fcio_ilen, mode)) {
1585 			retval = EFAULT;
1586 			kmem_free(req_iu, fcio->fcio_ilen);
1587 			kmem_free(rsp_iu, fcio->fcio_olen);
1588 			break;
1589 		}
1590 
1591 		user_ibuf = fcio->fcio_ibuf;
1592 		user_obuf = fcio->fcio_obuf;
1593 		fcio->fcio_ibuf = req_iu;
1594 		fcio->fcio_obuf = rsp_iu;
1595 
1596 		status = fcsm_ct_passthru(fcsm->sm_instance, fcio, KM_SLEEP,
1597 		    FCSM_JOBFLAG_SYNC, NULL);
1598 		if (status != FC_SUCCESS) {
1599 			retval = EIO;
1600 		}
1601 
1602 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1603 		    "fciocmd: cmd 0x%x completion status 0x%x",
1604 		    fcio->fcio_cmd, status));
1605 		fcio->fcio_errno = status;
1606 		fcio->fcio_ibuf = user_ibuf;
1607 		fcio->fcio_obuf = user_obuf;
1608 
1609 		if (ddi_copyout(rsp_iu, fcio->fcio_obuf,
1610 		    fcio->fcio_olen, mode)) {
1611 			retval = EFAULT;
1612 			kmem_free(req_iu, fcio->fcio_ilen);
1613 			kmem_free(rsp_iu, fcio->fcio_olen);
1614 			break;
1615 		}
1616 
1617 		kmem_free(req_iu, fcio->fcio_ilen);
1618 		kmem_free(rsp_iu, fcio->fcio_olen);
1619 
1620 		if (fcsm_fcio_copyout(fcio, arg, mode)) {
1621 			retval = EFAULT;
1622 		}
1623 		break;
1624 	}
1625 
1626 	case  FCSMIO_ADAPTER_LIST: {
1627 	    fc_hba_list_t	*list;
1628 	    int			count;
1629 
1630 	    if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
1631 		    (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0)) {
1632 		retval = EINVAL;
1633 		break;
1634 	    }
1635 
1636 	    list = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
1637 
1638 	    if (ddi_copyin(fcio->fcio_obuf, list, fcio->fcio_olen, mode)) {
1639 		retval = EFAULT;
1640 		break;
1641 	    }
1642 	    list->version = FC_HBA_LIST_VERSION;
1643 
1644 	    if (fcio->fcio_olen < MAXPATHLEN * list->numAdapters) {
1645 		retval = EFAULT;
1646 		break;
1647 	    }
1648 
1649 	    count = fc_ulp_get_adapter_paths((char *)list->hbaPaths,
1650 		list->numAdapters);
1651 	    if (count < 0) { /* Did something go wrong? */
1652 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1653 		    "Error fetching adapter list."));
1654 		retval = ENXIO;
1655 		kmem_free(list, fcio->fcio_olen);
1656 		break;
1657 	    }
1658 	    /* Sucess (or short buffer) */
1659 	    list->numAdapters = count;
1660 	    if (ddi_copyout(list, fcio->fcio_obuf,
1661 		    fcio->fcio_olen, mode)) {
1662 		retval = EFAULT;
1663 	    }
1664 	    kmem_free(list, fcio->fcio_olen);
1665 	    break;
1666 	}
1667 
1668 	default:
1669 		FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
1670 		    "fciocmd: unknown cmd <0x%x>", fcio->fcio_cmd));
1671 		retval = ENOTTY;
1672 		break;
1673 	}
1674 
1675 	return (retval);
1676 }
1677 
1678 static int
1679 fcsm_fcio_copyout(fcio_t *fcio, intptr_t arg, int mode)
1680 {
1681 	int status;
1682 
1683 #ifdef	_MULTI_DATAMODEL
1684 	switch (ddi_model_convert_from(mode & FMODELS)) {
1685 	case DDI_MODEL_ILP32: {
1686 		struct fcio32 fcio32;
1687 
1688 		fcio32.fcio_xfer = fcio->fcio_xfer;
1689 		fcio32.fcio_cmd = fcio->fcio_cmd;
1690 		fcio32.fcio_flags = fcio->fcio_flags;
1691 		fcio32.fcio_cmd_flags = fcio->fcio_cmd_flags;
1692 		fcio32.fcio_ilen = fcio->fcio_ilen;
1693 		fcio32.fcio_ibuf = (caddr32_t)(long)fcio->fcio_ibuf;
1694 		fcio32.fcio_olen = fcio->fcio_olen;
1695 		fcio32.fcio_obuf = (caddr32_t)(long)fcio->fcio_obuf;
1696 		fcio32.fcio_alen = fcio->fcio_alen;
1697 		fcio32.fcio_abuf = (caddr32_t)(long)fcio->fcio_abuf;
1698 		fcio32.fcio_errno = fcio->fcio_errno;
1699 
1700 		status = ddi_copyout((void *)&fcio32, (void *)arg,
1701 		    sizeof (struct fcio32), mode);
1702 		break;
1703 	}
1704 	case DDI_MODEL_NONE:
1705 		status = ddi_copyout((void *)fcio, (void *)arg,
1706 		    sizeof (fcio_t), mode);
1707 		break;
1708 	}
1709 #else	/* _MULTI_DATAMODEL */
1710 	status = ddi_copyout((void *)fcio, (void *)arg, sizeof (fcio_t), mode);
1711 #endif	/* _MULTI_DATAMODEL */
1712 
1713 	return (status);
1714 }
1715 
1716 
1717 /* ARGSUSED */
1718 static int
1719 fcsm_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1720 {
1721 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "open"));
1722 
1723 	if (otyp != OTYP_CHR) {
1724 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1725 		    "fcsm_open: failed. open type 0x%x for minor 0x%x is not "
1726 		    "OTYP_CHR", otyp, getminor(*devp)));
1727 		return (EINVAL);
1728 	}
1729 
1730 	/*
1731 	 * Allow anybody to open (both root and non-root users).
1732 	 * Previlege level checks are made on the per ioctl basis.
1733 	 */
1734 	mutex_enter(&fcsm_global_mutex);
1735 	if (flags & FEXCL) {
1736 		if (fcsm_flag & FCSM_OPEN) {
1737 			mutex_exit(&fcsm_global_mutex);
1738 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1739 			    "fcsm_open: exclusive open of 0x%x failed",
1740 			    getminor(*devp)));
1741 			return (EBUSY);
1742 		} else {
1743 			ASSERT(fcsm_flag == FCSM_IDLE);
1744 			fcsm_flag |= FCSM_EXCL;
1745 		}
1746 	} else {
1747 		if (fcsm_flag & FCSM_EXCL) {
1748 			mutex_exit(&fcsm_global_mutex);
1749 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1750 			    "fcsm_open: failed. Device minor 0x%x is in "
1751 			    "exclusive open mode", getminor(*devp)));
1752 			return (EBUSY);
1753 		}
1754 
1755 	}
1756 	fcsm_flag |= FCSM_OPEN;
1757 	mutex_exit(&fcsm_global_mutex);
1758 	return (0);
1759 }
1760 
1761 
1762 /* ARGSUSED */
1763 static int
1764 fcsm_close(dev_t dev, int flag, int otyp, cred_t *credp)
1765 {
1766 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "close"));
1767 
1768 	if (otyp != OTYP_CHR) {
1769 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1770 		    "fcsm_close: failed. close type 0x%x for minor 0x%x is not "
1771 		    "OTYP_CHR", otyp, getminor(dev)));
1772 		return (EINVAL);
1773 	}
1774 
1775 	mutex_enter(&fcsm_global_mutex);
1776 	if ((fcsm_flag & FCSM_OPEN) == 0) {
1777 		mutex_exit(&fcsm_global_mutex);
1778 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1779 		    "fcsm_close: failed. minor 0x%x is already closed",
1780 		    getminor(dev)));
1781 		return (ENODEV);
1782 	}
1783 	fcsm_flag = FCSM_IDLE;
1784 	mutex_exit(&fcsm_global_mutex);
1785 	return (0);
1786 }
1787 
1788 
1789 /* ARGSUSED */
1790 static void
1791 fcsm_disp_devlist(fcsm_t *fcsm, fc_portmap_t *devlist, uint32_t dev_cnt)
1792 {
1793 	fc_portmap_t	*map;
1794 	uint32_t	i;
1795 
1796 	if (dev_cnt == 0) {
1797 		return;
1798 	}
1799 
1800 	ASSERT(devlist != NULL);
1801 	for (i = 0; i < dev_cnt; i++) {
1802 		map = &devlist[i];
1803 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1804 		    "list[%d]: ID 0x%x WWN %x:%x:%x:%x:%x:%x:%x:%x "
1805 		    "state (0x%x) "
1806 		    "type <%s>(0x%x) "
1807 		    "flags (0x%x)",
1808 		    i, map->map_did.port_id,
1809 		    map->map_pwwn.raw_wwn[0], map->map_pwwn.raw_wwn[1],
1810 		    map->map_pwwn.raw_wwn[2], map->map_pwwn.raw_wwn[3],
1811 		    map->map_pwwn.raw_wwn[4], map->map_pwwn.raw_wwn[5],
1812 		    map->map_pwwn.raw_wwn[6], map->map_pwwn.raw_wwn[7],
1813 		    map->map_state,
1814 		    fcsm_dev_type_to_str(map->map_type), map->map_type,
1815 		    map->map_flags));
1816 	}
1817 }
1818 
1819 /* ARGSUSED */
1820 static void
1821 fcsm_display(int level, int flags, fcsm_t *fcsm, fc_packet_t *pkt,
1822     const char *fmt, ...)
1823 {
1824 	caddr_t	buf;
1825 	va_list	ap;
1826 
1827 	buf = kmem_zalloc(256, KM_NOSLEEP);
1828 	if (buf == NULL) {
1829 		return;
1830 	}
1831 
1832 	if (fcsm) {
1833 		(void) sprintf(buf + strlen(buf), "fcsm(%d): ",
1834 		    ddi_get_instance(fcsm->sm_port_info.port_dip));
1835 	} else {
1836 		(void) sprintf(buf, "fcsm: ");
1837 	}
1838 
1839 	va_start(ap, fmt);
1840 	(void) vsprintf(buf + strlen(buf), fmt, ap);
1841 	va_end(ap);
1842 
1843 	if (pkt) {
1844 		caddr_t state, reason, action, expln;
1845 
1846 		(void) fc_ulp_pkt_error(pkt, &state, &reason, &action, &expln);
1847 
1848 		(void) sprintf(buf + strlen(buf),
1849 		    " state: %s(0x%x); reason: %s(0x%x)",
1850 		    state, pkt->pkt_state, reason, pkt->pkt_reason);
1851 	}
1852 
1853 	switch (flags) {
1854 		case SM_LOG:
1855 			cmn_err(level, "!%s", buf);
1856 			break;
1857 
1858 		case SM_CONSOLE:
1859 			cmn_err(level, "^%s", buf);
1860 			break;
1861 
1862 		default:
1863 			cmn_err(level, "%s", buf);
1864 			break;
1865 	}
1866 
1867 	kmem_free(buf, 256);
1868 }
1869 
1870 
1871 /*
1872  * Convert FC packet state to FC errno
1873  */
1874 int
1875 fcsm_pkt_state_to_rval(uchar_t state, uint32_t reason)
1876 {
1877 	int count;
1878 
1879 	if (state == FC_PKT_LOCAL_RJT && (reason == FC_REASON_NO_CONNECTION ||
1880 	    reason == FC_REASON_LOGIN_REQUIRED)) {
1881 		return (FC_LOGINREQ);
1882 	} else if (state == FC_PKT_PORT_OFFLINE &&
1883 	    reason == FC_REASON_LOGIN_REQUIRED) {
1884 		return (FC_LOGINREQ);
1885 	}
1886 
1887 	for (count = 0; count < sizeof (fcsm_xlat_pkt_state) /
1888 	    sizeof (fcsm_xlat_pkt_state[0]); count++) {
1889 		if (fcsm_xlat_pkt_state[count].xlat_state == state) {
1890 			return (fcsm_xlat_pkt_state[count].xlat_rval);
1891 		}
1892 	}
1893 
1894 	return (FC_FAILURE);
1895 }
1896 
1897 
1898 /*
1899  * Convert port state state to descriptive string
1900  */
1901 caddr_t
1902 fcsm_port_state_to_str(uint32_t port_state)
1903 {
1904 	int count;
1905 
1906 	for (count = 0; count < sizeof (fcsm_xlat_port_state) /
1907 	    sizeof (fcsm_xlat_port_state[0]); count++) {
1908 		if (fcsm_xlat_port_state[count].xlat_pstate == port_state) {
1909 			return (fcsm_xlat_port_state[count].xlat_state_str);
1910 		}
1911 	}
1912 
1913 	return (NULL);
1914 }
1915 
1916 
1917 /*
1918  * Convert port topology state to descriptive string
1919  */
1920 caddr_t
1921 fcsm_topology_to_str(uint32_t topology)
1922 {
1923 	int count;
1924 
1925 	for (count = 0; count < sizeof (fcsm_xlat_topology) /
1926 	    sizeof (fcsm_xlat_topology[0]); count++) {
1927 		if (fcsm_xlat_topology[count].xlat_top == topology) {
1928 			return (fcsm_xlat_topology[count].xlat_top_str);
1929 		}
1930 	}
1931 
1932 	return (NULL);
1933 }
1934 
1935 
1936 /*
1937  * Convert port topology state to descriptive string
1938  */
1939 static caddr_t
1940 fcsm_dev_type_to_str(uint32_t type)
1941 {
1942 	int count;
1943 
1944 	for (count = 0; count < sizeof (fcsm_xlat_dev_type) /
1945 	    sizeof (fcsm_xlat_dev_type[0]); count++) {
1946 		if (fcsm_xlat_dev_type[count].xlat_type == type) {
1947 			return (fcsm_xlat_dev_type[count].xlat_str);
1948 		}
1949 	}
1950 
1951 	return (NULL);
1952 }
1953 
1954 static int
1955 fcsm_cmd_cache_constructor(void *buf, void *cdarg, int kmflags)
1956 {
1957 	fcsm_cmd_t		*cmd = (fcsm_cmd_t *)buf;
1958 	fcsm_t			*fcsm = (fcsm_t *)cdarg;
1959 	int			(*callback)(caddr_t);
1960 	fc_packet_t		*pkt;
1961 	fc_ulp_port_info_t	*pinfo;
1962 
1963 	ASSERT(fcsm != NULL && buf != NULL);
1964 	callback = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
1965 
1966 	cmd->cmd_fp_pkt		= &cmd->cmd_fc_packet;
1967 	cmd->cmd_job		= NULL;
1968 	cmd->cmd_fcsm		= fcsm;
1969 	cmd->cmd_dma_flags	= 0;
1970 
1971 	pkt = &cmd->cmd_fc_packet;
1972 
1973 	pkt->pkt_ulp_rscn_infop = NULL;
1974 	pkt->pkt_fca_private = (opaque_t)((caddr_t)cmd + sizeof (fcsm_cmd_t));
1975 	pkt->pkt_ulp_private = (opaque_t)cmd;
1976 
1977 	pinfo = &fcsm->sm_port_info;
1978 	if (ddi_dma_alloc_handle(pinfo->port_dip, pinfo->port_cmd_dma_attr,
1979 	    callback, NULL, &pkt->pkt_cmd_dma) != DDI_SUCCESS) {
1980 		return (1);
1981 	}
1982 
1983 	if (ddi_dma_alloc_handle(pinfo->port_dip, pinfo->port_resp_dma_attr,
1984 	    callback, NULL, &pkt->pkt_resp_dma) != DDI_SUCCESS) {
1985 		ddi_dma_free_handle(&pkt->pkt_cmd_dma);
1986 		return (1);
1987 	}
1988 
1989 	pkt->pkt_cmd_acc = pkt->pkt_resp_acc = NULL;
1990 	pkt->pkt_cmd_cookie_cnt = pkt->pkt_resp_cookie_cnt =
1991 	    pkt->pkt_data_cookie_cnt = 0;
1992 	pkt->pkt_cmd_cookie = pkt->pkt_resp_cookie =
1993 	    pkt->pkt_data_cookie = NULL;
1994 
1995 	return (0);
1996 }
1997 
1998 
1999 /* ARGSUSED */
2000 static void
2001 fcsm_cmd_cache_destructor(void *buf, void *cdarg)
2002 {
2003 	fcsm_cmd_t	*cmd = (fcsm_cmd_t *)buf;
2004 	fcsm_t		*fcsm = (fcsm_t *)cdarg;
2005 	fc_packet_t	*pkt;
2006 
2007 	ASSERT(fcsm == cmd->cmd_fcsm);
2008 
2009 	pkt = cmd->cmd_fp_pkt;
2010 
2011 	if (pkt->pkt_cmd_dma != NULL) {
2012 		ddi_dma_free_handle(&pkt->pkt_cmd_dma);
2013 	}
2014 
2015 	if (pkt->pkt_resp_dma != NULL) {
2016 		ddi_dma_free_handle(&pkt->pkt_resp_dma);
2017 	}
2018 }
2019 
2020 
2021 static fcsm_cmd_t *
2022 fcsm_alloc_cmd(fcsm_t *fcsm, uint32_t cmd_len, uint32_t resp_len, int sleep)
2023 {
2024 	fcsm_cmd_t	*cmd;
2025 	fc_packet_t	*pkt;
2026 	int		rval;
2027 	ulong_t		real_len;
2028 	int		(*callback)(caddr_t);
2029 	ddi_dma_cookie_t	pkt_cookie;
2030 	ddi_dma_cookie_t	*cp;
2031 	uint32_t		cnt;
2032 	fc_ulp_port_info_t	*pinfo;
2033 
2034 	ASSERT(fcsm != NULL);
2035 	pinfo = &fcsm->sm_port_info;
2036 
2037 	callback = (sleep == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
2038 
2039 	cmd = (fcsm_cmd_t *)kmem_cache_alloc(fcsm->sm_cmd_cache, sleep);
2040 	if (cmd == NULL) {
2041 		FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, fcsm, NULL,
2042 		    "alloc_cmd: kmem_cache_alloc failed"));
2043 		return (NULL);
2044 	}
2045 
2046 	cmd->cmd_retry_count	= 0;
2047 	cmd->cmd_max_retries	= 0;
2048 	cmd->cmd_retry_interval	= 0;
2049 	cmd->cmd_transport	= NULL;
2050 
2051 	ASSERT(cmd->cmd_dma_flags == 0);
2052 	ASSERT(cmd->cmd_fp_pkt == &cmd->cmd_fc_packet);
2053 	pkt = cmd->cmd_fp_pkt;
2054 
2055 	/* Zero out the important fc_packet fields */
2056 	pkt->pkt_pd		= NULL;
2057 	pkt->pkt_datalen	= 0;
2058 	pkt->pkt_data		= NULL;
2059 	pkt->pkt_state		= 0;
2060 	pkt->pkt_action		= 0;
2061 	pkt->pkt_reason		= 0;
2062 	pkt->pkt_expln		= 0;
2063 
2064 	/*
2065 	 * Now that pkt_pd is initialized, we can call fc_ulp_init_packet
2066 	 */
2067 
2068 	if (fc_ulp_init_packet((opaque_t)pinfo->port_handle, pkt, sleep)
2069 	    != FC_SUCCESS) {
2070 		kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2071 		return (NULL);
2072 	}
2073 
2074 	if (cmd_len) {
2075 		ASSERT(pkt->pkt_cmd_dma != NULL);
2076 
2077 		rval = ddi_dma_mem_alloc(pkt->pkt_cmd_dma, cmd_len,
2078 		    fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
2079 		    callback, NULL, (caddr_t *)&pkt->pkt_cmd, &real_len,
2080 		    &pkt->pkt_cmd_acc);
2081 
2082 		if (rval != DDI_SUCCESS) {
2083 			(void) fc_ulp_uninit_packet(
2084 			    (opaque_t)pinfo->port_handle, pkt);
2085 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2086 			fcsm_free_cmd_dma(cmd);
2087 			return (NULL);
2088 		}
2089 
2090 		cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_MEM;
2091 
2092 		if (real_len < cmd_len) {
2093 			(void) fc_ulp_uninit_packet(
2094 			    (opaque_t)pinfo->port_handle, pkt);
2095 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2096 			fcsm_free_cmd_dma(cmd);
2097 			return (NULL);
2098 		}
2099 
2100 		rval = ddi_dma_addr_bind_handle(pkt->pkt_cmd_dma, NULL,
2101 		    pkt->pkt_cmd, real_len, DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
2102 		    callback, NULL, &pkt_cookie, &pkt->pkt_cmd_cookie_cnt);
2103 
2104 		if (rval != DDI_DMA_MAPPED) {
2105 			(void) fc_ulp_uninit_packet(
2106 			    (opaque_t)pinfo->port_handle, pkt);
2107 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2108 			fcsm_free_cmd_dma(cmd);
2109 			return (NULL);
2110 		}
2111 
2112 		cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_BIND;
2113 
2114 		if (pkt->pkt_cmd_cookie_cnt >
2115 		    pinfo->port_cmd_dma_attr->dma_attr_sgllen) {
2116 			(void) fc_ulp_uninit_packet(
2117 			    (opaque_t)pinfo->port_handle, pkt);
2118 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2119 			fcsm_free_cmd_dma(cmd);
2120 			return (NULL);
2121 		}
2122 
2123 		ASSERT(pkt->pkt_cmd_cookie_cnt != 0);
2124 
2125 		cp = pkt->pkt_cmd_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2126 		    pkt->pkt_cmd_cookie_cnt * sizeof (pkt_cookie),
2127 		    KM_NOSLEEP);
2128 
2129 		if (cp == NULL) {
2130 			(void) fc_ulp_uninit_packet(
2131 			    (opaque_t)pinfo->port_handle, pkt);
2132 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2133 			fcsm_free_cmd_dma(cmd);
2134 			return (NULL);
2135 		}
2136 
2137 		*cp = pkt_cookie;
2138 		cp++;
2139 		for (cnt = 1; cnt < pkt->pkt_cmd_cookie_cnt; cnt++, cp++) {
2140 			ddi_dma_nextcookie(pkt->pkt_cmd_dma, &pkt_cookie);
2141 			*cp = pkt_cookie;
2142 		}
2143 	}
2144 
2145 	if (resp_len) {
2146 		ASSERT(pkt->pkt_resp_dma != NULL);
2147 
2148 		rval = ddi_dma_mem_alloc(pkt->pkt_resp_dma, resp_len,
2149 		    fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
2150 		    callback, NULL, (caddr_t *)&pkt->pkt_resp, &real_len,
2151 		    &pkt->pkt_resp_acc);
2152 
2153 		if (rval != DDI_SUCCESS) {
2154 			(void) fc_ulp_uninit_packet(
2155 			    (opaque_t)pinfo->port_handle, pkt);
2156 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2157 			fcsm_free_cmd_dma(cmd);
2158 			return (NULL);
2159 		}
2160 
2161 		cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_MEM;
2162 
2163 		if (real_len < resp_len) {
2164 			(void) fc_ulp_uninit_packet(
2165 			    (opaque_t)pinfo->port_handle, pkt);
2166 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2167 			fcsm_free_cmd_dma(cmd);
2168 			return (NULL);
2169 		}
2170 
2171 		rval = ddi_dma_addr_bind_handle(pkt->pkt_resp_dma, NULL,
2172 		    pkt->pkt_resp, real_len, DDI_DMA_READ | DDI_DMA_CONSISTENT,
2173 		    callback, NULL, &pkt_cookie, &pkt->pkt_resp_cookie_cnt);
2174 
2175 		if (rval != DDI_DMA_MAPPED) {
2176 			(void) fc_ulp_uninit_packet(
2177 			    (opaque_t)pinfo->port_handle, pkt);
2178 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2179 			fcsm_free_cmd_dma(cmd);
2180 			return (NULL);
2181 		}
2182 
2183 		cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_BIND;
2184 
2185 		if (pkt->pkt_resp_cookie_cnt >
2186 		    pinfo->port_resp_dma_attr->dma_attr_sgllen) {
2187 			(void) fc_ulp_uninit_packet(
2188 			    (opaque_t)pinfo->port_handle, pkt);
2189 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2190 			fcsm_free_cmd_dma(cmd);
2191 			return (NULL);
2192 		}
2193 
2194 		ASSERT(pkt->pkt_resp_cookie_cnt != 0);
2195 
2196 		cp = pkt->pkt_resp_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2197 		    pkt->pkt_resp_cookie_cnt * sizeof (pkt_cookie),
2198 		    KM_NOSLEEP);
2199 
2200 		if (cp == NULL) {
2201 			(void) fc_ulp_uninit_packet(
2202 			    (opaque_t)pinfo->port_handle, pkt);
2203 			kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2204 			fcsm_free_cmd_dma(cmd);
2205 			return (NULL);
2206 		}
2207 
2208 		*cp = pkt_cookie;
2209 		cp++;
2210 		for (cnt = 1; cnt < pkt->pkt_resp_cookie_cnt; cnt++, cp++) {
2211 			ddi_dma_nextcookie(pkt->pkt_resp_dma, &pkt_cookie);
2212 			*cp = pkt_cookie;
2213 		}
2214 	}
2215 
2216 	pkt->pkt_cmdlen = cmd_len;
2217 	pkt->pkt_rsplen = resp_len;
2218 
2219 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2220 	    "alloc_cmd: cmd 0x%p", (void *)cmd));
2221 	return (cmd);
2222 }
2223 
2224 static void
2225 fcsm_free_cmd(fcsm_cmd_t *cmd)
2226 {
2227 	fcsm_t		*fcsm;
2228 
2229 	fcsm = cmd->cmd_fcsm;
2230 	ASSERT(fcsm != NULL);
2231 
2232 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2233 	    "free_cmd: cmd 0x%p", (void *)cmd));
2234 
2235 	fcsm_free_cmd_dma(cmd);
2236 
2237 	(void) fc_ulp_uninit_packet((opaque_t)fcsm->sm_port_info.port_handle,
2238 	    cmd->cmd_fp_pkt);
2239 	kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2240 }
2241 
2242 static void
2243 fcsm_free_cmd_dma(fcsm_cmd_t *cmd)
2244 {
2245 	fc_packet_t	*pkt;
2246 
2247 	pkt = cmd->cmd_fp_pkt;
2248 	ASSERT(pkt != NULL);
2249 
2250 	pkt->pkt_cmdlen = 0;
2251 	pkt->pkt_rsplen = 0;
2252 	pkt->pkt_tran_type = 0;
2253 	pkt->pkt_tran_flags = 0;
2254 
2255 	if (pkt->pkt_cmd_cookie != NULL) {
2256 		kmem_free(pkt->pkt_cmd_cookie, pkt->pkt_cmd_cookie_cnt *
2257 		    sizeof (ddi_dma_cookie_t));
2258 		pkt->pkt_cmd_cookie = NULL;
2259 	}
2260 
2261 	if (pkt->pkt_resp_cookie != NULL) {
2262 		kmem_free(pkt->pkt_resp_cookie, pkt->pkt_resp_cookie_cnt *
2263 		    sizeof (ddi_dma_cookie_t));
2264 		pkt->pkt_resp_cookie = NULL;
2265 	}
2266 
2267 	if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_BIND) {
2268 		(void) ddi_dma_unbind_handle(pkt->pkt_cmd_dma);
2269 	}
2270 
2271 	if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_MEM) {
2272 		if (pkt->pkt_cmd_acc) {
2273 			ddi_dma_mem_free(&pkt->pkt_cmd_acc);
2274 		}
2275 	}
2276 
2277 	if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_BIND) {
2278 		(void) ddi_dma_unbind_handle(pkt->pkt_resp_dma);
2279 	}
2280 
2281 	if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_MEM) {
2282 		if (pkt->pkt_resp_acc) {
2283 			ddi_dma_mem_free(&pkt->pkt_resp_acc);
2284 		}
2285 	}
2286 
2287 	cmd->cmd_dma_flags = 0;
2288 }
2289 
2290 /* ARGSUSED */
2291 static int
2292 fcsm_job_cache_constructor(void *buf, void *cdarg, int kmflag)
2293 {
2294 	fcsm_job_t *job = (fcsm_job_t *)buf;
2295 
2296 	mutex_init(&job->job_mutex, NULL, MUTEX_DRIVER, NULL);
2297 	sema_init(&job->job_sema, 0, NULL, SEMA_DEFAULT, NULL);
2298 
2299 	return (0);
2300 }
2301 
2302 /* ARGSUSED */
2303 static void
2304 fcsm_job_cache_destructor(void *buf, void *cdarg)
2305 {
2306 	fcsm_job_t *job = (fcsm_job_t *)buf;
2307 
2308 	sema_destroy(&job->job_sema);
2309 	mutex_destroy(&job->job_mutex);
2310 }
2311 
2312 
2313 static fcsm_job_t *
2314 fcsm_alloc_job(int sleep)
2315 {
2316 	fcsm_job_t	*job;
2317 
2318 	job = (fcsm_job_t *)kmem_cache_alloc(fcsm_job_cache, sleep);
2319 	if (job != NULL) {
2320 		job->job_code		= FCSM_JOB_NONE;
2321 		job->job_flags		= 0;
2322 		job->job_port_instance	= -1;
2323 		job->job_result 	= -1;
2324 		job->job_arg		= (opaque_t)0;
2325 		job->job_caller_priv	= (opaque_t)0;
2326 		job->job_comp		= NULL;
2327 		job->job_comp_arg	= (opaque_t)0;
2328 		job->job_priv		= (void *)0;
2329 		job->job_priv_flags	= 0;
2330 		job->job_next		= 0;
2331 	}
2332 
2333 	return (job);
2334 }
2335 
2336 static void
2337 fcsm_dealloc_job(fcsm_job_t *job)
2338 {
2339 	kmem_cache_free(fcsm_job_cache, (void *)job);
2340 }
2341 
2342 
2343 static void
2344 fcsm_init_job(fcsm_job_t *job, int instance, uint32_t command, uint32_t flags,
2345     opaque_t arg, opaque_t caller_priv,
2346     void (*comp)(opaque_t, fcsm_job_t *, int), opaque_t comp_arg)
2347 {
2348 	ASSERT(job != NULL);
2349 	job->job_port_instance	= instance;
2350 	job->job_code		= command;
2351 	job->job_flags		= flags;
2352 	job->job_arg 		= arg;
2353 	job->job_caller_priv 	= caller_priv;
2354 	job->job_comp		= comp;
2355 	job->job_comp_arg	= comp_arg;
2356 	job->job_retry_count	= 0;
2357 }
2358 
2359 static int
2360 fcsm_process_job(fcsm_job_t *job, int priority_flag)
2361 {
2362 	fcsm_t	*fcsm;
2363 	int	sync;
2364 
2365 	ASSERT(job != NULL);
2366 	ASSERT(!MUTEX_HELD(&job->job_mutex));
2367 
2368 	fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2369 
2370 	if (fcsm == NULL) {
2371 		FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
2372 		    "process_job: port instance 0x%x not found",
2373 		    job->job_port_instance));
2374 		return (FC_BADDEV);
2375 	}
2376 
2377 	mutex_enter(&job->job_mutex);
2378 	/* Both SYNC and ASYNC flags should not be set */
2379 	ASSERT(((job->job_flags & (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) ==
2380 	    FCSM_JOBFLAG_SYNC) || ((job->job_flags &
2381 	    (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) == FCSM_JOBFLAG_ASYNC));
2382 	/*
2383 	 * Check if job is a synchronous job. We might not be able to
2384 	 * check it reliably after enque_job(), if job is an ASYNC job.
2385 	 */
2386 	sync = job->job_flags & FCSM_JOBFLAG_SYNC;
2387 	mutex_exit(&job->job_mutex);
2388 
2389 	/* Queue the job for processing by job thread */
2390 	fcsm_enque_job(fcsm, job, priority_flag);
2391 
2392 	/* Wait for job completion, if it is a synchronous job */
2393 	if (sync) {
2394 		/*
2395 		 * This is a Synchronous Job. So job structure is available.
2396 		 * Caller is responsible for freeing it.
2397 		 */
2398 		FCSM_DEBUG(SMDL_ERR, (CE_CONT, SM_LOG, fcsm, NULL,
2399 		    "process_job: Waiting for sync job <%p> completion",
2400 		    (void *)job));
2401 		sema_p(&job->job_sema);
2402 	}
2403 
2404 	return (FC_SUCCESS);
2405 }
2406 
2407 static void
2408 fcsm_enque_job(fcsm_t *fcsm, fcsm_job_t *job, int priority_flag)
2409 {
2410 	ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
2411 
2412 	mutex_enter(&fcsm->sm_mutex);
2413 	/* Queue the job at the head or tail depending on the job priority */
2414 	if (priority_flag) {
2415 		FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
2416 		    "enque_job: job 0x%p is high priority", job));
2417 		/* Queue at the head */
2418 		if (fcsm->sm_job_tail == NULL) {
2419 			ASSERT(fcsm->sm_job_head == NULL);
2420 			fcsm->sm_job_head = fcsm->sm_job_tail = job;
2421 		} else {
2422 			ASSERT(fcsm->sm_job_head != NULL);
2423 			job->job_next = fcsm->sm_job_head;
2424 			fcsm->sm_job_head = job;
2425 		}
2426 	} else {
2427 		FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
2428 		    "enque_job: job 0x%p is normal", job));
2429 		/* Queue at the tail */
2430 		if (fcsm->sm_job_tail == NULL) {
2431 			ASSERT(fcsm->sm_job_head == NULL);
2432 			fcsm->sm_job_head = fcsm->sm_job_tail = job;
2433 		} else {
2434 			ASSERT(fcsm->sm_job_head != NULL);
2435 			fcsm->sm_job_tail->job_next = job;
2436 			fcsm->sm_job_tail = job;
2437 		}
2438 		job->job_next = NULL;
2439 	}
2440 
2441 	/* Signal the job thread to process the job */
2442 	cv_signal(&fcsm->sm_job_cv);
2443 	mutex_exit(&fcsm->sm_mutex);
2444 }
2445 
2446 static int
2447 fcsm_retry_job(fcsm_t *fcsm, fcsm_job_t *job)
2448 {
2449 	/*
2450 	 * If it is a CT passthru job and status is login required, then
2451 	 * retry the job so that login can be performed again.
2452 	 * Ensure that this retry is performed a finite number of times,
2453 	 * so that a faulty fabric does not cause us to retry forever.
2454 	 */
2455 
2456 	switch (job->job_code) {
2457 	case FCSM_JOB_CT_PASSTHRU: {
2458 		uint32_t	jobflag;
2459 		fc_ct_header_t	*ct_header;
2460 
2461 		if (job->job_result != FC_LOGINREQ) {
2462 			break;
2463 		}
2464 
2465 		/*
2466 		 * If it is a management server command
2467 		 * then Reset the Management server login flag, so that login
2468 		 * gets re-established.
2469 		 * If it is a Name server command,
2470 		 * then it is 'fp' responsibility to perform the login.
2471 		 */
2472 		ASSERT(job->job_arg != NULL);
2473 		ct_header =
2474 		    (fc_ct_header_t *)((fcio_t *)job->job_arg)->fcio_ibuf;
2475 		if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
2476 			mutex_enter(&fcsm->sm_mutex);
2477 			fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
2478 			mutex_exit(&fcsm->sm_mutex);
2479 		}
2480 
2481 		if (job->job_retry_count >= fcsm_max_job_retries) {
2482 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2483 			    "retry_job: job 0x%p max retries (%d) reached",
2484 			    (void *)job, job->job_retry_count));
2485 			break;
2486 		}
2487 
2488 		/*
2489 		 * Login is required again. Retry the command, so that
2490 		 * login will get performed again.
2491 		 */
2492 		mutex_enter(&job->job_mutex);
2493 		job->job_retry_count++;
2494 		jobflag = job->job_flags;
2495 		mutex_exit(&job->job_mutex);
2496 
2497 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2498 		    "retry_job: retry(%d) job 0x%p",
2499 		    job->job_retry_count, (void *)job));
2500 		/*
2501 		 * This job should get picked up before the
2502 		 * other jobs sitting in the queue.
2503 		 * Requeue the command at the head and then
2504 		 * reset the SERIALIZE flag.
2505 		 */
2506 		fcsm_enque_job(fcsm, job, 1);
2507 		if (jobflag & FCSM_JOBFLAG_SERIALIZE) {
2508 			mutex_enter(&fcsm->sm_mutex);
2509 			ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
2510 			fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
2511 
2512 			/* Signal the job thread to process the job */
2513 			cv_signal(&fcsm->sm_job_cv);
2514 			mutex_exit(&fcsm->sm_mutex);
2515 		}
2516 
2517 		/* Command is queued for retrying */
2518 		return (0);
2519 	}
2520 
2521 	default:
2522 		break;
2523 	}
2524 	return (1);
2525 }
2526 
2527 static void
2528 fcsm_jobdone(fcsm_job_t *job)
2529 {
2530 	fcsm_t	*fcsm;
2531 
2532 	fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2533 	ASSERT(fcsm != NULL);
2534 
2535 	if (job->job_result != FC_SUCCESS) {
2536 		if (fcsm_retry_job(fcsm, job) == 0) {
2537 			/* Job retried. so just return from here */
2538 			return;
2539 		}
2540 	}
2541 
2542 	if (job->job_comp) {
2543 		job->job_comp(job->job_comp_arg, job, job->job_result);
2544 	}
2545 
2546 	mutex_enter(&job->job_mutex);
2547 	if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
2548 		mutex_exit(&job->job_mutex);
2549 		mutex_enter(&fcsm->sm_mutex);
2550 		ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
2551 		fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
2552 
2553 		/* Signal the job thread to process the job */
2554 		cv_signal(&fcsm->sm_job_cv);
2555 		mutex_exit(&fcsm->sm_mutex);
2556 		mutex_enter(&job->job_mutex);
2557 	}
2558 
2559 	if (job->job_flags & FCSM_JOBFLAG_SYNC) {
2560 		mutex_exit(&job->job_mutex);
2561 		sema_v(&job->job_sema);
2562 	} else {
2563 		mutex_exit(&job->job_mutex);
2564 		/* Async job, free the job structure */
2565 		fcsm_dealloc_job(job);
2566 	}
2567 }
2568 
2569 fcsm_job_t *
2570 fcsm_deque_job(fcsm_t *fcsm)
2571 {
2572 	fcsm_job_t	*job;
2573 
2574 	ASSERT(MUTEX_HELD(&fcsm->sm_mutex));
2575 
2576 	if (fcsm->sm_job_head == NULL) {
2577 		ASSERT(fcsm->sm_job_tail == NULL);
2578 		job = NULL;
2579 	} else {
2580 		ASSERT(fcsm->sm_job_tail != NULL);
2581 		job = fcsm->sm_job_head;
2582 		if (job->job_next == NULL) {
2583 			ASSERT(fcsm->sm_job_tail == job);
2584 			fcsm->sm_job_tail = NULL;
2585 		}
2586 		fcsm->sm_job_head = job->job_next;
2587 		job->job_next = NULL;
2588 	}
2589 
2590 	return (job);
2591 }
2592 
2593 
2594 /* Dedicated per port thread to process various commands */
2595 static void
2596 fcsm_job_thread(fcsm_t *fcsm)
2597 {
2598 	fcsm_job_t	*job;
2599 
2600 	ASSERT(fcsm != NULL);
2601 #ifndef __lock_lint
2602 	CALLB_CPR_INIT(&fcsm->sm_cpr_info, &fcsm->sm_mutex,
2603 	    callb_generic_cpr, "fcsm_job_thread");
2604 #endif /* __lock_lint */
2605 
2606 	for (;;) {
2607 		mutex_enter(&fcsm->sm_mutex);
2608 
2609 		while (fcsm->sm_job_head == NULL ||
2610 		    fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD) {
2611 			CALLB_CPR_SAFE_BEGIN(&fcsm->sm_cpr_info);
2612 			cv_wait(&fcsm->sm_job_cv, &fcsm->sm_mutex);
2613 			CALLB_CPR_SAFE_END(&fcsm->sm_cpr_info, &fcsm->sm_mutex);
2614 		}
2615 
2616 		job = fcsm_deque_job(fcsm);
2617 
2618 		mutex_exit(&fcsm->sm_mutex);
2619 
2620 		mutex_enter(&job->job_mutex);
2621 		if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
2622 			mutex_exit(&job->job_mutex);
2623 
2624 			mutex_enter(&fcsm->sm_mutex);
2625 			ASSERT(!(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD));
2626 			fcsm->sm_flags |= FCSM_SERIALIZE_JOBTHREAD;
2627 			mutex_exit(&fcsm->sm_mutex);
2628 		} else {
2629 			mutex_exit(&job->job_mutex);
2630 		}
2631 
2632 		ASSERT(fcsm->sm_instance == job->job_port_instance);
2633 
2634 		switch (job->job_code) {
2635 		case FCSM_JOB_NONE:
2636 			fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
2637 			    "job_thread: uninitialized job code");
2638 			job->job_result = FC_FAILURE;
2639 			fcsm_jobdone(job);
2640 			break;
2641 
2642 		case FCSM_JOB_THREAD_SHUTDOWN:
2643 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2644 			    "job_thread: job code <JOB PORT SHUTDOWN>"));
2645 
2646 			/*
2647 			 * There should not be any pending jobs, when this
2648 			 * is being called.
2649 			 */
2650 			mutex_enter(&fcsm->sm_mutex);
2651 			ASSERT(fcsm->sm_job_head == NULL);
2652 			ASSERT(fcsm->sm_job_tail == NULL);
2653 			ASSERT(fcsm->sm_retry_head == NULL);
2654 			ASSERT(fcsm->sm_retry_tail == NULL);
2655 			job->job_result = FC_SUCCESS;
2656 #ifndef __lock_lint
2657 			CALLB_CPR_EXIT(&fcsm->sm_cpr_info);
2658 #endif
2659 			/* CPR_EXIT has also dropped the fcsm->sm_mutex */
2660 
2661 			fcsm_jobdone(job);
2662 			thread_exit();
2663 			/* NOTREACHED */
2664 			break;
2665 
2666 		case FCSM_JOB_LOGIN_NAME_SERVER:
2667 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2668 			    "job_thread: job code <LOGIN_NAME_SERVER>"));
2669 			job->job_result = FC_SUCCESS;
2670 			fcsm_jobdone(job);
2671 			break;
2672 
2673 		case FCSM_JOB_LOGIN_MGMT_SERVER:
2674 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2675 			    "job_thread: job code <LOGIN_MGMT_SERVER>"));
2676 			fcsm_job_login_mgmt_server(job);
2677 			break;
2678 
2679 		case FCSM_JOB_CT_PASSTHRU:
2680 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2681 			    "job_thread: job code <CT_PASSTHRU>"));
2682 			fcsm_job_ct_passthru(job);
2683 			break;
2684 
2685 		default:
2686 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2687 			    "job_thread: job code <UNKNOWN>"));
2688 			job->job_result = FC_FAILURE;
2689 			fcsm_jobdone(job);
2690 			break;
2691 		}
2692 	}
2693 
2694 	/* NOTREACHED */
2695 }
2696 
2697 
2698 static void
2699 fcsm_ct_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, fc_ct_aiu_t *req_iu, size_t req_len,
2700     void (*comp_func)())
2701 {
2702 	fc_packet_t	*pkt;
2703 
2704 	pkt = cmd->cmd_fp_pkt;
2705 	ASSERT(pkt != NULL);
2706 
2707 	ASSERT(req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE ||
2708 	    (req_iu->aiu_header.ct_fcstype == FCSTYPE_DIRECTORY &&
2709 	    req_iu->aiu_header.ct_fcssubtype == FCSSUB_DS_NAME_SERVER));
2710 
2711 
2712 	/* Set the pkt d_id properly */
2713 	if (req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE) {
2714 		pkt->pkt_cmd_fhdr.d_id	= FS_MANAGEMENT_SERVER;
2715 	} else {
2716 		pkt->pkt_cmd_fhdr.d_id	= FS_NAME_SERVER;
2717 	}
2718 
2719 	pkt->pkt_cmd_fhdr.r_ctl	= R_CTL_UNSOL_CONTROL;
2720 	pkt->pkt_cmd_fhdr.rsvd	= 0;
2721 	pkt->pkt_cmd_fhdr.s_id	= fcsm->sm_sid;
2722 	pkt->pkt_cmd_fhdr.type	= FC_TYPE_FC_SERVICES;
2723 	pkt->pkt_cmd_fhdr.f_ctl	= F_CTL_SEQ_INITIATIVE |
2724 	    F_CTL_FIRST_SEQ | F_CTL_END_SEQ;
2725 	pkt->pkt_cmd_fhdr.seq_id = 0;
2726 	pkt->pkt_cmd_fhdr.df_ctl = 0;
2727 	pkt->pkt_cmd_fhdr.seq_cnt = 0;
2728 	pkt->pkt_cmd_fhdr.ox_id = 0xffff;
2729 	pkt->pkt_cmd_fhdr.rx_id = 0xffff;
2730 	pkt->pkt_cmd_fhdr.ro	= 0;
2731 
2732 	pkt->pkt_timeout	= FCSM_MS_TIMEOUT;
2733 	pkt->pkt_comp		= comp_func;
2734 
2735 	FCSM_REP_WR(pkt->pkt_cmd_acc, req_iu, pkt->pkt_cmd, req_len);
2736 
2737 	cmd->cmd_transport = fc_ulp_transport;
2738 }
2739 
2740 static void
2741 fcsm_ct_intr(fcsm_cmd_t *cmd)
2742 {
2743 	fc_packet_t	*pkt;
2744 	fcsm_job_t	*job;
2745 	fcio_t		*fcio;
2746 
2747 	pkt = cmd->cmd_fp_pkt;
2748 	job = cmd->cmd_job;
2749 	ASSERT(job != NULL);
2750 
2751 	fcio = job->job_arg;
2752 	ASSERT(fcio != NULL);
2753 
2754 	if (pkt->pkt_state != FC_PKT_SUCCESS) {
2755 		fcsm_display(CE_WARN, SM_LOG, cmd->cmd_fcsm, pkt,
2756 		    "ct_intr: CT command <0x%x> to did 0x%x failed",
2757 		    ((fc_ct_aiu_t *)fcio->fcio_ibuf)->aiu_header.ct_cmdrsp,
2758 		    pkt->pkt_cmd_fhdr.d_id);
2759 	} else {
2760 		/* Get the CT response payload */
2761 		FCSM_REP_RD(pkt->pkt_resp_acc, fcio->fcio_obuf,
2762 		    pkt->pkt_resp, fcio->fcio_olen);
2763 	}
2764 
2765 	job->job_result =
2766 	    fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
2767 
2768 	fcsm_free_cmd(cmd);
2769 
2770 	fcsm_jobdone(job);
2771 }
2772 
2773 
2774 static void
2775 fcsm_job_ct_passthru(fcsm_job_t *job)
2776 {
2777 	fcsm_t		*fcsm;
2778 	fcio_t		*fcio;
2779 	fcsm_cmd_t	*cmd;
2780 	int		status;
2781 	fc_ct_header_t	*ct_header;
2782 
2783 	ASSERT(job != NULL);
2784 	ASSERT(job->job_port_instance != -1);
2785 
2786 	job->job_result = FC_FAILURE;
2787 	fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2788 	if (fcsm == NULL) {
2789 		fcsm_jobdone(job);
2790 		return;
2791 	}
2792 
2793 	/*
2794 	 * Process the CT Passthru job only if port is attached
2795 	 * to a FABRIC.
2796 	 */
2797 	if (!FC_TOP_EXTERNAL(fcsm->sm_port_top)) {
2798 		FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2799 		    "job_ct_passthru: end (non-fabric port)"));
2800 		job->job_result = FC_BADDEV;
2801 		fcsm_jobdone(job);
2802 		return;
2803 	}
2804 
2805 	fcio = job->job_arg;
2806 	ASSERT(fcio != NULL);
2807 
2808 	/*
2809 	 * If it is NOT a Management Seriver (MS) or Name Server (NS) command
2810 	 * then complete the command with failure.
2811 	 */
2812 	ct_header = (fc_ct_header_t *)fcio->fcio_ibuf;
2813 
2814 	/*
2815 	 * According to libHBAAPI spec, CT header from libHBAAPI would always
2816 	 * be big endian, so we must swap CT header before continue in little
2817 	 * endian platforms.
2818 	 */
2819 	mutex_enter(&job->job_mutex);
2820 	if (!(job->job_flags & FCSM_JOBFLAG_CTHEADER_BE)) {
2821 		job->job_flags |= FCSM_JOBFLAG_CTHEADER_BE;
2822 		*((uint32_t *)((uint32_t *)ct_header + 0)) =
2823 		    BE_32(*((uint32_t *)((uint32_t *)ct_header + 0)));
2824 		*((uint32_t *)((uint32_t *)ct_header + 1)) =
2825 		    BE_32(*((uint32_t *)((uint32_t *)ct_header + 1)));
2826 		*((uint32_t *)((uint32_t *)ct_header + 2)) =
2827 		    BE_32(*((uint32_t *)((uint32_t *)ct_header + 2)));
2828 		*((uint32_t *)((uint32_t *)ct_header + 3)) =
2829 		    BE_32(*((uint32_t *)((uint32_t *)ct_header + 3)));
2830 	}
2831 	mutex_exit(&job->job_mutex);
2832 
2833 	if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
2834 		FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2835 		    "job_ct_passthru: Management Server Cmd"));
2836 	} else if (ct_header->ct_fcstype == FCSTYPE_DIRECTORY) {
2837 		FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2838 		    "job_ct_passthru: Name Server Cmd"));
2839 	} else {
2840 		FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2841 		    "job_ct_passthru: Unsupported Destination "
2842 		    "gs_type <0x%x> gs_subtype <0x%x>",
2843 		    ct_header->ct_fcstype, ct_header->ct_fcssubtype));
2844 	}
2845 
2846 	if (ct_header->ct_fcstype != FCSTYPE_MGMTSERVICE &&
2847 	    (ct_header->ct_fcstype != FCSTYPE_DIRECTORY ||
2848 	    ct_header->ct_fcssubtype != FCSSUB_DS_NAME_SERVER)) {
2849 		FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2850 		    "job_ct_passthru: end (Not a Name Server OR "
2851 		    "Mgmt Server Cmd)"));
2852 		job->job_result = FC_BADCMD;
2853 		fcsm_jobdone(job);
2854 		return;
2855 	}
2856 
2857 	/*
2858 	 * If it is an MS command and we are not logged in to the management
2859 	 * server, then start the login and requeue the command.
2860 	 * If login to management server is in progress, then reque the
2861 	 * command to wait for login to complete.
2862 	 */
2863 	mutex_enter(&fcsm->sm_mutex);
2864 	if ((ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) &&
2865 	    !(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN)) {
2866 		mutex_exit(&fcsm->sm_mutex);
2867 		if (fcsm_login_and_process_job(fcsm, job) != FC_SUCCESS) {
2868 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2869 			    "job_ct_passthru: perform login failed"));
2870 			job->job_result = FC_FAILURE;
2871 			fcsm_jobdone(job);
2872 		}
2873 		return;
2874 	}
2875 	mutex_exit(&fcsm->sm_mutex);
2876 
2877 	/*
2878 	 * We are already logged in to the management server.
2879 	 * Issue the CT Passthru command
2880 	 */
2881 	cmd = fcsm_alloc_cmd(fcsm, fcio->fcio_ilen, fcio->fcio_olen, KM_SLEEP);
2882 	if (cmd == NULL) {
2883 		job->job_result = FC_NOMEM;
2884 		fcsm_jobdone(job);
2885 		return;
2886 	}
2887 
2888 	FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
2889 	    fcsm_max_cmd_retries, fcsm_ct_intr);
2890 
2891 	fcsm_ct_init(fcsm, cmd, (fc_ct_aiu_t *)fcio->fcio_ibuf, fcio->fcio_ilen,
2892 	    fcsm_pkt_common_intr);
2893 
2894 	if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
2895 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
2896 		    "job_ct_passthru: issue CT Passthru failed, status 0x%x",
2897 		    status));
2898 		job->job_result = status;
2899 		fcsm_free_cmd(cmd);
2900 		fcsm_jobdone(job);
2901 		return;
2902 	}
2903 }
2904 
2905 static int
2906 fcsm_login_and_process_job(fcsm_t *fcsm, fcsm_job_t *orig_job)
2907 {
2908 	fcsm_job_t	*login_job;
2909 #ifdef DEBUG
2910 	int		status;
2911 #endif /* DEBUG */
2912 
2913 	if (orig_job->job_code != FCSM_JOB_CT_PASSTHRU) {
2914 		return (FC_FAILURE);
2915 	}
2916 
2917 	FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2918 	    "login_and_process_job: start login."));
2919 
2920 	mutex_enter(&fcsm->sm_mutex);
2921 	if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) {
2922 		/*
2923 		 * Directory server login completed just now, while the
2924 		 * mutex was dropped. Just queue the command again for
2925 		 * processing.
2926 		 */
2927 		mutex_exit(&fcsm->sm_mutex);
2928 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2929 		    "login_and_process_job: got job 0x%p. login just "
2930 		    "completed", (void *)orig_job));
2931 		fcsm_enque_job(fcsm, orig_job, 0);
2932 		return (FC_SUCCESS);
2933 	}
2934 
2935 	if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG) {
2936 		/*
2937 		 * Ideally we shouldn't have come here, since login
2938 		 * job has the serialize flag set.
2939 		 * Anyway, put the command back on the queue.
2940 		 */
2941 		mutex_exit(&fcsm->sm_mutex);
2942 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2943 		    "login_and_process_job: got job 0x%p while login to "
2944 		    "management server in progress", (void *)orig_job));
2945 		fcsm_enque_job(fcsm, orig_job, 0);
2946 		return (FC_SUCCESS);
2947 	}
2948 
2949 	fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGIN_IN_PROG;
2950 	mutex_exit(&fcsm->sm_mutex);
2951 
2952 	login_job = fcsm_alloc_job(KM_SLEEP);
2953 	ASSERT(login_job != NULL);
2954 
2955 	/*
2956 	 * Mark the login job as SERIALIZE, so that all other jobs will
2957 	 * be processed after completing the login.
2958 	 * Save the original job (CT Passthru job) in the caller private
2959 	 * field in the job structure, so that CT command can be issued
2960 	 * after login has completed.
2961 	 */
2962 	fcsm_init_job(login_job, fcsm->sm_instance, FCSM_JOB_LOGIN_MGMT_SERVER,
2963 	    FCSM_JOBFLAG_ASYNC | FCSM_JOBFLAG_SERIALIZE,
2964 	    (opaque_t)NULL, (opaque_t)orig_job, fcsm_login_ms_comp, NULL);
2965 	orig_job->job_priv = (void *)login_job;
2966 
2967 #ifdef DEBUG
2968 	status = fcsm_process_job(login_job, 1);
2969 	ASSERT(status == FC_SUCCESS);
2970 #else /* DEBUG */
2971 	(void) fcsm_process_job(login_job, 1);
2972 #endif /* DEBUG */
2973 	return (FC_SUCCESS);
2974 }
2975 
2976 
2977 /* ARGSUSED */
2978 static void
2979 fcsm_login_ms_comp(opaque_t comp_arg, fcsm_job_t *login_job, int result)
2980 {
2981 	fcsm_t		*fcsm;
2982 	fcsm_job_t	*orig_job;
2983 
2984 	ASSERT(login_job != NULL);
2985 
2986 	orig_job = (fcsm_job_t *)login_job->job_caller_priv;
2987 
2988 	ASSERT(orig_job != NULL);
2989 	ASSERT(orig_job->job_priv == (void *)login_job);
2990 	orig_job->job_priv = NULL;
2991 
2992 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
2993 	    "login_ms_comp: result 0x%x", login_job->job_result));
2994 
2995 	/* Set the login flag in the per port fcsm structure */
2996 	ASSERT(login_job->job_port_instance == orig_job->job_port_instance);
2997 	fcsm = ddi_get_soft_state(fcsm_state, login_job->job_port_instance);
2998 	ASSERT(fcsm != NULL);
2999 
3000 	mutex_enter(&fcsm->sm_mutex);
3001 	ASSERT((fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) == 0);
3002 	ASSERT(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG);
3003 	fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGIN_IN_PROG;
3004 	if (login_job->job_result != FC_SUCCESS) {
3005 		caddr_t	msg;
3006 
3007 		/*
3008 		 * Login failed. Complete the original job with FC_LOGINREQ
3009 		 * status. Retry of that job will cause login to be
3010 		 * retried.
3011 		 */
3012 		mutex_exit(&fcsm->sm_mutex);
3013 		orig_job->job_result = FC_LOGINREQ;
3014 		fcsm_jobdone(orig_job);
3015 
3016 		(void) fc_ulp_error(login_job->job_result, &msg);
3017 		fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
3018 		    "login_ms_comp: Management server login failed: <%s>", msg);
3019 		return;
3020 	}
3021 	fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGGED_IN;
3022 	mutex_exit(&fcsm->sm_mutex);
3023 
3024 	/*
3025 	 * Queue the original job at the head of the queue for processing.
3026 	 */
3027 	fcsm_enque_job(fcsm, orig_job, 1);
3028 }
3029 
3030 
3031 static void
3032 fcsm_els_init(fcsm_cmd_t *cmd, uint32_t d_id)
3033 {
3034 	fc_packet_t	*pkt;
3035 	fcsm_t		*fcsm;
3036 
3037 	fcsm = cmd->cmd_fcsm;
3038 	pkt = cmd->cmd_fp_pkt;
3039 	ASSERT(fcsm != NULL && pkt != NULL);
3040 
3041 	pkt->pkt_cmd_fhdr.r_ctl	= R_CTL_ELS_REQ;
3042 	pkt->pkt_cmd_fhdr.d_id	= d_id;
3043 	pkt->pkt_cmd_fhdr.rsvd	= 0;
3044 	pkt->pkt_cmd_fhdr.s_id	= fcsm->sm_sid;
3045 	pkt->pkt_cmd_fhdr.type	= FC_TYPE_EXTENDED_LS;
3046 	pkt->pkt_cmd_fhdr.f_ctl	= F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
3047 	pkt->pkt_cmd_fhdr.seq_id = 0;
3048 	pkt->pkt_cmd_fhdr.df_ctl = 0;
3049 	pkt->pkt_cmd_fhdr.seq_cnt = 0;
3050 	pkt->pkt_cmd_fhdr.ox_id = 0xffff;
3051 	pkt->pkt_cmd_fhdr.rx_id = 0xffff;
3052 	pkt->pkt_cmd_fhdr.ro	= 0;
3053 
3054 	pkt->pkt_timeout	= FCSM_ELS_TIMEOUT;
3055 }
3056 
3057 
3058 static int
3059 fcsm_xlogi_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, uint32_t d_id,
3060     void (*comp_func)(), uchar_t ls_code)
3061 {
3062 	ls_code_t	payload;
3063 	fc_packet_t	*pkt;
3064 	la_els_logi_t	*login_params;
3065 	int		status;
3066 
3067 	login_params = (la_els_logi_t *)
3068 	    kmem_zalloc(sizeof (la_els_logi_t), KM_SLEEP);
3069 	if (login_params == NULL) {
3070 		return (FC_NOMEM);
3071 	}
3072 
3073 	status = fc_ulp_get_port_login_params(fcsm->sm_port_info.port_handle,
3074 	    login_params);
3075 	if (status != FC_SUCCESS) {
3076 		kmem_free(login_params, sizeof (la_els_logi_t));
3077 		return (status);
3078 	}
3079 
3080 	pkt = cmd->cmd_fp_pkt;
3081 
3082 	fcsm_els_init(cmd, d_id);
3083 	pkt->pkt_comp = comp_func;
3084 
3085 	payload.ls_code = ls_code;
3086 	payload.mbz = 0;
3087 
3088 	FCSM_REP_WR(pkt->pkt_cmd_acc, login_params,
3089 	    pkt->pkt_cmd, sizeof (la_els_logi_t));
3090 	FCSM_REP_WR(pkt->pkt_cmd_acc, &payload,
3091 	    pkt->pkt_cmd, sizeof (payload));
3092 
3093 	cmd->cmd_transport = fc_ulp_issue_els;
3094 
3095 	kmem_free(login_params, sizeof (la_els_logi_t));
3096 
3097 	return (FC_SUCCESS);
3098 }
3099 
3100 static void
3101 fcsm_xlogi_intr(fcsm_cmd_t *cmd)
3102 {
3103 	fc_packet_t	*pkt;
3104 	fcsm_job_t	*job;
3105 	fcsm_t		*fcsm;
3106 
3107 	pkt = cmd->cmd_fp_pkt;
3108 	job = cmd->cmd_job;
3109 	ASSERT(job != NULL);
3110 
3111 	fcsm = cmd->cmd_fcsm;
3112 	ASSERT(fcsm != NULL);
3113 
3114 	if (pkt->pkt_state != FC_PKT_SUCCESS) {
3115 		fcsm_display(CE_WARN, SM_LOG, fcsm, pkt,
3116 		    "xlogi_intr: login to DID 0x%x failed",
3117 		    pkt->pkt_cmd_fhdr.d_id);
3118 	} else {
3119 		/* Get the Login parameters of the Management Server */
3120 		FCSM_REP_RD(pkt->pkt_resp_acc, &fcsm->sm_ms_service_params,
3121 		    pkt->pkt_resp, sizeof (la_els_logi_t));
3122 	}
3123 
3124 
3125 	job->job_result =
3126 	    fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
3127 
3128 	fcsm_free_cmd(cmd);
3129 
3130 	fcsm_jobdone(job);
3131 }
3132 
3133 static void
3134 fcsm_job_login_mgmt_server(fcsm_job_t *job)
3135 {
3136 	fcsm_t		*fcsm;
3137 	fcsm_cmd_t	*cmd;
3138 	int		status;
3139 
3140 	ASSERT(job != NULL);
3141 	ASSERT(job->job_port_instance != -1);
3142 
3143 	fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
3144 	if (fcsm == NULL) {
3145 		job->job_result = FC_NOMEM;
3146 		fcsm_jobdone(job);
3147 		return;
3148 	}
3149 
3150 	/*
3151 	 * Issue the  Login command to the management server.
3152 	 */
3153 	cmd = fcsm_alloc_cmd(fcsm, sizeof (la_els_logi_t),
3154 	    sizeof (la_els_logi_t), KM_SLEEP);
3155 	if (cmd == NULL) {
3156 		job->job_result = FC_NOMEM;
3157 		fcsm_jobdone(job);
3158 		return;
3159 	}
3160 
3161 	FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
3162 	    fcsm_max_cmd_retries, fcsm_xlogi_intr);
3163 
3164 	status = fcsm_xlogi_init(fcsm, cmd, FS_MANAGEMENT_SERVER,
3165 	    fcsm_pkt_common_intr, LA_ELS_PLOGI);
3166 
3167 	if (status != FC_SUCCESS) {
3168 		FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
3169 		    "job_login_mgmt_server: plogi init failed. status 0x%x",
3170 		    status));
3171 		job->job_result = status;
3172 		fcsm_free_cmd(cmd);
3173 		fcsm_jobdone(job);
3174 		return;
3175 	}
3176 
3177 	if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
3178 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3179 		    "job_ct_passthru: issue login cmd failed, status 0x%x",
3180 		    status));
3181 		job->job_result = status;
3182 		fcsm_free_cmd(cmd);
3183 		fcsm_jobdone(job);
3184 		return;
3185 	}
3186 }
3187 
3188 
3189 int
3190 fcsm_ct_passthru(int instance, fcio_t *fcio, int sleep, int job_flags,
3191     void (*func)(fcio_t *))
3192 {
3193 	fcsm_job_t	*job;
3194 	int		status;
3195 
3196 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3197 	    "ct_passthru: instance 0x%x fcio 0x%p", instance, fcio));
3198 	job = fcsm_alloc_job(sleep);
3199 	ASSERT(sleep == KM_NOSLEEP || job != NULL);
3200 
3201 	fcsm_init_job(job, instance, FCSM_JOB_CT_PASSTHRU, job_flags,
3202 	    (opaque_t)fcio, (opaque_t)func, fcsm_ct_passthru_comp, NULL);
3203 	status = fcsm_process_job(job, 0);
3204 	if (status != FC_SUCCESS) {
3205 		/* Job could not be issued. So free the job and return */
3206 		fcsm_dealloc_job(job);
3207 		return (status);
3208 	}
3209 
3210 	if (job_flags & FCSM_JOBFLAG_SYNC) {
3211 		status = job->job_result;
3212 		fcsm_dealloc_job(job);
3213 	}
3214 
3215 	return (status);
3216 }
3217 
3218 
3219 /* ARGSUSED */
3220 static void
3221 fcsm_ct_passthru_comp(opaque_t comp_arg, fcsm_job_t *job, int result)
3222 {
3223 	ASSERT(job != NULL);
3224 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3225 	    "ct_passthru_comp: result 0x%x port 0x%x",
3226 	    job->job_result, job->job_port_instance));
3227 }
3228 
3229 
3230 static void
3231 fcsm_pkt_common_intr(fc_packet_t *pkt)
3232 {
3233 	fcsm_cmd_t	*cmd;
3234 	int		jobstatus;
3235 	fcsm_t		*fcsm;
3236 
3237 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3238 	    "pkt_common_intr"));
3239 
3240 	cmd = (fcsm_cmd_t *)pkt->pkt_ulp_private;
3241 	ASSERT(cmd != NULL);
3242 
3243 	if (pkt->pkt_state == FC_PKT_SUCCESS) {
3244 		/* Command completed successfully. Just complete the command */
3245 		cmd->cmd_comp(cmd);
3246 		return;
3247 	}
3248 
3249 	fcsm = cmd->cmd_fcsm;
3250 	ASSERT(fcsm != NULL);
3251 
3252 	fcsm_display(CE_WARN, SM_LOG, cmd->cmd_fcsm, pkt,
3253 	    "fc packet to DID 0x%x failed for pkt 0x%p",
3254 	    pkt->pkt_cmd_fhdr.d_id, pkt);
3255 
3256 	mutex_enter(&fcsm->sm_mutex);
3257 	if (fcsm->sm_flags & FCSM_LINK_DOWN) {
3258 		/*
3259 		 * No need to retry the command. The link previously
3260 		 * suffered an offline  timeout.
3261 		 */
3262 		mutex_exit(&fcsm->sm_mutex);
3263 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3264 		    "pkt_common_intr: end. Link is down"));
3265 		cmd->cmd_comp(cmd);
3266 		return;
3267 	}
3268 	mutex_exit(&fcsm->sm_mutex);
3269 
3270 	jobstatus = fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
3271 	if (jobstatus == FC_LOGINREQ) {
3272 		/*
3273 		 * Login to the destination is required. No need to
3274 		 * retry this cmd again.
3275 		 */
3276 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3277 		    "pkt_common_intr: end. LOGIN required"));
3278 		cmd->cmd_comp(cmd);
3279 		return;
3280 	}
3281 
3282 	switch (pkt->pkt_state) {
3283 	case FC_PKT_PORT_OFFLINE:
3284 	case FC_PKT_LOCAL_RJT:
3285 	case FC_PKT_TIMEOUT: {
3286 		uchar_t		pkt_state;
3287 
3288 		pkt_state = pkt->pkt_state;
3289 		cmd->cmd_retry_interval = fcsm_retry_interval;
3290 		if (fcsm_retry_cmd(cmd) != 0) {
3291 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
3292 			    cmd->cmd_fcsm, NULL,
3293 			    "common_intr: max retries(%d) reached, status 0x%x",
3294 			    cmd->cmd_retry_count));
3295 
3296 			/*
3297 			 * Restore the pkt_state to the actual failure status
3298 			 * received at the time of pkt completion.
3299 			 */
3300 			pkt->pkt_state = pkt_state;
3301 			pkt->pkt_reason = 0;
3302 			cmd->cmd_comp(cmd);
3303 		} else {
3304 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
3305 			    cmd->cmd_fcsm, NULL,
3306 			    "pkt_common_intr: retry(%d) on pkt state (0x%x)",
3307 			    cmd->cmd_retry_count, pkt_state));
3308 		}
3309 		break;
3310 	}
3311 	default:
3312 		cmd->cmd_comp(cmd);
3313 		break;
3314 	}
3315 }
3316 
3317 static int
3318 fcsm_issue_cmd(fcsm_cmd_t *cmd)
3319 {
3320 	fc_packet_t	*pkt;
3321 	fcsm_t		*fcsm;
3322 	int		status;
3323 
3324 	pkt = cmd->cmd_fp_pkt;
3325 	fcsm = cmd->cmd_fcsm;
3326 
3327 	/* Explicitly invalidate this field till fcsm decides to use it */
3328 	pkt->pkt_ulp_rscn_infop = NULL;
3329 
3330 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3331 	    "issue_cmd: entry"));
3332 
3333 	ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3334 	mutex_enter(&fcsm->sm_mutex);
3335 	if (fcsm->sm_flags & FCSM_LINK_DOWN) {
3336 		/*
3337 		 * Update the pkt_state/pkt_reason appropriately.
3338 		 * Caller of this function can decide whether to call
3339 		 * 'pkt->pkt_comp' or use the 'status' returned by this func.
3340 		 */
3341 		mutex_exit(&fcsm->sm_mutex);
3342 		pkt->pkt_state = FC_PKT_PORT_OFFLINE;
3343 		pkt->pkt_reason = FC_REASON_OFFLINE;
3344 		return (FC_OFFLINE);
3345 	}
3346 	mutex_exit(&fcsm->sm_mutex);
3347 
3348 	ASSERT(cmd->cmd_transport != NULL);
3349 	status = cmd->cmd_transport(fcsm->sm_port_info.port_handle, pkt);
3350 	if (status != FC_SUCCESS) {
3351 		switch (status) {
3352 		case FC_LOGINREQ:
3353 			/*
3354 			 * No need to retry. Return the cause of failure.
3355 			 * Also update the pkt_state/pkt_reason. Caller of
3356 			 * this function can decide, whether to call
3357 			 * 'pkt->pkt_comp' or use the 'status' code returned
3358 			 * by this function.
3359 			 */
3360 			pkt->pkt_state = FC_PKT_LOCAL_RJT;
3361 			pkt->pkt_reason = FC_REASON_LOGIN_REQUIRED;
3362 			break;
3363 
3364 		case FC_DEVICE_BUSY_NEW_RSCN:
3365 			/*
3366 			 * There was a newer RSCN than what fcsm knows about.
3367 			 * So, just retry again
3368 			 */
3369 			cmd->cmd_retry_count = 0;
3370 			/*FALLTHROUGH*/
3371 		case FC_OFFLINE:
3372 		case FC_STATEC_BUSY:
3373 			/*
3374 			 * TODO: set flag, so that command is retried after
3375 			 * port is back online.
3376 			 * FALL Through for now.
3377 			 */
3378 
3379 		case FC_TRAN_BUSY:
3380 		case FC_NOMEM:
3381 		case FC_DEVICE_BUSY:
3382 			cmd->cmd_retry_interval = fcsm_retry_interval;
3383 			if (fcsm_retry_cmd(cmd) != 0) {
3384 				FCSM_DEBUG(SMDL_TRACE,
3385 				    (CE_WARN, SM_LOG, fcsm, NULL,
3386 				    "issue_cmd: max retries (%d) reached",
3387 				    cmd->cmd_retry_count));
3388 
3389 				/*
3390 				 * status variable is not changed here.
3391 				 * Return the cause of the original
3392 				 * cmd_transport failure.
3393 				 * Update the pkt_state/pkt_reason. Caller
3394 				 * of this function can decide whether to
3395 				 * call 'pkt->pkt_comp' or use the 'status'
3396 				 * code returned by this function.
3397 				 */
3398 				pkt->pkt_state = FC_PKT_TRAN_BSY;
3399 				pkt->pkt_reason = 0;
3400 			} else {
3401 				FCSM_DEBUG(SMDL_TRACE,
3402 				    (CE_WARN, SM_LOG, fcsm, NULL,
3403 				    "issue_cmd: retry (%d) on fc status (0x%x)",
3404 				    cmd->cmd_retry_count, status));
3405 
3406 				status = FC_SUCCESS;
3407 			}
3408 			break;
3409 
3410 		default:
3411 			FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
3412 			    "issue_cmd: failure status 0x%x", status));
3413 
3414 			pkt->pkt_state = FC_PKT_TRAN_ERROR;
3415 			pkt->pkt_reason = 0;
3416 			break;
3417 
3418 
3419 		}
3420 	}
3421 
3422 	return (status);
3423 }
3424 
3425 
3426 static int
3427 fcsm_retry_cmd(fcsm_cmd_t *cmd)
3428 {
3429 	if (cmd->cmd_retry_count < cmd->cmd_max_retries) {
3430 		cmd->cmd_retry_count++;
3431 		fcsm_enque_cmd(cmd->cmd_fcsm, cmd);
3432 		return (0);
3433 	}
3434 
3435 	return (1);
3436 }
3437 
3438 static void
3439 fcsm_enque_cmd(fcsm_t *fcsm, fcsm_cmd_t *cmd)
3440 {
3441 	ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3442 
3443 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "enque_cmd"));
3444 
3445 	cmd->cmd_next = NULL;
3446 	mutex_enter(&fcsm->sm_mutex);
3447 	if (fcsm->sm_retry_tail) {
3448 		ASSERT(fcsm->sm_retry_head != NULL);
3449 		fcsm->sm_retry_tail->cmd_next = cmd;
3450 		fcsm->sm_retry_tail = cmd;
3451 	} else {
3452 		ASSERT(fcsm->sm_retry_tail == NULL);
3453 		fcsm->sm_retry_head = fcsm->sm_retry_tail = cmd;
3454 
3455 		/* Schedule retry thread, if not already running */
3456 		if (fcsm->sm_retry_tid == NULL) {
3457 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3458 			    "enque_cmd: schedule retry thread"));
3459 			fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
3460 			    (caddr_t)fcsm, fcsm_retry_ticks);
3461 		}
3462 	}
3463 	mutex_exit(&fcsm->sm_mutex);
3464 }
3465 
3466 
3467 static fcsm_cmd_t *
3468 fcsm_deque_cmd(fcsm_t *fcsm)
3469 {
3470 	fcsm_cmd_t	*cmd;
3471 
3472 	ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3473 
3474 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "deque_cmd"));
3475 
3476 	mutex_enter(&fcsm->sm_mutex);
3477 	if (fcsm->sm_retry_head == NULL) {
3478 		ASSERT(fcsm->sm_retry_tail == NULL);
3479 		cmd = NULL;
3480 	} else {
3481 		cmd = fcsm->sm_retry_head;
3482 		fcsm->sm_retry_head = cmd->cmd_next;
3483 		if (fcsm->sm_retry_head == NULL) {
3484 			fcsm->sm_retry_tail = NULL;
3485 		}
3486 		cmd->cmd_next = NULL;
3487 	}
3488 	mutex_exit(&fcsm->sm_mutex);
3489 
3490 	return (cmd);
3491 }
3492 
3493 static void
3494 fcsm_retry_timeout(void *handle)
3495 {
3496 	fcsm_t		*fcsm;
3497 	fcsm_cmd_t	*curr_tail;
3498 	fcsm_cmd_t	*cmd;
3499 	int		done = 0;
3500 	int		linkdown;
3501 
3502 	fcsm = (fcsm_t *)handle;
3503 
3504 	FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "retry_timeout"));
3505 
3506 	/*
3507 	 * If retry cmd queue is suspended, then go away.
3508 	 * This retry thread will be restarted, when cmd queue resumes.
3509 	 */
3510 	mutex_enter(&fcsm->sm_mutex);
3511 	if (fcsm->sm_flags & FCSM_CMD_RETRY_Q_SUSPENDED) {
3512 		/*
3513 		 * Clear the retry_tid, to indicate that this routine is not
3514 		 * currently being rescheduled.
3515 		 */
3516 		fcsm->sm_retry_tid = (timeout_id_t)NULL;
3517 		mutex_exit(&fcsm->sm_mutex);
3518 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3519 		    "retry_timeout: end. No processing. "
3520 		    "Queue is currently suspended for this instance"));
3521 		return;
3522 	}
3523 
3524 	linkdown = (fcsm->sm_flags & FCSM_LINK_DOWN) ? 1 : 0;
3525 
3526 	/*
3527 	 * Save the curr_tail, so that we only process the commands
3528 	 * which are in the queue at this time.
3529 	 */
3530 	curr_tail = fcsm->sm_retry_tail;
3531 	mutex_exit(&fcsm->sm_mutex);
3532 
3533 	/*
3534 	 * Check for done flag before dequeing the command.
3535 	 * Dequeing before checking the done flag will cause a command
3536 	 * to be lost.
3537 	 */
3538 	while ((!done) && ((cmd = fcsm_deque_cmd(fcsm)) != NULL)) {
3539 
3540 		if (cmd == curr_tail) {
3541 			done = 1;
3542 		}
3543 
3544 		cmd->cmd_retry_interval -= fcsm_retry_ticker;
3545 
3546 		if (linkdown) {
3547 			fc_packet_t *pkt;
3548 
3549 			/*
3550 			 * No need to retry the command. The link has
3551 			 * suffered an offline  timeout.
3552 			 */
3553 			pkt = cmd->cmd_fp_pkt;
3554 			pkt->pkt_state = FC_PKT_PORT_OFFLINE;
3555 			pkt->pkt_reason = FC_REASON_OFFLINE;
3556 			pkt->pkt_comp(pkt);
3557 			continue;
3558 		}
3559 
3560 		if (cmd->cmd_retry_interval <= 0) {
3561 			/* Retry the command */
3562 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3563 			    "retry_timeout: issue cmd 0x%p", (void *)cmd));
3564 			if (fcsm_issue_cmd(cmd) != FC_SUCCESS) {
3565 				cmd->cmd_fp_pkt->pkt_comp(cmd->cmd_fp_pkt);
3566 			}
3567 		} else {
3568 			/*
3569 			 * Put the command back on the queue. Retry time
3570 			 * has not yet reached.
3571 			 */
3572 			FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3573 			    "retry_timeout: queue cmd 0x%p", (void *)cmd));
3574 			fcsm_enque_cmd(fcsm, cmd);
3575 		}
3576 	}
3577 
3578 	mutex_enter(&fcsm->sm_mutex);
3579 	if (fcsm->sm_retry_head) {
3580 		/* Activate timer */
3581 		fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
3582 		    (caddr_t)fcsm, fcsm_retry_ticks);
3583 		FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3584 		    "retry_timeout: retry thread rescheduled"));
3585 	} else {
3586 		/*
3587 		 * Reset the tid variable. The first thread which queues the
3588 		 * command, will restart the timer.
3589 		 */
3590 		fcsm->sm_retry_tid = (timeout_id_t)NULL;
3591 	}
3592 	mutex_exit(&fcsm->sm_mutex);
3593 }
3594