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