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