Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / fibre-channel / ulp / fcsm.c
blobd200d878bdd623dd09bd515af4fe415a2847a5b8
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * fcsm - ULP Module for Fibre Channel SAN Management
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>
40 /* Definitions */
41 #define FCSM_VERSION "20090729-1.28"
42 #define FCSM_NAME_VERSION "SunFC FCSM v" FCSM_VERSION
44 /* Global Variables */
45 static char fcsm_name[] = "FCSM";
46 static void *fcsm_state = NULL;
47 static kmutex_t fcsm_global_mutex;
48 static uint32_t fcsm_flag = FCSM_IDLE;
49 static dev_info_t *fcsm_dip = NULL;
50 static fcsm_t *fcsm_port_head = NULL;
51 static kmem_cache_t *fcsm_job_cache = NULL;
52 static int fcsm_num_attaching = 0;
53 static int fcsm_num_detaching = 0;
54 static int fcsm_detached = 0;
56 static int fcsm_max_cmd_retries = FCSM_MAX_CMD_RETRIES;
57 static int fcsm_retry_interval = FCSM_RETRY_INTERVAL;
58 static int fcsm_retry_ticker = FCSM_RETRY_TICKER;
59 static int fcsm_offline_ticker = FCSM_OFFLINE_TICKER;
60 static int fcsm_max_job_retries = FCSM_MAX_JOB_RETRIES;
61 static clock_t fcsm_retry_ticks;
62 static clock_t fcsm_offline_ticks;
66 #ifdef DEBUG
67 uint32_t fcsm_debug = 0;
68 #endif
71 /* Character/Block entry points */
72 struct cb_ops fcsm_cb_ops = {
73 fcsm_open, /* open */
74 fcsm_close, /* close */
75 nodev, /* strategy */
76 nodev, /* print */
77 nodev, /* dump */
78 nodev, /* read */
79 nodev, /* write */
80 fcsm_ioctl, /* ioctl */
81 nodev, /* devmap */
82 nodev, /* mmap */
83 nodev, /* segmap */
84 nochpoll, /* poll */
85 ddi_prop_op,
86 NULL, /* streams info */
87 D_NEW | D_MP,
88 CB_REV,
89 nodev, /* aread */
90 nodev /* awrite */
93 struct dev_ops fcsm_ops = {
94 DEVO_REV,
95 0, /* refcnt */
96 fcsm_getinfo, /* get info */
97 nulldev, /* identify (obsolete) */
98 nulldev, /* probe (not required for self-identifying devices) */
99 fcsm_attach, /* attach */
100 fcsm_detach, /* detach */
101 nodev, /* reset */
102 &fcsm_cb_ops, /* char/block entry points structure for leaf drivers */
103 NULL, /* bus operations for nexus driver */
104 NULL /* power management */
108 struct modldrv modldrv = {
109 &mod_driverops,
110 FCSM_NAME_VERSION,
111 &fcsm_ops
114 struct modlinkage modlinkage = {
115 MODREV_1,
116 &modldrv,
117 NULL
120 static fc_ulp_modinfo_t fcsm_modinfo = {
121 &fcsm_modinfo, /* ulp_handle */
122 FCTL_ULP_MODREV_4, /* ulp_rev */
123 FC_TYPE_FC_SERVICES, /* ulp_type */
124 fcsm_name, /* ulp_name */
125 0, /* ulp_statec_mask: get all statec callbacks */
126 fcsm_port_attach, /* ulp_port_attach */
127 fcsm_port_detach, /* ulp_port_detach */
128 fcsm_port_ioctl, /* ulp_port_ioctl */
129 fcsm_els_cb, /* ulp_els_callback */
130 fcsm_data_cb, /* ulp_data_callback */
131 fcsm_statec_cb /* ulp_statec_callback */
134 struct fcsm_xlat_pkt_state {
135 uchar_t xlat_state;
136 int xlat_rval;
137 } fcsm_xlat_pkt_state [] = {
138 { FC_PKT_SUCCESS, FC_SUCCESS },
139 { FC_PKT_REMOTE_STOP, FC_FAILURE },
140 { FC_PKT_LOCAL_RJT, FC_TRANSPORT_ERROR },
141 { FC_PKT_NPORT_RJT, FC_PREJECT },
142 { FC_PKT_FABRIC_RJT, FC_FREJECT },
143 { FC_PKT_LOCAL_BSY, FC_TRAN_BUSY },
144 { FC_PKT_TRAN_BSY, FC_TRAN_BUSY },
145 { FC_PKT_NPORT_BSY, FC_PBUSY },
146 { FC_PKT_FABRIC_BSY, FC_FBUSY },
147 { FC_PKT_LS_RJT, FC_PREJECT },
148 { FC_PKT_BA_RJT, FC_PREJECT },
149 { FC_PKT_TIMEOUT, FC_FAILURE },
150 { FC_PKT_FS_RJT, FC_FAILURE },
151 { FC_PKT_TRAN_ERROR, FC_TRANSPORT_ERROR },
152 { FC_PKT_FAILURE, FC_FAILURE },
153 { FC_PKT_PORT_OFFLINE, FC_OFFLINE },
154 { FC_PKT_ELS_IN_PROGRESS, FC_FAILURE }
157 struct fcsm_xlat_port_state {
158 uint32_t xlat_pstate;
159 caddr_t xlat_state_str;
160 } fcsm_xlat_port_state [] = {
161 { FC_STATE_OFFLINE, "OFFLINE" },
162 { FC_STATE_ONLINE, "ONLINE" },
163 { FC_STATE_LOOP, "LOOP" },
164 { FC_STATE_NAMESERVICE, "NAMESERVICE" },
165 { FC_STATE_RESET, "RESET" },
166 { FC_STATE_RESET_REQUESTED, "RESET_REQUESTED" },
167 { FC_STATE_LIP, "LIP" },
168 { FC_STATE_LIP_LBIT_SET, "LIP_LBIT_SET" },
169 { FC_STATE_DEVICE_CHANGE, "DEVICE_CHANGE" },
170 { FC_STATE_TARGET_PORT_RESET, "TARGET_PORT_RESET" }
173 struct fcsm_xlat_topology {
174 uint32_t xlat_top;
175 caddr_t xlat_top_str;
176 } fcsm_xlat_topology [] = {
177 { FC_TOP_UNKNOWN, "UNKNOWN" },
178 { FC_TOP_PRIVATE_LOOP, "Private Loop" },
179 { FC_TOP_PUBLIC_LOOP, "Public Loop" },
180 { FC_TOP_FABRIC, "Fabric" },
181 { FC_TOP_PT_PT, "Point-to-Point" },
182 { FC_TOP_NO_NS, "NO_NS" }
185 struct fcsm_xlat_dev_type {
186 uint32_t xlat_type;
187 caddr_t xlat_str;
188 } fcsm_xlat_dev_type [] = {
189 { PORT_DEVICE_NOCHANGE, "No Change" },
190 { PORT_DEVICE_NEW, "New" },
191 { PORT_DEVICE_OLD, "Old" },
192 { PORT_DEVICE_CHANGED, "Changed" },
193 { PORT_DEVICE_DELETE, "Delete" },
194 { PORT_DEVICE_USER_LOGIN, "User Login" },
195 { PORT_DEVICE_USER_LOGOUT, "User Logout" },
196 { PORT_DEVICE_USER_CREATE, "User Create" },
197 { PORT_DEVICE_USER_DELETE, "User Delete" }
201 _init(void)
203 int rval;
205 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_init"));
207 fcsm_retry_ticks = drv_usectohz(fcsm_retry_ticker * 1000 * 1000);
208 fcsm_offline_ticks = drv_usectohz(fcsm_offline_ticker * 1000 * 1000);
210 if (rval = ddi_soft_state_init(&fcsm_state, sizeof (fcsm_t),
211 FCSM_INIT_INSTANCES)) {
212 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
213 "_init: ddi_soft_state_init failed");
214 return (ENOMEM);
217 mutex_init(&fcsm_global_mutex, NULL, MUTEX_DRIVER, NULL);
219 fcsm_job_cache = kmem_cache_create("fcsm_job_cache",
220 sizeof (fcsm_job_t), 8, fcsm_job_cache_constructor,
221 fcsm_job_cache_destructor, NULL, NULL, NULL, 0);
223 if (fcsm_job_cache == NULL) {
224 mutex_destroy(&fcsm_global_mutex);
225 ddi_soft_state_fini(&fcsm_state);
226 return (ENOMEM);
230 * Now call fc_ulp_add to add this ULP in the transport layer
231 * database. This will cause 'ulp_port_attach' callback function
232 * to be called.
234 rval = fc_ulp_add(&fcsm_modinfo);
235 if (rval != 0) {
236 switch (rval) {
237 case FC_ULP_SAMEMODULE:
238 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
239 "_init: FC SAN Management module is already "
240 "registered with transport layer");
241 rval = EEXIST;
242 break;
244 case FC_ULP_SAMETYPE:
245 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
246 "_init: Another module with same type 0x%x is "
247 "already registered with transport layer",
248 fcsm_modinfo.ulp_type);
249 rval = EEXIST;
250 break;
252 case FC_BADULP:
253 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
254 "_init: Please upgrade this module. Current "
255 "version 0x%x is not the most recent version",
256 fcsm_modinfo.ulp_rev);
257 rval = EIO;
258 break;
259 default:
260 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
261 "_init: fc_ulp_add failed with status 0x%x", rval);
262 rval = EIO;
263 break;
265 kmem_cache_destroy(fcsm_job_cache);
266 mutex_destroy(&fcsm_global_mutex);
267 ddi_soft_state_fini(&fcsm_state);
268 return (rval);
271 if ((rval = mod_install(&modlinkage)) != 0) {
272 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
273 "_init: mod_install failed with status 0x%x", rval));
274 (void) fc_ulp_remove(&fcsm_modinfo);
275 kmem_cache_destroy(fcsm_job_cache);
276 mutex_destroy(&fcsm_global_mutex);
277 ddi_soft_state_fini(&fcsm_state);
278 return (rval);
281 return (rval);
285 _fini(void)
287 int rval;
288 #ifdef DEBUG
289 int status;
290 #endif /* DEBUG */
292 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_fini"));
295 * don't start cleaning up until we know that the module remove
296 * has worked -- if this works, then we know that each instance
297 * has successfully been DDI_DETACHed
299 if ((rval = mod_remove(&modlinkage)) != 0) {
300 return (rval);
303 #ifdef DEBUG
304 status = fc_ulp_remove(&fcsm_modinfo);
305 if (status != 0) {
306 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
307 "_fini: fc_ulp_remove failed with status 0x%x", status));
309 #else
310 (void) fc_ulp_remove(&fcsm_modinfo);
311 #endif /* DEBUG */
313 fcsm_detached = 0;
316 * It is possible to modunload fcsm manually, which will cause
317 * a bypass of all the port_detach functionality. We may need
318 * to force that code path to be executed to properly clean up
319 * in that case.
321 fcsm_force_port_detach_all();
323 kmem_cache_destroy(fcsm_job_cache);
324 mutex_destroy(&fcsm_global_mutex);
325 ddi_soft_state_fini(&fcsm_state);
327 return (rval);
332 _info(struct modinfo *modinfop)
334 return (mod_info(&modlinkage, modinfop));
337 /* ARGSUSED */
338 static int
339 fcsm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
341 int rval = DDI_FAILURE;
343 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
344 "attach: cmd 0x%x", cmd));
346 switch (cmd) {
347 case DDI_ATTACH:
348 mutex_enter(&fcsm_global_mutex);
349 if (fcsm_dip != NULL) {
350 mutex_exit(&fcsm_global_mutex);
351 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
352 "attach: duplicate attach of fcsm!!"));
353 break;
356 fcsm_dip = dip;
359 * The detach routine cleans up all the port instances
360 * i.e. it detaches all ports.
361 * If _fini never got called after detach, then
362 * perform an fc_ulp_remove() followed by fc_ulp_add()
363 * to ensure that port_attach callbacks are called
364 * again.
366 if (fcsm_detached) {
367 int status;
369 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
370 "attach: rebinding to transport driver"));
372 mutex_exit(&fcsm_global_mutex);
374 (void) fc_ulp_remove(&fcsm_modinfo);
377 * Reset the detached flag, so that ports can attach
379 mutex_enter(&fcsm_global_mutex);
380 fcsm_detached = 0;
381 mutex_exit(&fcsm_global_mutex);
383 status = fc_ulp_add(&fcsm_modinfo);
385 if (status != 0) {
387 * ULP add failed. So set the
388 * detached flag again
390 mutex_enter(&fcsm_global_mutex);
391 fcsm_detached = 1;
392 mutex_exit(&fcsm_global_mutex);
394 switch (status) {
395 case FC_ULP_SAMEMODULE:
396 fcsm_display(CE_WARN, SM_LOG, NULL,
397 NULL, "attach: FC SAN Management "
398 "module is already "
399 "registered with transport layer");
400 break;
402 case FC_ULP_SAMETYPE:
403 fcsm_display(CE_WARN, SM_LOG, NULL,
404 NULL, "attach: Another module with "
405 "same type 0x%x is already "
406 "registered with transport layer",
407 fcsm_modinfo.ulp_type);
408 break;
410 case FC_BADULP:
411 fcsm_display(CE_WARN, SM_LOG, NULL,
412 NULL, "attach: Please upgrade this "
413 "module. Current version 0x%x is "
414 "not the most recent version",
415 fcsm_modinfo.ulp_rev);
416 break;
417 default:
418 fcsm_display(CE_WARN, SM_LOG, NULL,
419 NULL, "attach: fc_ulp_add failed "
420 "with status 0x%x", status);
421 break;
424 /* Return failure */
425 break;
428 mutex_enter(&fcsm_global_mutex);
431 /* Create a minor node */
432 if (ddi_create_minor_node(fcsm_dip, "fcsm", S_IFCHR,
433 0, DDI_PSEUDO, 0) == DDI_SUCCESS) {
434 /* Announce presence of the device */
435 mutex_exit(&fcsm_global_mutex);
436 ddi_report_dev(dip);
437 rval = DDI_SUCCESS;
438 } else {
439 fcsm_dip = NULL;
440 mutex_exit(&fcsm_global_mutex);
441 fcsm_display(CE_WARN, SM_LOG_AND_CONSOLE,
442 NULL, NULL, "attach: create minor node failed");
444 break;
446 case DDI_RESUME:
447 rval = DDI_SUCCESS;
448 break;
450 default:
451 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
452 "attach: unknown cmd 0x%x dip 0x%p", cmd, dip));
453 break;
456 return (rval);
459 /* ARGSUSED */
460 static int
461 fcsm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
463 int instance;
464 int rval = DDI_SUCCESS;
466 instance = getminor((dev_t)arg);
468 switch (cmd) {
469 case DDI_INFO_DEVT2INSTANCE:
470 *result = (void *)(long)instance; /* minor number is instance */
471 break;
473 case DDI_INFO_DEVT2DEVINFO:
474 mutex_enter(&fcsm_global_mutex);
475 *result = (void *)fcsm_dip;
476 mutex_exit(&fcsm_global_mutex);
477 break;
479 default:
480 rval = DDI_FAILURE;
481 break;
484 return (rval);
488 /* ARGSUSED */
489 static int
490 fcsm_port_attach(opaque_t ulph, fc_ulp_port_info_t *pinfo,
491 fc_attach_cmd_t cmd, uint32_t s_id)
493 int instance;
494 int rval = FC_FAILURE;
496 instance = ddi_get_instance(pinfo->port_dip);
499 * Set the attaching flag, so that fcsm_detach will fail, if
500 * port attach is in progress.
502 mutex_enter(&fcsm_global_mutex);
503 if (fcsm_detached) {
504 mutex_exit(&fcsm_global_mutex);
506 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
507 "port_attach: end. detach in progress. failing attach "
508 "instance 0x%x", instance));
509 return (((cmd == FC_CMD_POWER_UP) || (cmd == FC_CMD_RESUME)) ?
510 FC_FAILURE_SILENT : FC_FAILURE);
513 fcsm_num_attaching++;
514 mutex_exit(&fcsm_global_mutex);
516 switch (cmd) {
517 case FC_CMD_ATTACH:
518 if (fcsm_handle_port_attach(pinfo, s_id, instance)
519 != DDI_SUCCESS) {
520 ASSERT(ddi_get_soft_state(fcsm_state,
521 instance) == NULL);
522 break;
524 rval = FC_SUCCESS;
525 break;
527 case FC_CMD_RESUME:
528 case FC_CMD_POWER_UP: {
529 fcsm_t *fcsm;
530 char fcsm_pathname[MAXPATHLEN];
532 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
533 "port_attach: cmd 0x%x instance 0x%x", cmd, instance));
535 /* Get the soft state structure */
536 if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
537 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
538 "port_attach: instance 0x%x, cmd 0x%x "
539 "get softstate failed", instance, cmd));
540 break;
543 ASSERT(fcsm->sm_instance == instance);
545 /* If this instance is not attached, then return failure */
546 mutex_enter(&fcsm->sm_mutex);
547 if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
548 mutex_exit(&fcsm->sm_mutex);
549 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
550 "port_detach: port is not attached");
551 break;
553 mutex_exit(&fcsm->sm_mutex);
555 if (fcsm_handle_port_resume(ulph, pinfo, cmd, s_id, fcsm) !=
556 DDI_SUCCESS) {
557 break;
560 (void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
561 fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
562 "attached to path %s", fcsm_pathname);
563 rval = FC_SUCCESS;
564 break;
567 default:
568 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
569 "port_attach: unknown cmd 0x%x for port 0x%x",
570 cmd, instance));
571 break;
574 mutex_enter(&fcsm_global_mutex);
575 fcsm_num_attaching--;
576 mutex_exit(&fcsm_global_mutex);
577 return (rval);
581 static int
582 fcsm_handle_port_attach(fc_ulp_port_info_t *pinfo, uint32_t s_id, int instance)
584 fcsm_t *fcsm;
585 kthread_t *thread;
586 char name[32];
587 char fcsm_pathname[MAXPATHLEN];
589 /* Allocate a soft state structure for the port */
590 if (ddi_soft_state_zalloc(fcsm_state, instance) != DDI_SUCCESS) {
591 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
592 "port_attach: instance 0x%x, soft state alloc failed",
593 instance);
594 return (DDI_FAILURE);
597 if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
598 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
599 "port_attach: instance 0x%x, get soft state failed",
600 instance);
601 ddi_soft_state_free(fcsm_state, instance);
602 return (DDI_FAILURE);
606 /* Initialize the mutex */
607 mutex_init(&fcsm->sm_mutex, NULL, MUTEX_DRIVER, NULL);
608 cv_init(&fcsm->sm_job_cv, NULL, CV_DRIVER, NULL);
610 mutex_enter(&fcsm->sm_mutex);
611 fcsm->sm_flags |= FCSM_ATTACHING;
612 fcsm->sm_sid = s_id;
613 fcsm->sm_instance = instance;
614 fcsm->sm_port_state = pinfo->port_state;
617 * Make a copy of the port_information structure, since fctl
618 * uses a temporary structure.
620 fcsm->sm_port_info = *pinfo; /* Structure copy !!! */
621 mutex_exit(&fcsm->sm_mutex);
624 (void) sprintf(name, "fcsm%d_cmd_cache", fcsm->sm_instance);
625 fcsm->sm_cmd_cache = kmem_cache_create(name,
626 sizeof (fcsm_cmd_t) + pinfo->port_fca_pkt_size, 8,
627 fcsm_cmd_cache_constructor, fcsm_cmd_cache_destructor,
628 NULL, (void *)fcsm, NULL, 0);
629 if (fcsm->sm_cmd_cache == NULL) {
630 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
631 "port_attach: pkt cache create failed");
632 cv_destroy(&fcsm->sm_job_cv);
633 mutex_destroy(&fcsm->sm_mutex);
634 ddi_soft_state_free(fcsm_state, instance);
635 return (DDI_FAILURE);
638 thread = thread_create(NULL, 0, fcsm_job_thread,
639 (caddr_t)fcsm, 0, &p0, TS_RUN, v.v_maxsyspri-2);
640 if (thread == NULL) {
641 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
642 "port_attach: job thread create failed");
643 kmem_cache_destroy(fcsm->sm_cmd_cache);
644 cv_destroy(&fcsm->sm_job_cv);
645 mutex_destroy(&fcsm->sm_mutex);
646 ddi_soft_state_free(fcsm_state, instance);
647 return (DDI_FAILURE);
650 fcsm->sm_thread = thread;
652 /* Add this structure to fcsm global linked list */
653 mutex_enter(&fcsm_global_mutex);
654 if (fcsm_port_head == NULL) {
655 fcsm_port_head = fcsm;
656 } else {
657 fcsm->sm_next = fcsm_port_head;
658 fcsm_port_head = fcsm;
660 mutex_exit(&fcsm_global_mutex);
662 mutex_enter(&fcsm->sm_mutex);
663 fcsm->sm_flags &= ~FCSM_ATTACHING;
664 fcsm->sm_flags |= FCSM_ATTACHED;
665 fcsm->sm_port_top = pinfo->port_flags;
666 fcsm->sm_port_state = pinfo->port_state;
667 if (pinfo->port_acc_attr == NULL) {
669 * The corresponding FCA doesn't support DMA at all
671 fcsm->sm_flags |= FCSM_USING_NODMA_FCA;
673 mutex_exit(&fcsm->sm_mutex);
675 (void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
676 fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
677 "attached to path %s", fcsm_pathname);
679 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
680 "port_attach: state <%s>(0x%x) topology <%s>(0x%x)",
681 fcsm_port_state_to_str(FC_PORT_STATE_MASK(pinfo->port_state)),
682 pinfo->port_state,
683 fcsm_topology_to_str(pinfo->port_flags), pinfo->port_flags));
685 return (DDI_SUCCESS);
688 static int
689 fcsm_handle_port_resume(opaque_t ulph, fc_ulp_port_info_t *pinfo,
690 fc_attach_cmd_t cmd, uint32_t s_id, fcsm_t *fcsm)
692 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
693 "port_resume: cmd 0x%x", cmd));
695 mutex_enter(&fcsm->sm_mutex);
697 switch (cmd) {
698 case FC_CMD_RESUME:
699 ASSERT(!(fcsm->sm_flags & FCSM_POWER_DOWN));
700 fcsm->sm_flags &= ~FCSM_SUSPENDED;
701 break;
703 case FC_CMD_POWER_UP:
704 /* If port is suspended, then no need to resume */
705 fcsm->sm_flags &= ~FCSM_POWER_DOWN;
706 if (fcsm->sm_flags & FCSM_SUSPENDED) {
707 mutex_exit(&fcsm->sm_mutex);
708 return (DDI_SUCCESS);
710 break;
711 default:
712 mutex_exit(&fcsm->sm_mutex);
713 return (DDI_FAILURE);
716 fcsm->sm_sid = s_id;
719 * Make a copy of the new port_information structure
721 fcsm->sm_port_info = *pinfo; /* Structure copy !!! */
722 mutex_exit(&fcsm->sm_mutex);
724 fcsm_resume_port(fcsm);
727 * Invoke state change processing.
728 * This will ensure that
729 * - offline timer is started if new port state changed to offline.
730 * - MGMT_SERVER_LOGIN flag is reset.
731 * - Port topology is updated.
733 fcsm_statec_cb(ulph, (opaque_t)pinfo->port_handle, pinfo->port_state,
734 pinfo->port_flags, NULL, 0, s_id);
736 return (DDI_SUCCESS);
740 /* ARGSUSED */
741 static int
742 fcsm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
744 int rval = DDI_SUCCESS;
746 switch (cmd) {
747 case DDI_DETACH: {
748 fcsm_t *fcsm;
750 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
751 "detach: start. cmd <DETACH>", cmd));
753 mutex_enter(&fcsm_global_mutex);
756 * If port attach/detach in progress, then wait for 5 seconds
757 * for them to complete.
759 if (fcsm_num_attaching || fcsm_num_detaching) {
760 int count;
762 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
763 "detach: wait for port attach/detach to complete"));
765 count = 0;
766 while ((count++ <= 30) &&
767 (fcsm_num_attaching || fcsm_num_detaching)) {
768 mutex_exit(&fcsm_global_mutex);
769 ddi_sleep(1);
770 mutex_enter(&fcsm_global_mutex);
773 /* Port attach/detach still in prog, so fail detach */
774 if (fcsm_num_attaching || fcsm_num_detaching) {
775 mutex_exit(&fcsm_global_mutex);
776 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL,
777 NULL, "detach: Failing detach. port "
778 "attach/detach in progress"));
779 rval = DDI_FAILURE;
780 break;
784 if (fcsm_port_head == NULL) {
785 /* Not much do, Succeed to detach. */
786 ddi_remove_minor_node(fcsm_dip, NULL);
787 fcsm_dip = NULL;
788 fcsm_detached = 0;
789 mutex_exit(&fcsm_global_mutex);
790 break;
794 * Check to see, if any ports are active.
795 * If not, then set the DETACHING flag to indicate
796 * that they are being detached.
798 fcsm = fcsm_port_head;
799 while (fcsm != NULL) {
801 mutex_enter(&fcsm->sm_mutex);
802 if (!(fcsm->sm_flags & FCSM_ATTACHED) ||
803 fcsm->sm_ncmds || fcsm->sm_cb_count) {
804 /* port is busy. We can't detach */
805 mutex_exit(&fcsm->sm_mutex);
806 break;
809 fcsm->sm_flags |= FCSM_DETACHING;
810 mutex_exit(&fcsm->sm_mutex);
812 fcsm = fcsm->sm_next;
816 * If all ports could not be marked for detaching,
817 * then clear the flags and fail the detach.
818 * Also if a port attach is currently in progress
819 * then fail the detach.
821 if (fcsm != NULL || fcsm_num_attaching || fcsm_num_detaching) {
823 * Some ports were busy, so can't detach.
824 * Clear the DETACHING flag and return failure
826 fcsm = fcsm_port_head;
827 while (fcsm != NULL) {
828 mutex_enter(&fcsm->sm_mutex);
829 if (fcsm->sm_flags & FCSM_DETACHING) {
830 fcsm->sm_flags &= ~FCSM_DETACHING;
832 mutex_exit(&fcsm->sm_mutex);
834 fcsm = fcsm->sm_next;
836 mutex_exit(&fcsm_global_mutex);
837 return (DDI_FAILURE);
838 } else {
839 fcsm_detached = 1;
841 * Mark all the detaching ports as detached, as we
842 * will be detaching them
844 fcsm = fcsm_port_head;
845 while (fcsm != NULL) {
846 mutex_enter(&fcsm->sm_mutex);
847 fcsm->sm_flags &= ~FCSM_DETACHING;
848 fcsm->sm_flags |= FCSM_DETACHED;
849 mutex_exit(&fcsm->sm_mutex);
851 fcsm = fcsm->sm_next;
854 mutex_exit(&fcsm_global_mutex);
858 * Go ahead and detach the ports
860 mutex_enter(&fcsm_global_mutex);
861 while (fcsm_port_head != NULL) {
862 fcsm = fcsm_port_head;
863 mutex_exit(&fcsm_global_mutex);
866 * Call fcsm_cleanup_port(). This cleansup and
867 * removes the fcsm structure from global linked list
869 fcsm_cleanup_port(fcsm);
872 * Soft state cleanup done.
873 * Remember that fcsm struct doesn't exist anymore.
876 mutex_enter(&fcsm_global_mutex);
879 ddi_remove_minor_node(fcsm_dip, NULL);
880 fcsm_dip = NULL;
881 mutex_exit(&fcsm_global_mutex);
882 break;
885 case DDI_SUSPEND:
886 rval = DDI_SUCCESS;
887 break;
889 default:
890 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
891 "detach: unknown cmd 0x%x", cmd));
892 rval = DDI_FAILURE;
893 break;
896 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
897 "detach: end. cmd 0x%x, rval 0x%x", cmd, rval));
899 return (rval);
903 /* ARGSUSED */
904 static void
905 fcsm_force_port_detach_all(void)
907 fcsm_t *fcsm;
909 fcsm = fcsm_port_head;
911 while (fcsm) {
912 fcsm_cleanup_port(fcsm);
914 * fcsm_cleanup_port will remove the current fcsm structure
915 * from the list, which will cause fcsm_port_head to point
916 * to what would have been the next structure on the list.
918 fcsm = fcsm_port_head;
923 /* ARGSUSED */
924 static int
925 fcsm_port_detach(opaque_t ulph, fc_ulp_port_info_t *pinfo, fc_detach_cmd_t cmd)
927 int instance;
928 int rval = FC_FAILURE;
929 fcsm_t *fcsm;
931 instance = ddi_get_instance(pinfo->port_dip);
933 mutex_enter(&fcsm_global_mutex);
934 if (fcsm_detached) {
935 mutex_exit(&fcsm_global_mutex);
937 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
938 "port_detach: end. instance 0x%x, fcsm is detached",
939 instance));
940 return (FC_SUCCESS);
942 fcsm_num_detaching++; /* Set the flag */
943 mutex_exit(&fcsm_global_mutex);
945 /* Get the soft state structure */
946 if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
947 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
948 "port_detach: instance 0x%x, cmd 0x%x get softstate failed",
949 instance, cmd));
950 mutex_enter(&fcsm_global_mutex);
951 fcsm_num_detaching--;
952 mutex_exit(&fcsm_global_mutex);
953 return (rval);
956 ASSERT(fcsm->sm_instance == instance);
958 /* If this instance is not attached, then fail the detach */
959 mutex_enter(&fcsm->sm_mutex);
960 if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
961 mutex_exit(&fcsm->sm_mutex);
962 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
963 "port_detach: port is not attached");
964 mutex_enter(&fcsm_global_mutex);
965 fcsm_num_detaching--;
966 mutex_exit(&fcsm_global_mutex);
967 return (rval);
969 mutex_exit(&fcsm->sm_mutex);
972 * If fcsm has been detached, then all instance has already been
973 * detached or are being detached. So succeed this detach.
976 switch (cmd) {
977 case FC_CMD_DETACH:
978 case FC_CMD_SUSPEND:
979 case FC_CMD_POWER_DOWN:
980 break;
982 default:
983 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
984 "port_detach: port unknown cmd 0x%x", cmd));
985 mutex_enter(&fcsm_global_mutex);
986 fcsm_num_detaching--;
987 mutex_exit(&fcsm_global_mutex);
988 return (rval);
991 if (fcsm_handle_port_detach(pinfo, fcsm, cmd) == DDI_SUCCESS) {
992 rval = FC_SUCCESS;
995 mutex_enter(&fcsm_global_mutex);
996 fcsm_num_detaching--;
997 mutex_exit(&fcsm_global_mutex);
999 /* If it was a detach, then fcsm state structure no longer exists */
1000 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1001 "port_detach: end. cmd 0x%x rval 0x%x", cmd, rval));
1002 return (rval);
1006 static int
1007 fcsm_handle_port_detach(fc_ulp_port_info_t *pinfo, fcsm_t *fcsm,
1008 fc_detach_cmd_t cmd)
1010 uint32_t flag;
1011 int count;
1012 #ifdef DEBUG
1013 char pathname[MAXPATHLEN];
1014 #endif /* DEBUG */
1017 * If port is already powered down OR suspended and there is nothing
1018 * else to do then just return.
1019 * Otherwise, set the flag, so that no more new activity will be
1020 * initiated on this port.
1022 mutex_enter(&fcsm->sm_mutex);
1024 switch (cmd) {
1025 case FC_CMD_DETACH:
1026 flag = FCSM_DETACHING;
1027 break;
1029 case FC_CMD_SUSPEND:
1030 case FC_CMD_POWER_DOWN:
1031 ((cmd == FC_CMD_SUSPEND) ? (flag = FCSM_SUSPENDED) :
1032 (flag = FCSM_POWER_DOWN));
1033 if (fcsm->sm_flags &
1034 (FCSM_POWER_DOWN | FCSM_SUSPENDED)) {
1035 fcsm->sm_flags |= flag;
1036 mutex_exit(&fcsm->sm_mutex);
1037 return (DDI_SUCCESS);
1039 break;
1041 default:
1042 mutex_exit(&fcsm->sm_mutex);
1043 return (DDI_FAILURE);
1046 fcsm->sm_flags |= flag;
1049 * If some commands are pending OR callback in progress, then
1050 * wait for some finite amount of time for their completion.
1051 * TODO: add more checks here to check for cmd timeout, offline
1052 * timeout and other (??) threads.
1054 count = 0;
1055 while ((count++ <= 30) && (fcsm->sm_ncmds || fcsm->sm_cb_count)) {
1056 mutex_exit(&fcsm->sm_mutex);
1057 ddi_sleep(1);
1058 mutex_enter(&fcsm->sm_mutex);
1060 if (fcsm->sm_ncmds || fcsm->sm_cb_count) {
1061 fcsm->sm_flags &= ~flag;
1062 mutex_exit(&fcsm->sm_mutex);
1063 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
1064 "port_detach: Failing suspend, port is busy");
1065 return (DDI_FAILURE);
1067 if (flag == FCSM_DETACHING) {
1068 fcsm->sm_flags &= ~FCSM_DETACHING;
1069 fcsm->sm_flags |= FCSM_DETACHED;
1072 mutex_exit(&fcsm->sm_mutex);
1074 FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
1075 "port_detach: cmd 0x%x pathname <%s>",
1076 cmd, ddi_pathname(pinfo->port_dip, pathname)));
1078 if (cmd == FC_CMD_DETACH) {
1079 fcsm_cleanup_port(fcsm);
1081 * Soft state cleanup done.
1082 * Always remember that fcsm struct doesn't exist anymore.
1084 } else {
1085 fcsm_suspend_port(fcsm);
1088 return (DDI_SUCCESS);
1091 static void
1092 fcsm_suspend_port(fcsm_t *fcsm)
1094 mutex_enter(&fcsm->sm_mutex);
1096 if (fcsm->sm_offline_tid != NULL) {
1097 timeout_id_t tid;
1099 tid = fcsm->sm_offline_tid;
1100 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1101 mutex_exit(&fcsm->sm_mutex);
1102 (void) untimeout(tid);
1103 mutex_enter(&fcsm->sm_mutex);
1104 fcsm->sm_flags |= FCSM_RESTORE_OFFLINE_TIMEOUT;
1107 if (fcsm->sm_retry_tid != NULL) {
1108 timeout_id_t tid;
1110 tid = fcsm->sm_retry_tid;
1111 fcsm->sm_retry_tid = (timeout_id_t)NULL;
1112 mutex_exit(&fcsm->sm_mutex);
1113 (void) untimeout(tid);
1114 mutex_enter(&fcsm->sm_mutex);
1115 fcsm->sm_flags |= FCSM_RESTORE_RETRY_TIMEOUT;
1118 mutex_exit(&fcsm->sm_mutex);
1121 static void
1122 fcsm_resume_port(fcsm_t *fcsm)
1124 mutex_enter(&fcsm->sm_mutex);
1126 if (fcsm->sm_flags & FCSM_RESTORE_OFFLINE_TIMEOUT) {
1127 fcsm->sm_flags &= ~FCSM_RESTORE_OFFLINE_TIMEOUT;
1130 * If port if offline, link is not marked down and offline
1131 * timer is not already running, then restart offline timer.
1133 if (!(fcsm->sm_flags & FCSM_LINK_DOWN) &&
1134 fcsm->sm_offline_tid == NULL &&
1135 (fcsm->sm_flags & FCSM_PORT_OFFLINE)) {
1136 fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
1137 (caddr_t)fcsm, fcsm_offline_ticks);
1141 if (fcsm->sm_flags & FCSM_RESTORE_RETRY_TIMEOUT) {
1142 fcsm->sm_flags &= ~FCSM_RESTORE_RETRY_TIMEOUT;
1145 * If retry queue is not suspended and some cmds are waiting
1146 * to be retried, then restart the retry timer
1148 if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1149 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1150 (caddr_t)fcsm, fcsm_retry_ticks);
1153 mutex_exit(&fcsm->sm_mutex);
1156 static void
1157 fcsm_cleanup_port(fcsm_t *fcsm)
1159 fcsm_t *curr, *prev;
1160 int status;
1161 fcsm_job_t *job;
1163 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1164 "fcsm_cleanup_port: entered"));
1167 * Kill the job thread
1169 job = fcsm_alloc_job(KM_SLEEP);
1170 ASSERT(job != NULL);
1171 fcsm_init_job(job, fcsm->sm_instance, FCSM_JOB_THREAD_SHUTDOWN,
1172 FCSM_JOBFLAG_SYNC, NULL, NULL, NULL, NULL);
1174 status = fcsm_process_job(job, 0);
1175 ASSERT(status == FC_SUCCESS);
1177 ASSERT(job->job_result == FC_SUCCESS);
1178 fcsm_dealloc_job(job);
1181 * We got here after ensuring the no commands are pending or active.
1182 * Therefore retry timeout thread should NOT be running.
1183 * Kill the offline timeout thread if currently running.
1185 mutex_enter(&fcsm->sm_mutex);
1187 ASSERT(fcsm->sm_retry_tid == NULL);
1189 if (fcsm->sm_offline_tid != NULL) {
1190 timeout_id_t tid;
1192 tid = fcsm->sm_offline_tid;
1193 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1194 mutex_exit(&fcsm->sm_mutex);
1195 (void) untimeout(tid);
1196 } else {
1197 mutex_exit(&fcsm->sm_mutex);
1200 /* Remove from the fcsm state structure from global linked list */
1201 mutex_enter(&fcsm_global_mutex);
1202 curr = fcsm_port_head;
1203 prev = NULL;
1204 while (curr != fcsm && curr != NULL) {
1205 prev = curr;
1206 curr = curr->sm_next;
1208 ASSERT(curr != NULL);
1210 if (prev == NULL) {
1211 fcsm_port_head = curr->sm_next;
1212 } else {
1213 prev->sm_next = curr->sm_next;
1215 mutex_exit(&fcsm_global_mutex);
1217 if (fcsm->sm_cmd_cache != NULL) {
1218 kmem_cache_destroy(fcsm->sm_cmd_cache);
1220 cv_destroy(&fcsm->sm_job_cv);
1221 mutex_destroy(&fcsm->sm_mutex);
1223 /* Free the fcsm state structure */
1224 ddi_soft_state_free(fcsm_state, fcsm->sm_instance);
1228 /* ARGSUSED */
1229 static void
1230 fcsm_statec_cb(opaque_t ulph, opaque_t port_handle, uint32_t port_state,
1231 uint32_t port_top, fc_portmap_t *devlist, uint32_t dev_cnt,
1232 uint32_t port_sid)
1234 fcsm_t *fcsm;
1235 timeout_id_t offline_tid, retry_tid;
1237 mutex_enter(&fcsm_global_mutex);
1238 if (fcsm_detached) {
1239 mutex_exit(&fcsm_global_mutex);
1240 return;
1243 fcsm = ddi_get_soft_state(fcsm_state,
1244 fc_ulp_get_port_instance(port_handle));
1245 if (fcsm == NULL) {
1246 mutex_exit(&fcsm_global_mutex);
1247 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
1248 "statec_cb: instance 0x%x not found",
1249 fc_ulp_get_port_instance(port_handle)));
1250 return;
1252 mutex_enter(&fcsm->sm_mutex);
1253 ASSERT(fcsm->sm_instance == fc_ulp_get_port_instance(port_handle));
1254 if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
1255 mutex_exit(&fcsm->sm_mutex);
1256 mutex_exit(&fcsm_global_mutex);
1257 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, fcsm, NULL,
1258 "statec_cb: port not attached"));
1259 return;
1262 ASSERT(fcsm->sm_cb_count >= 0);
1264 fcsm->sm_cb_count++;
1265 mutex_exit(&fcsm->sm_mutex);
1266 mutex_exit(&fcsm_global_mutex);
1268 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1269 "statec_cb: state <%s>(0x%x) topology <%s>(0x%x) dev_cnt %d",
1270 fcsm_port_state_to_str(FC_PORT_STATE_MASK(port_state)), port_state,
1271 fcsm_topology_to_str(port_top), port_top, dev_cnt));
1273 fcsm_disp_devlist(fcsm, devlist, dev_cnt);
1275 mutex_enter(&fcsm->sm_mutex);
1278 * Reset the Mgmt server Login flag, so that login is performed again.
1280 fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
1282 fcsm->sm_sid = port_sid;
1283 fcsm->sm_port_top = port_top;
1284 fcsm->sm_port_state = port_state;
1286 switch (port_state) {
1287 case FC_STATE_OFFLINE:
1288 case FC_STATE_RESET:
1289 case FC_STATE_RESET_REQUESTED:
1290 fcsm->sm_flags |= FCSM_PORT_OFFLINE;
1291 break;
1293 case FC_STATE_ONLINE:
1294 case FC_STATE_LOOP:
1295 case FC_STATE_LIP:
1296 case FC_STATE_LIP_LBIT_SET:
1297 fcsm->sm_flags &= ~FCSM_PORT_OFFLINE;
1298 fcsm->sm_flags &= ~FCSM_LINK_DOWN;
1299 break;
1301 case FC_STATE_NAMESERVICE:
1302 case FC_STATE_DEVICE_CHANGE:
1303 case FC_STATE_TARGET_PORT_RESET:
1304 default:
1305 /* Do nothing */
1306 break;
1309 offline_tid = retry_tid = NULL;
1310 if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
1312 * Port is offline.
1313 * Suspend cmd processing and start offline timeout thread.
1315 if (fcsm->sm_offline_tid == NULL) {
1316 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1317 "statec_cb: schedule offline timeout thread"));
1318 fcsm->sm_flags |= FCSM_CMD_RETRY_Q_SUSPENDED;
1319 /* Stop the cmd retry thread */
1320 retry_tid = fcsm->sm_retry_tid;
1321 fcsm->sm_retry_tid = (timeout_id_t)NULL;
1323 fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
1324 (caddr_t)fcsm, fcsm_offline_ticks);
1327 } else {
1329 * Port is online.
1330 * Cancel offline timeout thread and resume command processing.
1332 if (fcsm->sm_offline_tid) {
1333 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1334 "statec_cb: cancel offline timeout thread"));
1335 offline_tid = fcsm->sm_offline_tid;
1336 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1339 fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
1340 /* Start retry thread if needed */
1341 if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1342 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1343 (caddr_t)fcsm, fcsm_retry_ticks);
1347 mutex_exit(&fcsm->sm_mutex);
1349 if (offline_tid != NULL) {
1350 (void) untimeout(offline_tid);
1353 if (retry_tid != NULL) {
1354 (void) untimeout(retry_tid);
1357 mutex_enter(&fcsm->sm_mutex);
1358 fcsm->sm_cb_count--;
1359 ASSERT(fcsm->sm_cb_count >= 0);
1360 mutex_exit(&fcsm->sm_mutex);
1364 static void
1365 fcsm_offline_timeout(void *handle)
1367 fcsm_t *fcsm = (fcsm_t *)handle;
1369 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1370 "offline_timeout"));
1372 mutex_enter(&fcsm->sm_mutex);
1373 if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
1374 fcsm->sm_flags |= FCSM_LINK_DOWN;
1376 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1377 fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
1379 /* Start the retry thread if needed */
1380 if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1381 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1382 "offline_timeout: reschedule cmd retry thread"));
1383 ASSERT(fcsm->sm_retry_tid == NULL);
1384 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1385 (caddr_t)fcsm, fcsm_retry_ticks);
1387 mutex_exit(&fcsm->sm_mutex);
1390 /* ARGSUSED */
1391 static int
1392 fcsm_els_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
1393 uint32_t claimed)
1395 return (FC_UNCLAIMED);
1399 /* ARGSUSED */
1400 static int
1401 fcsm_data_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
1402 uint32_t claimed)
1404 return (FC_UNCLAIMED);
1408 /* ARGSUSED */
1409 static int
1410 fcsm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1411 int *rval_p)
1413 int retval = 0;
1415 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: start"));
1417 mutex_enter(&fcsm_global_mutex);
1418 if (!(fcsm_flag & FCSM_OPEN)) {
1419 mutex_exit(&fcsm_global_mutex);
1420 return (ENXIO);
1422 mutex_exit(&fcsm_global_mutex);
1424 /* Allow only root to talk */
1425 if (drv_priv(credp)) {
1426 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1427 "ioctl: end (disallowing underprivileged user)"));
1428 return (EPERM);
1431 switch (cmd) {
1433 case FCSMIO_CMD: {
1434 fcio_t fcio;
1435 int status;
1436 #ifdef _MULTI_DATAMODEL
1437 switch (ddi_model_convert_from(mode & FMODELS)) {
1438 case DDI_MODEL_ILP32: {
1439 struct fcio32 fcio32;
1441 if (status = ddi_copyin((void *)arg, (void *)&fcio32,
1442 sizeof (struct fcio32), mode)) {
1443 retval = EFAULT;
1444 break;
1446 fcio.fcio_xfer = fcio32.fcio_xfer;
1447 fcio.fcio_cmd = fcio32.fcio_cmd;
1448 fcio.fcio_flags = fcio32.fcio_flags;
1449 fcio.fcio_cmd_flags = fcio32.fcio_cmd_flags;
1450 fcio.fcio_ilen = (size_t)fcio32.fcio_ilen;
1451 fcio.fcio_ibuf = (caddr_t)(long)fcio32.fcio_ibuf;
1452 fcio.fcio_olen = (size_t)fcio32.fcio_olen;
1453 fcio.fcio_obuf = (caddr_t)(long)fcio32.fcio_obuf;
1454 fcio.fcio_alen = (size_t)fcio32.fcio_alen;
1455 fcio.fcio_abuf = (caddr_t)(long)fcio32.fcio_abuf;
1456 fcio.fcio_errno = fcio32.fcio_errno;
1457 break;
1460 case DDI_MODEL_NONE:
1461 if (status = ddi_copyin((void *)arg, (void *)&fcio,
1462 sizeof (fcio_t), mode)) {
1463 retval = EFAULT;
1465 break;
1467 #else /* _MULTI_DATAMODEL */
1468 if (status = ddi_copyin((void *)arg, (void *)&fcio,
1469 sizeof (fcio_t), mode)) {
1470 retval = EFAULT;
1471 break;
1473 #endif /* _MULTI_DATAMODEL */
1474 if (!status) {
1475 retval = fcsm_fciocmd(arg, mode, credp, &fcio);
1477 break;
1480 default:
1481 retval = ENOTTY;
1482 break;
1485 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: end"));
1486 return (retval);
1489 /* ARGSUSED */
1490 static int
1491 fcsm_port_ioctl(opaque_t ulph, opaque_t port_handle, dev_t dev, int cmd,
1492 intptr_t arg, int mode, cred_t *credp, int *rval, uint32_t claimed)
1494 return (FC_UNCLAIMED);
1498 /* ARGSUSED */
1499 static int
1500 fcsm_fciocmd(intptr_t arg, int mode, cred_t *credp, fcio_t *fcio)
1502 int retval = 0;
1504 switch (fcio->fcio_cmd) {
1505 case FCSMIO_CT_CMD: {
1506 fcsm_t *fcsm;
1507 caddr_t user_ibuf, user_obuf;
1508 caddr_t req_iu, rsp_iu, abuf;
1509 int status, instance, count;
1511 if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
1512 (fcio->fcio_ilen == 0) || (fcio->fcio_ibuf == 0) ||
1513 (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0) ||
1514 (fcio->fcio_alen == 0) || (fcio->fcio_abuf == 0) ||
1515 (fcio->fcio_flags != 0) || (fcio->fcio_cmd_flags != 0) ||
1516 (fcio->fcio_ilen > FCSM_MAX_CT_SIZE) ||
1517 (fcio->fcio_olen > FCSM_MAX_CT_SIZE) ||
1518 (fcio->fcio_alen > MAXPATHLEN)) {
1519 retval = EINVAL;
1520 break;
1524 * Get the destination port for which this ioctl
1525 * is targeted. The abuf will have the fp_minor
1526 * number.
1528 abuf = kmem_zalloc(fcio->fcio_alen, KM_SLEEP);
1529 ASSERT(abuf != NULL);
1530 if (ddi_copyin(fcio->fcio_abuf, abuf, fcio->fcio_alen, mode)) {
1531 retval = EFAULT;
1532 kmem_free(abuf, fcio->fcio_alen);
1533 break;
1536 instance = *((int *)abuf);
1537 kmem_free(abuf, fcio->fcio_alen);
1539 if (instance < 0) {
1540 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
1541 "fciocmd: instance 0x%x, invalid instance",
1542 instance));
1543 retval = ENXIO;
1544 break;
1548 * We confirmed that path corresponds to our port driver
1549 * and a valid instance.
1550 * If this port instance is not yet attached, then wait
1551 * for a finite time for attach to complete
1553 fcsm = ddi_get_soft_state(fcsm_state, instance);
1554 count = 0;
1555 while (count++ <= 30) {
1556 if (fcsm != NULL) {
1557 mutex_enter(&fcsm->sm_mutex);
1558 if (fcsm->sm_flags & FCSM_ATTACHED) {
1559 mutex_exit(&fcsm->sm_mutex);
1560 break;
1562 mutex_exit(&fcsm->sm_mutex);
1564 if (count == 1) {
1565 FCSM_DEBUG(SMDL_TRACE,
1566 (CE_WARN, SM_LOG, NULL, NULL,
1567 "fciocmd: instance 0x%x, "
1568 "wait for port attach", instance));
1570 ddi_sleep(1);
1571 fcsm = ddi_get_soft_state(fcsm_state, instance);
1573 if (count > 30) {
1574 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
1575 "fciocmd: instance 0x%x, port not attached",
1576 instance));
1577 retval = ENXIO;
1578 break;
1581 req_iu = kmem_zalloc(fcio->fcio_ilen, KM_SLEEP);
1582 rsp_iu = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
1583 ASSERT((req_iu != NULL) && (rsp_iu != NULL));
1585 if (ddi_copyin(fcio->fcio_ibuf, req_iu,
1586 fcio->fcio_ilen, mode)) {
1587 retval = EFAULT;
1588 kmem_free(req_iu, fcio->fcio_ilen);
1589 kmem_free(rsp_iu, fcio->fcio_olen);
1590 break;
1593 user_ibuf = fcio->fcio_ibuf;
1594 user_obuf = fcio->fcio_obuf;
1595 fcio->fcio_ibuf = req_iu;
1596 fcio->fcio_obuf = rsp_iu;
1598 status = fcsm_ct_passthru(fcsm->sm_instance, fcio, KM_SLEEP,
1599 FCSM_JOBFLAG_SYNC, NULL);
1600 if (status != FC_SUCCESS) {
1601 retval = EIO;
1604 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1605 "fciocmd: cmd 0x%x completion status 0x%x",
1606 fcio->fcio_cmd, status));
1607 fcio->fcio_errno = status;
1608 fcio->fcio_ibuf = user_ibuf;
1609 fcio->fcio_obuf = user_obuf;
1611 if (ddi_copyout(rsp_iu, fcio->fcio_obuf,
1612 fcio->fcio_olen, mode)) {
1613 retval = EFAULT;
1614 kmem_free(req_iu, fcio->fcio_ilen);
1615 kmem_free(rsp_iu, fcio->fcio_olen);
1616 break;
1619 kmem_free(req_iu, fcio->fcio_ilen);
1620 kmem_free(rsp_iu, fcio->fcio_olen);
1622 if (fcsm_fcio_copyout(fcio, arg, mode)) {
1623 retval = EFAULT;
1625 break;
1628 case FCSMIO_ADAPTER_LIST: {
1629 fc_hba_list_t *list;
1630 int count;
1632 if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
1633 (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0)) {
1634 retval = EINVAL;
1635 break;
1638 list = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
1640 if (ddi_copyin(fcio->fcio_obuf, list, fcio->fcio_olen, mode)) {
1641 retval = EFAULT;
1642 break;
1644 list->version = FC_HBA_LIST_VERSION;
1646 if (fcio->fcio_olen < MAXPATHLEN * list->numAdapters) {
1647 retval = EFAULT;
1648 break;
1651 count = fc_ulp_get_adapter_paths((char *)list->hbaPaths,
1652 list->numAdapters);
1653 if (count < 0) {
1654 /* Did something go wrong? */
1655 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1656 "Error fetching adapter list."));
1657 retval = ENXIO;
1658 kmem_free(list, fcio->fcio_olen);
1659 break;
1661 /* Sucess (or short buffer) */
1662 list->numAdapters = count;
1663 if (ddi_copyout(list, fcio->fcio_obuf,
1664 fcio->fcio_olen, mode)) {
1665 retval = EFAULT;
1667 kmem_free(list, fcio->fcio_olen);
1668 break;
1671 default:
1672 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
1673 "fciocmd: unknown cmd <0x%x>", fcio->fcio_cmd));
1674 retval = ENOTTY;
1675 break;
1678 return (retval);
1681 static int
1682 fcsm_fcio_copyout(fcio_t *fcio, intptr_t arg, int mode)
1684 int status;
1686 #ifdef _MULTI_DATAMODEL
1687 switch (ddi_model_convert_from(mode & FMODELS)) {
1688 case DDI_MODEL_ILP32: {
1689 struct fcio32 fcio32;
1691 fcio32.fcio_xfer = fcio->fcio_xfer;
1692 fcio32.fcio_cmd = fcio->fcio_cmd;
1693 fcio32.fcio_flags = fcio->fcio_flags;
1694 fcio32.fcio_cmd_flags = fcio->fcio_cmd_flags;
1695 fcio32.fcio_ilen = fcio->fcio_ilen;
1696 fcio32.fcio_ibuf = (caddr32_t)(long)fcio->fcio_ibuf;
1697 fcio32.fcio_olen = fcio->fcio_olen;
1698 fcio32.fcio_obuf = (caddr32_t)(long)fcio->fcio_obuf;
1699 fcio32.fcio_alen = fcio->fcio_alen;
1700 fcio32.fcio_abuf = (caddr32_t)(long)fcio->fcio_abuf;
1701 fcio32.fcio_errno = fcio->fcio_errno;
1703 status = ddi_copyout((void *)&fcio32, (void *)arg,
1704 sizeof (struct fcio32), mode);
1705 break;
1707 case DDI_MODEL_NONE:
1708 status = ddi_copyout((void *)fcio, (void *)arg,
1709 sizeof (fcio_t), mode);
1710 break;
1712 #else /* _MULTI_DATAMODEL */
1713 status = ddi_copyout((void *)fcio, (void *)arg, sizeof (fcio_t), mode);
1714 #endif /* _MULTI_DATAMODEL */
1716 return (status);
1720 /* ARGSUSED */
1721 static int
1722 fcsm_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1724 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "open"));
1726 if (otyp != OTYP_CHR) {
1727 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1728 "fcsm_open: failed. open type 0x%x for minor 0x%x is not "
1729 "OTYP_CHR", otyp, getminor(*devp)));
1730 return (EINVAL);
1734 * Allow anybody to open (both root and non-root users).
1735 * Previlege level checks are made on the per ioctl basis.
1737 mutex_enter(&fcsm_global_mutex);
1738 if (flags & FEXCL) {
1739 if (fcsm_flag & FCSM_OPEN) {
1740 mutex_exit(&fcsm_global_mutex);
1741 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1742 "fcsm_open: exclusive open of 0x%x failed",
1743 getminor(*devp)));
1744 return (EBUSY);
1745 } else {
1746 ASSERT(fcsm_flag == FCSM_IDLE);
1747 fcsm_flag |= FCSM_EXCL;
1749 } else {
1750 if (fcsm_flag & FCSM_EXCL) {
1751 mutex_exit(&fcsm_global_mutex);
1752 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1753 "fcsm_open: failed. Device minor 0x%x is in "
1754 "exclusive open mode", getminor(*devp)));
1755 return (EBUSY);
1759 fcsm_flag |= FCSM_OPEN;
1760 mutex_exit(&fcsm_global_mutex);
1761 return (0);
1765 /* ARGSUSED */
1766 static int
1767 fcsm_close(dev_t dev, int flag, int otyp, cred_t *credp)
1769 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "close"));
1771 if (otyp != OTYP_CHR) {
1772 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1773 "fcsm_close: failed. close type 0x%x for minor 0x%x is not "
1774 "OTYP_CHR", otyp, getminor(dev)));
1775 return (EINVAL);
1778 mutex_enter(&fcsm_global_mutex);
1779 if ((fcsm_flag & FCSM_OPEN) == 0) {
1780 mutex_exit(&fcsm_global_mutex);
1781 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1782 "fcsm_close: failed. minor 0x%x is already closed",
1783 getminor(dev)));
1784 return (ENODEV);
1786 fcsm_flag = FCSM_IDLE;
1787 mutex_exit(&fcsm_global_mutex);
1788 return (0);
1792 /* ARGSUSED */
1793 static void
1794 fcsm_disp_devlist(fcsm_t *fcsm, fc_portmap_t *devlist, uint32_t dev_cnt)
1796 fc_portmap_t *map;
1797 uint32_t i;
1799 if (dev_cnt == 0) {
1800 return;
1803 ASSERT(devlist != NULL);
1804 for (i = 0; i < dev_cnt; i++) {
1805 map = &devlist[i];
1806 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1807 "list[%d]: ID 0x%x WWN %x:%x:%x:%x:%x:%x:%x:%x "
1808 "state (0x%x) "
1809 "type <%s>(0x%x) "
1810 "flags (0x%x)",
1811 i, map->map_did.port_id,
1812 map->map_pwwn.raw_wwn[0], map->map_pwwn.raw_wwn[1],
1813 map->map_pwwn.raw_wwn[2], map->map_pwwn.raw_wwn[3],
1814 map->map_pwwn.raw_wwn[4], map->map_pwwn.raw_wwn[5],
1815 map->map_pwwn.raw_wwn[6], map->map_pwwn.raw_wwn[7],
1816 map->map_state,
1817 fcsm_dev_type_to_str(map->map_type), map->map_type,
1818 map->map_flags));
1822 /* ARGSUSED */
1823 static void
1824 fcsm_display(int level, int flags, fcsm_t *fcsm, fc_packet_t *pkt,
1825 const char *fmt, ...)
1827 caddr_t buf;
1828 va_list ap;
1830 buf = kmem_zalloc(256, KM_NOSLEEP);
1831 if (buf == NULL) {
1832 return;
1835 if (fcsm) {
1836 (void) sprintf(buf + strlen(buf), "fcsm(%d): ",
1837 ddi_get_instance(fcsm->sm_port_info.port_dip));
1838 } else {
1839 (void) sprintf(buf, "fcsm: ");
1842 va_start(ap, fmt);
1843 (void) vsprintf(buf + strlen(buf), fmt, ap);
1844 va_end(ap);
1846 if (pkt) {
1847 caddr_t state, reason, action, expln;
1849 (void) fc_ulp_pkt_error(pkt, &state, &reason, &action, &expln);
1851 (void) sprintf(buf + strlen(buf),
1852 " state: %s(0x%x); reason: %s(0x%x)",
1853 state, pkt->pkt_state, reason, pkt->pkt_reason);
1856 switch (flags) {
1857 case SM_LOG:
1858 cmn_err(level, "!%s", buf);
1859 break;
1861 case SM_CONSOLE:
1862 cmn_err(level, "^%s", buf);
1863 break;
1865 default:
1866 cmn_err(level, "%s", buf);
1867 break;
1870 kmem_free(buf, 256);
1875 * Convert FC packet state to FC errno
1878 fcsm_pkt_state_to_rval(uchar_t state, uint32_t reason)
1880 int count;
1882 if (state == FC_PKT_LOCAL_RJT && (reason == FC_REASON_NO_CONNECTION ||
1883 reason == FC_REASON_LOGIN_REQUIRED)) {
1884 return (FC_LOGINREQ);
1885 } else if (state == FC_PKT_PORT_OFFLINE &&
1886 reason == FC_REASON_LOGIN_REQUIRED) {
1887 return (FC_LOGINREQ);
1890 for (count = 0; count < sizeof (fcsm_xlat_pkt_state) /
1891 sizeof (fcsm_xlat_pkt_state[0]); count++) {
1892 if (fcsm_xlat_pkt_state[count].xlat_state == state) {
1893 return (fcsm_xlat_pkt_state[count].xlat_rval);
1897 return (FC_FAILURE);
1902 * Convert port state state to descriptive string
1904 caddr_t
1905 fcsm_port_state_to_str(uint32_t port_state)
1907 int count;
1909 for (count = 0; count < sizeof (fcsm_xlat_port_state) /
1910 sizeof (fcsm_xlat_port_state[0]); count++) {
1911 if (fcsm_xlat_port_state[count].xlat_pstate == port_state) {
1912 return (fcsm_xlat_port_state[count].xlat_state_str);
1916 return (NULL);
1921 * Convert port topology state to descriptive string
1923 caddr_t
1924 fcsm_topology_to_str(uint32_t topology)
1926 int count;
1928 for (count = 0; count < sizeof (fcsm_xlat_topology) /
1929 sizeof (fcsm_xlat_topology[0]); count++) {
1930 if (fcsm_xlat_topology[count].xlat_top == topology) {
1931 return (fcsm_xlat_topology[count].xlat_top_str);
1935 return (NULL);
1940 * Convert port topology state to descriptive string
1942 static caddr_t
1943 fcsm_dev_type_to_str(uint32_t type)
1945 int count;
1947 for (count = 0; count < sizeof (fcsm_xlat_dev_type) /
1948 sizeof (fcsm_xlat_dev_type[0]); count++) {
1949 if (fcsm_xlat_dev_type[count].xlat_type == type) {
1950 return (fcsm_xlat_dev_type[count].xlat_str);
1954 return (NULL);
1957 static int
1958 fcsm_cmd_cache_constructor(void *buf, void *cdarg, int kmflags)
1960 fcsm_cmd_t *cmd = (fcsm_cmd_t *)buf;
1961 fcsm_t *fcsm = (fcsm_t *)cdarg;
1962 int (*callback)(caddr_t);
1963 fc_packet_t *pkt;
1964 fc_ulp_port_info_t *pinfo;
1966 ASSERT(fcsm != NULL && buf != NULL);
1967 callback = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
1969 cmd->cmd_fp_pkt = &cmd->cmd_fc_packet;
1970 cmd->cmd_job = NULL;
1971 cmd->cmd_fcsm = fcsm;
1972 cmd->cmd_dma_flags = 0;
1974 pkt = &cmd->cmd_fc_packet;
1976 pkt->pkt_ulp_rscn_infop = NULL;
1977 pkt->pkt_fca_private = (opaque_t)((caddr_t)cmd + sizeof (fcsm_cmd_t));
1978 pkt->pkt_ulp_private = (opaque_t)cmd;
1980 if (!(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
1981 pinfo = &fcsm->sm_port_info;
1982 if (ddi_dma_alloc_handle(pinfo->port_dip,
1983 pinfo->port_cmd_dma_attr,
1984 callback, NULL, &pkt->pkt_cmd_dma) != DDI_SUCCESS) {
1985 return (1);
1988 if (ddi_dma_alloc_handle(pinfo->port_dip,
1989 pinfo->port_resp_dma_attr,
1990 callback, NULL, &pkt->pkt_resp_dma) != DDI_SUCCESS) {
1991 ddi_dma_free_handle(&pkt->pkt_cmd_dma);
1992 return (1);
1994 } else {
1995 pkt->pkt_cmd_dma = NULL;
1996 pkt->pkt_cmd = NULL;
1997 pkt->pkt_resp_dma = NULL;
1998 pkt->pkt_resp = NULL;
2001 pkt->pkt_cmd_acc = pkt->pkt_resp_acc = NULL;
2002 pkt->pkt_cmd_cookie_cnt = pkt->pkt_resp_cookie_cnt =
2003 pkt->pkt_data_cookie_cnt = 0;
2004 pkt->pkt_cmd_cookie = pkt->pkt_resp_cookie =
2005 pkt->pkt_data_cookie = NULL;
2007 return (0);
2011 /* ARGSUSED */
2012 static void
2013 fcsm_cmd_cache_destructor(void *buf, void *cdarg)
2015 fcsm_cmd_t *cmd = (fcsm_cmd_t *)buf;
2016 fcsm_t *fcsm = (fcsm_t *)cdarg;
2017 fc_packet_t *pkt;
2019 ASSERT(fcsm == cmd->cmd_fcsm);
2021 pkt = cmd->cmd_fp_pkt;
2023 if (pkt->pkt_cmd_dma != NULL) {
2024 ddi_dma_free_handle(&pkt->pkt_cmd_dma);
2027 if (pkt->pkt_resp_dma != NULL) {
2028 ddi_dma_free_handle(&pkt->pkt_resp_dma);
2033 static fcsm_cmd_t *
2034 fcsm_alloc_cmd(fcsm_t *fcsm, uint32_t cmd_len, uint32_t resp_len, int sleep)
2036 fcsm_cmd_t *cmd;
2037 fc_packet_t *pkt;
2038 int rval;
2039 ulong_t real_len;
2040 int (*callback)(caddr_t);
2041 ddi_dma_cookie_t pkt_cookie;
2042 ddi_dma_cookie_t *cp;
2043 uint32_t cnt;
2044 fc_ulp_port_info_t *pinfo;
2046 ASSERT(fcsm != NULL);
2047 pinfo = &fcsm->sm_port_info;
2049 callback = (sleep == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
2051 cmd = (fcsm_cmd_t *)kmem_cache_alloc(fcsm->sm_cmd_cache, sleep);
2052 if (cmd == NULL) {
2053 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, fcsm, NULL,
2054 "alloc_cmd: kmem_cache_alloc failed"));
2055 return (NULL);
2058 cmd->cmd_retry_count = 0;
2059 cmd->cmd_max_retries = 0;
2060 cmd->cmd_retry_interval = 0;
2061 cmd->cmd_transport = NULL;
2063 ASSERT(cmd->cmd_dma_flags == 0);
2064 ASSERT(cmd->cmd_fp_pkt == &cmd->cmd_fc_packet);
2065 pkt = cmd->cmd_fp_pkt;
2067 /* Zero out the important fc_packet fields */
2068 pkt->pkt_pd = NULL;
2069 pkt->pkt_datalen = 0;
2070 pkt->pkt_data = NULL;
2071 pkt->pkt_state = 0;
2072 pkt->pkt_action = 0;
2073 pkt->pkt_reason = 0;
2074 pkt->pkt_expln = 0;
2077 * Now that pkt_pd is initialized, we can call fc_ulp_init_packet
2080 if (fc_ulp_init_packet((opaque_t)pinfo->port_handle, pkt, sleep)
2081 != FC_SUCCESS) {
2082 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2083 return (NULL);
2086 if ((cmd_len) && !(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
2087 ASSERT(pkt->pkt_cmd_dma != NULL);
2089 rval = ddi_dma_mem_alloc(pkt->pkt_cmd_dma, cmd_len,
2090 fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
2091 callback, NULL, (caddr_t *)&pkt->pkt_cmd, &real_len,
2092 &pkt->pkt_cmd_acc);
2094 if (rval != DDI_SUCCESS) {
2095 (void) fc_ulp_uninit_packet(
2096 (opaque_t)pinfo->port_handle, pkt);
2097 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2098 fcsm_free_cmd_dma(cmd);
2099 return (NULL);
2102 cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_MEM;
2104 if (real_len < cmd_len) {
2105 (void) fc_ulp_uninit_packet(
2106 (opaque_t)pinfo->port_handle, pkt);
2107 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2108 fcsm_free_cmd_dma(cmd);
2109 return (NULL);
2112 rval = ddi_dma_addr_bind_handle(pkt->pkt_cmd_dma, NULL,
2113 pkt->pkt_cmd, real_len, DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
2114 callback, NULL, &pkt_cookie, &pkt->pkt_cmd_cookie_cnt);
2116 if (rval != DDI_DMA_MAPPED) {
2117 (void) fc_ulp_uninit_packet(
2118 (opaque_t)pinfo->port_handle, pkt);
2119 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2120 fcsm_free_cmd_dma(cmd);
2121 return (NULL);
2124 cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_BIND;
2126 if (pkt->pkt_cmd_cookie_cnt >
2127 pinfo->port_cmd_dma_attr->dma_attr_sgllen) {
2128 (void) fc_ulp_uninit_packet(
2129 (opaque_t)pinfo->port_handle, pkt);
2130 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2131 fcsm_free_cmd_dma(cmd);
2132 return (NULL);
2135 ASSERT(pkt->pkt_cmd_cookie_cnt != 0);
2137 cp = pkt->pkt_cmd_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2138 pkt->pkt_cmd_cookie_cnt * sizeof (pkt_cookie),
2139 KM_NOSLEEP);
2141 if (cp == NULL) {
2142 (void) fc_ulp_uninit_packet(
2143 (opaque_t)pinfo->port_handle, pkt);
2144 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2145 fcsm_free_cmd_dma(cmd);
2146 return (NULL);
2149 *cp = pkt_cookie;
2150 cp++;
2151 for (cnt = 1; cnt < pkt->pkt_cmd_cookie_cnt; cnt++, cp++) {
2152 ddi_dma_nextcookie(pkt->pkt_cmd_dma, &pkt_cookie);
2153 *cp = pkt_cookie;
2155 } else if (cmd_len != 0) {
2156 pkt->pkt_cmd = kmem_zalloc(cmd_len, KM_SLEEP);
2159 if ((resp_len) && !(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
2160 ASSERT(pkt->pkt_resp_dma != NULL);
2162 rval = ddi_dma_mem_alloc(pkt->pkt_resp_dma, resp_len,
2163 fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
2164 callback, NULL, (caddr_t *)&pkt->pkt_resp, &real_len,
2165 &pkt->pkt_resp_acc);
2167 if (rval != DDI_SUCCESS) {
2168 (void) fc_ulp_uninit_packet(
2169 (opaque_t)pinfo->port_handle, pkt);
2170 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2171 fcsm_free_cmd_dma(cmd);
2172 return (NULL);
2175 cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_MEM;
2177 if (real_len < resp_len) {
2178 (void) fc_ulp_uninit_packet(
2179 (opaque_t)pinfo->port_handle, pkt);
2180 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2181 fcsm_free_cmd_dma(cmd);
2182 return (NULL);
2185 rval = ddi_dma_addr_bind_handle(pkt->pkt_resp_dma, NULL,
2186 pkt->pkt_resp, real_len, DDI_DMA_READ | DDI_DMA_CONSISTENT,
2187 callback, NULL, &pkt_cookie, &pkt->pkt_resp_cookie_cnt);
2189 if (rval != DDI_DMA_MAPPED) {
2190 (void) fc_ulp_uninit_packet(
2191 (opaque_t)pinfo->port_handle, pkt);
2192 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2193 fcsm_free_cmd_dma(cmd);
2194 return (NULL);
2197 cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_BIND;
2199 if (pkt->pkt_resp_cookie_cnt >
2200 pinfo->port_resp_dma_attr->dma_attr_sgllen) {
2201 (void) fc_ulp_uninit_packet(
2202 (opaque_t)pinfo->port_handle, pkt);
2203 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2204 fcsm_free_cmd_dma(cmd);
2205 return (NULL);
2208 ASSERT(pkt->pkt_resp_cookie_cnt != 0);
2210 cp = pkt->pkt_resp_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2211 pkt->pkt_resp_cookie_cnt * sizeof (pkt_cookie),
2212 KM_NOSLEEP);
2214 if (cp == NULL) {
2215 (void) fc_ulp_uninit_packet(
2216 (opaque_t)pinfo->port_handle, pkt);
2217 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2218 fcsm_free_cmd_dma(cmd);
2219 return (NULL);
2222 *cp = pkt_cookie;
2223 cp++;
2224 for (cnt = 1; cnt < pkt->pkt_resp_cookie_cnt; cnt++, cp++) {
2225 ddi_dma_nextcookie(pkt->pkt_resp_dma, &pkt_cookie);
2226 *cp = pkt_cookie;
2228 } else if (resp_len != 0) {
2229 pkt->pkt_resp = kmem_zalloc(resp_len, KM_SLEEP);
2232 pkt->pkt_cmdlen = cmd_len;
2233 pkt->pkt_rsplen = resp_len;
2235 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2236 "alloc_cmd: cmd 0x%p", (void *)cmd));
2237 return (cmd);
2240 static void
2241 fcsm_free_cmd(fcsm_cmd_t *cmd)
2243 fcsm_t *fcsm;
2245 fcsm = cmd->cmd_fcsm;
2246 ASSERT(fcsm != NULL);
2248 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2249 "free_cmd: cmd 0x%p", (void *)cmd));
2251 fcsm_free_cmd_dma(cmd);
2253 (void) fc_ulp_uninit_packet((opaque_t)fcsm->sm_port_info.port_handle,
2254 cmd->cmd_fp_pkt);
2255 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2258 static void
2259 fcsm_free_cmd_dma(fcsm_cmd_t *cmd)
2261 fc_packet_t *pkt;
2263 pkt = cmd->cmd_fp_pkt;
2264 ASSERT(pkt != NULL);
2266 if (cmd->cmd_fcsm->sm_flags & FCSM_USING_NODMA_FCA) {
2267 if (pkt->pkt_cmd) {
2268 kmem_free(pkt->pkt_cmd, pkt->pkt_cmdlen);
2269 pkt->pkt_cmd = NULL;
2272 if (pkt->pkt_resp) {
2273 kmem_free(pkt->pkt_resp, pkt->pkt_rsplen);
2274 pkt->pkt_resp = NULL;
2278 pkt->pkt_cmdlen = 0;
2279 pkt->pkt_rsplen = 0;
2280 pkt->pkt_tran_type = 0;
2281 pkt->pkt_tran_flags = 0;
2283 if (pkt->pkt_cmd_cookie != NULL) {
2284 kmem_free(pkt->pkt_cmd_cookie, pkt->pkt_cmd_cookie_cnt *
2285 sizeof (ddi_dma_cookie_t));
2286 pkt->pkt_cmd_cookie = NULL;
2289 if (pkt->pkt_resp_cookie != NULL) {
2290 kmem_free(pkt->pkt_resp_cookie, pkt->pkt_resp_cookie_cnt *
2291 sizeof (ddi_dma_cookie_t));
2292 pkt->pkt_resp_cookie = NULL;
2295 if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_BIND) {
2296 (void) ddi_dma_unbind_handle(pkt->pkt_cmd_dma);
2299 if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_MEM) {
2300 if (pkt->pkt_cmd_acc) {
2301 ddi_dma_mem_free(&pkt->pkt_cmd_acc);
2305 if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_BIND) {
2306 (void) ddi_dma_unbind_handle(pkt->pkt_resp_dma);
2309 if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_MEM) {
2310 if (pkt->pkt_resp_acc) {
2311 ddi_dma_mem_free(&pkt->pkt_resp_acc);
2315 cmd->cmd_dma_flags = 0;
2318 /* ARGSUSED */
2319 static int
2320 fcsm_job_cache_constructor(void *buf, void *cdarg, int kmflag)
2322 fcsm_job_t *job = (fcsm_job_t *)buf;
2324 mutex_init(&job->job_mutex, NULL, MUTEX_DRIVER, NULL);
2325 sema_init(&job->job_sema, 0, NULL, SEMA_DEFAULT, NULL);
2327 return (0);
2330 /* ARGSUSED */
2331 static void
2332 fcsm_job_cache_destructor(void *buf, void *cdarg)
2334 fcsm_job_t *job = (fcsm_job_t *)buf;
2336 sema_destroy(&job->job_sema);
2337 mutex_destroy(&job->job_mutex);
2341 static fcsm_job_t *
2342 fcsm_alloc_job(int sleep)
2344 fcsm_job_t *job;
2346 job = (fcsm_job_t *)kmem_cache_alloc(fcsm_job_cache, sleep);
2347 if (job != NULL) {
2348 job->job_code = FCSM_JOB_NONE;
2349 job->job_flags = 0;
2350 job->job_port_instance = -1;
2351 job->job_result = -1;
2352 job->job_arg = (opaque_t)0;
2353 job->job_caller_priv = (opaque_t)0;
2354 job->job_comp = NULL;
2355 job->job_comp_arg = (opaque_t)0;
2356 job->job_priv = NULL;
2357 job->job_priv_flags = 0;
2358 job->job_next = 0;
2361 return (job);
2364 static void
2365 fcsm_dealloc_job(fcsm_job_t *job)
2367 kmem_cache_free(fcsm_job_cache, (void *)job);
2371 static void
2372 fcsm_init_job(fcsm_job_t *job, int instance, uint32_t command, uint32_t flags,
2373 opaque_t arg, opaque_t caller_priv,
2374 void (*comp)(opaque_t, fcsm_job_t *, int), opaque_t comp_arg)
2376 ASSERT(job != NULL);
2377 job->job_port_instance = instance;
2378 job->job_code = command;
2379 job->job_flags = flags;
2380 job->job_arg = arg;
2381 job->job_caller_priv = caller_priv;
2382 job->job_comp = comp;
2383 job->job_comp_arg = comp_arg;
2384 job->job_retry_count = 0;
2387 static int
2388 fcsm_process_job(fcsm_job_t *job, int priority_flag)
2390 fcsm_t *fcsm;
2391 int sync;
2393 ASSERT(job != NULL);
2394 ASSERT(!MUTEX_HELD(&job->job_mutex));
2396 fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2398 if (fcsm == NULL) {
2399 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
2400 "process_job: port instance 0x%x not found",
2401 job->job_port_instance));
2402 return (FC_BADDEV);
2405 mutex_enter(&job->job_mutex);
2406 /* Both SYNC and ASYNC flags should not be set */
2407 ASSERT(((job->job_flags & (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) ==
2408 FCSM_JOBFLAG_SYNC) || ((job->job_flags &
2409 (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) == FCSM_JOBFLAG_ASYNC));
2411 * Check if job is a synchronous job. We might not be able to
2412 * check it reliably after enque_job(), if job is an ASYNC job.
2414 sync = job->job_flags & FCSM_JOBFLAG_SYNC;
2415 mutex_exit(&job->job_mutex);
2417 /* Queue the job for processing by job thread */
2418 fcsm_enque_job(fcsm, job, priority_flag);
2420 /* Wait for job completion, if it is a synchronous job */
2421 if (sync) {
2423 * This is a Synchronous Job. So job structure is available.
2424 * Caller is responsible for freeing it.
2426 FCSM_DEBUG(SMDL_ERR, (CE_CONT, SM_LOG, fcsm, NULL,
2427 "process_job: Waiting for sync job <%p> completion",
2428 (void *)job));
2429 sema_p(&job->job_sema);
2432 return (FC_SUCCESS);
2435 static void
2436 fcsm_enque_job(fcsm_t *fcsm, fcsm_job_t *job, int priority_flag)
2438 ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
2440 mutex_enter(&fcsm->sm_mutex);
2441 /* Queue the job at the head or tail depending on the job priority */
2442 if (priority_flag) {
2443 FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
2444 "enque_job: job 0x%p is high priority", job));
2445 /* Queue at the head */
2446 if (fcsm->sm_job_tail == NULL) {
2447 ASSERT(fcsm->sm_job_head == NULL);
2448 fcsm->sm_job_head = fcsm->sm_job_tail = job;
2449 } else {
2450 ASSERT(fcsm->sm_job_head != NULL);
2451 job->job_next = fcsm->sm_job_head;
2452 fcsm->sm_job_head = job;
2454 } else {
2455 FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
2456 "enque_job: job 0x%p is normal", job));
2457 /* Queue at the tail */
2458 if (fcsm->sm_job_tail == NULL) {
2459 ASSERT(fcsm->sm_job_head == NULL);
2460 fcsm->sm_job_head = fcsm->sm_job_tail = job;
2461 } else {
2462 ASSERT(fcsm->sm_job_head != NULL);
2463 fcsm->sm_job_tail->job_next = job;
2464 fcsm->sm_job_tail = job;
2466 job->job_next = NULL;
2469 /* Signal the job thread to process the job */
2470 cv_signal(&fcsm->sm_job_cv);
2471 mutex_exit(&fcsm->sm_mutex);
2474 static int
2475 fcsm_retry_job(fcsm_t *fcsm, fcsm_job_t *job)
2478 * If it is a CT passthru job and status is login required, then
2479 * retry the job so that login can be performed again.
2480 * Ensure that this retry is performed a finite number of times,
2481 * so that a faulty fabric does not cause us to retry forever.
2484 switch (job->job_code) {
2485 case FCSM_JOB_CT_PASSTHRU: {
2486 uint32_t jobflag;
2487 fc_ct_header_t *ct_header;
2489 if (job->job_result != FC_LOGINREQ) {
2490 break;
2494 * If it is a management server command
2495 * then Reset the Management server login flag, so that login
2496 * gets re-established.
2497 * If it is a Name server command,
2498 * then it is 'fp' responsibility to perform the login.
2500 ASSERT(job->job_arg != NULL);
2501 ct_header =
2502 (fc_ct_header_t *)((fcio_t *)job->job_arg)->fcio_ibuf;
2503 if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
2504 mutex_enter(&fcsm->sm_mutex);
2505 fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
2506 mutex_exit(&fcsm->sm_mutex);
2509 if (job->job_retry_count >= fcsm_max_job_retries) {
2510 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2511 "retry_job: job 0x%p max retries (%d) reached",
2512 (void *)job, job->job_retry_count));
2513 break;
2517 * Login is required again. Retry the command, so that
2518 * login will get performed again.
2520 mutex_enter(&job->job_mutex);
2521 job->job_retry_count++;
2522 jobflag = job->job_flags;
2523 mutex_exit(&job->job_mutex);
2525 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2526 "retry_job: retry(%d) job 0x%p",
2527 job->job_retry_count, (void *)job));
2529 * This job should get picked up before the
2530 * other jobs sitting in the queue.
2531 * Requeue the command at the head and then
2532 * reset the SERIALIZE flag.
2534 fcsm_enque_job(fcsm, job, 1);
2535 if (jobflag & FCSM_JOBFLAG_SERIALIZE) {
2536 mutex_enter(&fcsm->sm_mutex);
2537 ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
2538 fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
2540 /* Signal the job thread to process the job */
2541 cv_signal(&fcsm->sm_job_cv);
2542 mutex_exit(&fcsm->sm_mutex);
2545 /* Command is queued for retrying */
2546 return (0);
2549 default:
2550 break;
2552 return (1);
2555 static void
2556 fcsm_jobdone(fcsm_job_t *job)
2558 fcsm_t *fcsm;
2560 fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2561 ASSERT(fcsm != NULL);
2563 if (job->job_result != FC_SUCCESS) {
2564 if (fcsm_retry_job(fcsm, job) == 0) {
2565 /* Job retried. so just return from here */
2566 return;
2570 if (job->job_comp) {
2571 job->job_comp(job->job_comp_arg, job, job->job_result);
2574 mutex_enter(&job->job_mutex);
2575 if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
2576 mutex_exit(&job->job_mutex);
2577 mutex_enter(&fcsm->sm_mutex);
2578 ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
2579 fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
2581 /* Signal the job thread to process the job */
2582 cv_signal(&fcsm->sm_job_cv);
2583 mutex_exit(&fcsm->sm_mutex);
2584 mutex_enter(&job->job_mutex);
2587 if (job->job_flags & FCSM_JOBFLAG_SYNC) {
2588 mutex_exit(&job->job_mutex);
2589 sema_v(&job->job_sema);
2590 } else {
2591 mutex_exit(&job->job_mutex);
2592 /* Async job, free the job structure */
2593 fcsm_dealloc_job(job);
2597 fcsm_job_t *
2598 fcsm_deque_job(fcsm_t *fcsm)
2600 fcsm_job_t *job;
2602 ASSERT(MUTEX_HELD(&fcsm->sm_mutex));
2604 if (fcsm->sm_job_head == NULL) {
2605 ASSERT(fcsm->sm_job_tail == NULL);
2606 job = NULL;
2607 } else {
2608 ASSERT(fcsm->sm_job_tail != NULL);
2609 job = fcsm->sm_job_head;
2610 if (job->job_next == NULL) {
2611 ASSERT(fcsm->sm_job_tail == job);
2612 fcsm->sm_job_tail = NULL;
2614 fcsm->sm_job_head = job->job_next;
2615 job->job_next = NULL;
2618 return (job);
2622 /* Dedicated per port thread to process various commands */
2623 static void
2624 fcsm_job_thread(fcsm_t *fcsm)
2626 fcsm_job_t *job;
2628 ASSERT(fcsm != NULL);
2629 #ifndef __lock_lint
2630 CALLB_CPR_INIT(&fcsm->sm_cpr_info, &fcsm->sm_mutex,
2631 callb_generic_cpr, "fcsm_job_thread");
2632 #endif /* __lock_lint */
2634 for (;;) {
2635 mutex_enter(&fcsm->sm_mutex);
2637 while (fcsm->sm_job_head == NULL ||
2638 fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD) {
2639 CALLB_CPR_SAFE_BEGIN(&fcsm->sm_cpr_info);
2640 cv_wait(&fcsm->sm_job_cv, &fcsm->sm_mutex);
2641 CALLB_CPR_SAFE_END(&fcsm->sm_cpr_info, &fcsm->sm_mutex);
2644 job = fcsm_deque_job(fcsm);
2646 mutex_exit(&fcsm->sm_mutex);
2648 mutex_enter(&job->job_mutex);
2649 if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
2650 mutex_exit(&job->job_mutex);
2652 mutex_enter(&fcsm->sm_mutex);
2653 ASSERT(!(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD));
2654 fcsm->sm_flags |= FCSM_SERIALIZE_JOBTHREAD;
2655 mutex_exit(&fcsm->sm_mutex);
2656 } else {
2657 mutex_exit(&job->job_mutex);
2660 ASSERT(fcsm->sm_instance == job->job_port_instance);
2662 switch (job->job_code) {
2663 case FCSM_JOB_NONE:
2664 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
2665 "job_thread: uninitialized job code");
2666 job->job_result = FC_FAILURE;
2667 fcsm_jobdone(job);
2668 break;
2670 case FCSM_JOB_THREAD_SHUTDOWN:
2671 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2672 "job_thread: job code <JOB PORT SHUTDOWN>"));
2675 * There should not be any pending jobs, when this
2676 * is being called.
2678 mutex_enter(&fcsm->sm_mutex);
2679 ASSERT(fcsm->sm_job_head == NULL);
2680 ASSERT(fcsm->sm_job_tail == NULL);
2681 ASSERT(fcsm->sm_retry_head == NULL);
2682 ASSERT(fcsm->sm_retry_tail == NULL);
2683 job->job_result = FC_SUCCESS;
2684 #ifndef __lock_lint
2685 CALLB_CPR_EXIT(&fcsm->sm_cpr_info);
2686 #endif
2687 /* CPR_EXIT has also dropped the fcsm->sm_mutex */
2689 fcsm_jobdone(job);
2690 thread_exit();
2691 /* NOTREACHED */
2692 break;
2694 case FCSM_JOB_LOGIN_NAME_SERVER:
2695 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2696 "job_thread: job code <LOGIN_NAME_SERVER>"));
2697 job->job_result = FC_SUCCESS;
2698 fcsm_jobdone(job);
2699 break;
2701 case FCSM_JOB_LOGIN_MGMT_SERVER:
2702 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2703 "job_thread: job code <LOGIN_MGMT_SERVER>"));
2704 fcsm_job_login_mgmt_server(job);
2705 break;
2707 case FCSM_JOB_CT_PASSTHRU:
2708 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2709 "job_thread: job code <CT_PASSTHRU>"));
2710 fcsm_job_ct_passthru(job);
2711 break;
2713 default:
2714 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2715 "job_thread: job code <UNKNOWN>"));
2716 job->job_result = FC_FAILURE;
2717 fcsm_jobdone(job);
2718 break;
2722 /* NOTREACHED */
2726 static void
2727 fcsm_ct_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, fc_ct_aiu_t *req_iu, size_t req_len,
2728 void (*comp_func)())
2730 fc_packet_t *pkt;
2732 pkt = cmd->cmd_fp_pkt;
2733 ASSERT(pkt != NULL);
2735 ASSERT(req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE ||
2736 (req_iu->aiu_header.ct_fcstype == FCSTYPE_DIRECTORY &&
2737 req_iu->aiu_header.ct_fcssubtype == FCSSUB_DS_NAME_SERVER));
2740 /* Set the pkt d_id properly */
2741 if (req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE) {
2742 pkt->pkt_cmd_fhdr.d_id = FS_MANAGEMENT_SERVER;
2743 } else {
2744 pkt->pkt_cmd_fhdr.d_id = FS_NAME_SERVER;
2747 pkt->pkt_cmd_fhdr.r_ctl = R_CTL_UNSOL_CONTROL;
2748 pkt->pkt_cmd_fhdr.rsvd = 0;
2749 pkt->pkt_cmd_fhdr.s_id = fcsm->sm_sid;
2750 pkt->pkt_cmd_fhdr.type = FC_TYPE_FC_SERVICES;
2751 pkt->pkt_cmd_fhdr.f_ctl = F_CTL_SEQ_INITIATIVE |
2752 F_CTL_FIRST_SEQ | F_CTL_END_SEQ;
2753 pkt->pkt_cmd_fhdr.seq_id = 0;
2754 pkt->pkt_cmd_fhdr.df_ctl = 0;
2755 pkt->pkt_cmd_fhdr.seq_cnt = 0;
2756 pkt->pkt_cmd_fhdr.ox_id = 0xffff;
2757 pkt->pkt_cmd_fhdr.rx_id = 0xffff;
2758 pkt->pkt_cmd_fhdr.ro = 0;
2760 pkt->pkt_timeout = FCSM_MS_TIMEOUT;
2761 pkt->pkt_comp = comp_func;
2763 FCSM_REP_WR(pkt->pkt_cmd_acc, req_iu, pkt->pkt_cmd, req_len);
2765 cmd->cmd_transport = fc_ulp_transport;
2768 static void
2769 fcsm_ct_intr(fcsm_cmd_t *cmd)
2771 fc_packet_t *pkt;
2772 fcsm_job_t *job;
2773 fcio_t *fcio;
2774 fcsm_t *fcsm;
2776 pkt = cmd->cmd_fp_pkt;
2777 job = cmd->cmd_job;
2778 ASSERT(job != NULL);
2780 fcio = job->job_arg;
2781 ASSERT(fcio != NULL);
2783 if (pkt->pkt_state != FC_PKT_SUCCESS) {
2784 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, cmd->cmd_fcsm, pkt,
2785 "ct_intr: CT command <0x%x> to did 0x%x failed",
2786 ((fc_ct_aiu_t *)fcio->fcio_ibuf)->aiu_header.ct_cmdrsp,
2787 pkt->pkt_cmd_fhdr.d_id));
2788 } else {
2789 /* Get the CT response payload */
2790 fcsm = cmd->cmd_fcsm;
2791 FCSM_REP_RD(pkt->pkt_resp_acc, fcio->fcio_obuf,
2792 pkt->pkt_resp, fcio->fcio_olen);
2795 job->job_result =
2796 fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
2798 fcsm_free_cmd(cmd);
2800 fcsm_jobdone(job);
2804 static void
2805 fcsm_job_ct_passthru(fcsm_job_t *job)
2807 fcsm_t *fcsm;
2808 fcio_t *fcio;
2809 fcsm_cmd_t *cmd;
2810 int status;
2811 fc_ct_header_t *ct_header;
2813 ASSERT(job != NULL);
2814 ASSERT(job->job_port_instance != -1);
2816 job->job_result = FC_FAILURE;
2817 fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2818 if (fcsm == NULL) {
2819 fcsm_jobdone(job);
2820 return;
2824 * Process the CT Passthru job only if port is attached
2825 * to a FABRIC.
2827 if (!FC_TOP_EXTERNAL(fcsm->sm_port_top)) {
2828 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2829 "job_ct_passthru: end (non-fabric port)"));
2830 job->job_result = FC_BADDEV;
2831 fcsm_jobdone(job);
2832 return;
2835 fcio = job->job_arg;
2836 ASSERT(fcio != NULL);
2839 * If it is NOT a Management Seriver (MS) or Name Server (NS) command
2840 * then complete the command with failure.
2842 ct_header = (fc_ct_header_t *)fcio->fcio_ibuf;
2845 * According to libHBAAPI spec, CT header from libHBAAPI would always
2846 * be big endian, so we must swap CT header before continue in little
2847 * endian platforms.
2849 mutex_enter(&job->job_mutex);
2850 if (!(job->job_flags & FCSM_JOBFLAG_CTHEADER_BE)) {
2851 job->job_flags |= FCSM_JOBFLAG_CTHEADER_BE;
2852 *((uint32_t *)((uint32_t *)ct_header + 0)) =
2853 BE_32(*((uint32_t *)((uint32_t *)ct_header + 0)));
2854 *((uint32_t *)((uint32_t *)ct_header + 1)) =
2855 BE_32(*((uint32_t *)((uint32_t *)ct_header + 1)));
2856 *((uint32_t *)((uint32_t *)ct_header + 2)) =
2857 BE_32(*((uint32_t *)((uint32_t *)ct_header + 2)));
2858 *((uint32_t *)((uint32_t *)ct_header + 3)) =
2859 BE_32(*((uint32_t *)((uint32_t *)ct_header + 3)));
2861 mutex_exit(&job->job_mutex);
2863 if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
2864 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2865 "job_ct_passthru: Management Server Cmd"));
2866 } else if (ct_header->ct_fcstype == FCSTYPE_DIRECTORY) {
2867 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2868 "job_ct_passthru: Name Server Cmd"));
2869 } else {
2870 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2871 "job_ct_passthru: Unsupported Destination "
2872 "gs_type <0x%x> gs_subtype <0x%x>",
2873 ct_header->ct_fcstype, ct_header->ct_fcssubtype));
2876 if (ct_header->ct_fcstype != FCSTYPE_MGMTSERVICE &&
2877 (ct_header->ct_fcstype != FCSTYPE_DIRECTORY ||
2878 ct_header->ct_fcssubtype != FCSSUB_DS_NAME_SERVER)) {
2879 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2880 "job_ct_passthru: end (Not a Name Server OR "
2881 "Mgmt Server Cmd)"));
2882 job->job_result = FC_BADCMD;
2883 fcsm_jobdone(job);
2884 return;
2888 * If it is an MS command and we are not logged in to the management
2889 * server, then start the login and requeue the command.
2890 * If login to management server is in progress, then reque the
2891 * command to wait for login to complete.
2893 mutex_enter(&fcsm->sm_mutex);
2894 if ((ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) &&
2895 !(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN)) {
2896 mutex_exit(&fcsm->sm_mutex);
2897 if (fcsm_login_and_process_job(fcsm, job) != FC_SUCCESS) {
2898 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2899 "job_ct_passthru: perform login failed"));
2900 job->job_result = FC_FAILURE;
2901 fcsm_jobdone(job);
2903 return;
2905 mutex_exit(&fcsm->sm_mutex);
2908 * We are already logged in to the management server.
2909 * Issue the CT Passthru command
2911 cmd = fcsm_alloc_cmd(fcsm, fcio->fcio_ilen, fcio->fcio_olen, KM_SLEEP);
2912 if (cmd == NULL) {
2913 job->job_result = FC_NOMEM;
2914 fcsm_jobdone(job);
2915 return;
2918 FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
2919 fcsm_max_cmd_retries, fcsm_ct_intr);
2921 fcsm_ct_init(fcsm, cmd, (fc_ct_aiu_t *)fcio->fcio_ibuf, fcio->fcio_ilen,
2922 fcsm_pkt_common_intr);
2924 if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
2925 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
2926 "job_ct_passthru: issue CT Passthru failed, status 0x%x",
2927 status));
2928 job->job_result = status;
2929 fcsm_free_cmd(cmd);
2930 fcsm_jobdone(job);
2931 return;
2935 static int
2936 fcsm_login_and_process_job(fcsm_t *fcsm, fcsm_job_t *orig_job)
2938 fcsm_job_t *login_job;
2939 #ifdef DEBUG
2940 int status;
2941 #endif /* DEBUG */
2943 if (orig_job->job_code != FCSM_JOB_CT_PASSTHRU) {
2944 return (FC_FAILURE);
2947 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2948 "login_and_process_job: start login."));
2950 mutex_enter(&fcsm->sm_mutex);
2951 if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) {
2953 * Directory server login completed just now, while the
2954 * mutex was dropped. Just queue the command again for
2955 * processing.
2957 mutex_exit(&fcsm->sm_mutex);
2958 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2959 "login_and_process_job: got job 0x%p. login just "
2960 "completed", (void *)orig_job));
2961 fcsm_enque_job(fcsm, orig_job, 0);
2962 return (FC_SUCCESS);
2965 if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG) {
2967 * Ideally we shouldn't have come here, since login
2968 * job has the serialize flag set.
2969 * Anyway, put the command back on the queue.
2971 mutex_exit(&fcsm->sm_mutex);
2972 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2973 "login_and_process_job: got job 0x%p while login to "
2974 "management server in progress", (void *)orig_job));
2975 fcsm_enque_job(fcsm, orig_job, 0);
2976 return (FC_SUCCESS);
2979 fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGIN_IN_PROG;
2980 mutex_exit(&fcsm->sm_mutex);
2982 login_job = fcsm_alloc_job(KM_SLEEP);
2983 ASSERT(login_job != NULL);
2986 * Mark the login job as SERIALIZE, so that all other jobs will
2987 * be processed after completing the login.
2988 * Save the original job (CT Passthru job) in the caller private
2989 * field in the job structure, so that CT command can be issued
2990 * after login has completed.
2992 fcsm_init_job(login_job, fcsm->sm_instance, FCSM_JOB_LOGIN_MGMT_SERVER,
2993 FCSM_JOBFLAG_ASYNC | FCSM_JOBFLAG_SERIALIZE,
2994 (opaque_t)NULL, (opaque_t)orig_job, fcsm_login_ms_comp, NULL);
2995 orig_job->job_priv = (void *)login_job;
2997 #ifdef DEBUG
2998 status = fcsm_process_job(login_job, 1);
2999 ASSERT(status == FC_SUCCESS);
3000 #else /* DEBUG */
3001 (void) fcsm_process_job(login_job, 1);
3002 #endif /* DEBUG */
3003 return (FC_SUCCESS);
3007 /* ARGSUSED */
3008 static void
3009 fcsm_login_ms_comp(opaque_t comp_arg, fcsm_job_t *login_job, int result)
3011 fcsm_t *fcsm;
3012 fcsm_job_t *orig_job;
3014 ASSERT(login_job != NULL);
3016 orig_job = (fcsm_job_t *)login_job->job_caller_priv;
3018 ASSERT(orig_job != NULL);
3019 ASSERT(orig_job->job_priv == (void *)login_job);
3020 orig_job->job_priv = NULL;
3022 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3023 "login_ms_comp: result 0x%x", login_job->job_result));
3025 /* Set the login flag in the per port fcsm structure */
3026 ASSERT(login_job->job_port_instance == orig_job->job_port_instance);
3027 fcsm = ddi_get_soft_state(fcsm_state, login_job->job_port_instance);
3028 ASSERT(fcsm != NULL);
3030 mutex_enter(&fcsm->sm_mutex);
3031 ASSERT((fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) == 0);
3032 ASSERT(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG);
3033 fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGIN_IN_PROG;
3034 if (login_job->job_result != FC_SUCCESS) {
3035 caddr_t msg;
3038 * Login failed. Complete the original job with FC_LOGINREQ
3039 * status. Retry of that job will cause login to be
3040 * retried.
3042 mutex_exit(&fcsm->sm_mutex);
3043 orig_job->job_result = FC_LOGINREQ;
3044 fcsm_jobdone(orig_job);
3046 (void) fc_ulp_error(login_job->job_result, &msg);
3047 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
3048 "login_ms_comp: Management server login failed: <%s>", msg);
3049 return;
3051 fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGGED_IN;
3052 mutex_exit(&fcsm->sm_mutex);
3055 * Queue the original job at the head of the queue for processing.
3057 fcsm_enque_job(fcsm, orig_job, 1);
3061 static void
3062 fcsm_els_init(fcsm_cmd_t *cmd, uint32_t d_id)
3064 fc_packet_t *pkt;
3065 fcsm_t *fcsm;
3067 fcsm = cmd->cmd_fcsm;
3068 pkt = cmd->cmd_fp_pkt;
3069 ASSERT(fcsm != NULL && pkt != NULL);
3071 pkt->pkt_cmd_fhdr.r_ctl = R_CTL_ELS_REQ;
3072 pkt->pkt_cmd_fhdr.d_id = d_id;
3073 pkt->pkt_cmd_fhdr.rsvd = 0;
3074 pkt->pkt_cmd_fhdr.s_id = fcsm->sm_sid;
3075 pkt->pkt_cmd_fhdr.type = FC_TYPE_EXTENDED_LS;
3076 pkt->pkt_cmd_fhdr.f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
3077 pkt->pkt_cmd_fhdr.seq_id = 0;
3078 pkt->pkt_cmd_fhdr.df_ctl = 0;
3079 pkt->pkt_cmd_fhdr.seq_cnt = 0;
3080 pkt->pkt_cmd_fhdr.ox_id = 0xffff;
3081 pkt->pkt_cmd_fhdr.rx_id = 0xffff;
3082 pkt->pkt_cmd_fhdr.ro = 0;
3084 pkt->pkt_timeout = FCSM_ELS_TIMEOUT;
3088 static int
3089 fcsm_xlogi_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, uint32_t d_id,
3090 void (*comp_func)(), uchar_t ls_code)
3092 ls_code_t payload;
3093 fc_packet_t *pkt;
3094 la_els_logi_t *login_params;
3095 int status;
3097 login_params = (la_els_logi_t *)
3098 kmem_zalloc(sizeof (la_els_logi_t), KM_SLEEP);
3099 if (login_params == NULL) {
3100 return (FC_NOMEM);
3103 status = fc_ulp_get_port_login_params(fcsm->sm_port_info.port_handle,
3104 login_params);
3105 if (status != FC_SUCCESS) {
3106 kmem_free(login_params, sizeof (la_els_logi_t));
3107 return (status);
3110 pkt = cmd->cmd_fp_pkt;
3112 fcsm_els_init(cmd, d_id);
3113 pkt->pkt_comp = comp_func;
3115 payload.ls_code = ls_code;
3116 payload.mbz = 0;
3118 FCSM_REP_WR(pkt->pkt_cmd_acc, login_params,
3119 pkt->pkt_cmd, sizeof (la_els_logi_t));
3120 FCSM_REP_WR(pkt->pkt_cmd_acc, &payload,
3121 pkt->pkt_cmd, sizeof (payload));
3123 cmd->cmd_transport = fc_ulp_issue_els;
3125 kmem_free(login_params, sizeof (la_els_logi_t));
3127 return (FC_SUCCESS);
3130 static void
3131 fcsm_xlogi_intr(fcsm_cmd_t *cmd)
3133 fc_packet_t *pkt;
3134 fcsm_job_t *job;
3135 fcsm_t *fcsm;
3137 pkt = cmd->cmd_fp_pkt;
3138 job = cmd->cmd_job;
3139 ASSERT(job != NULL);
3141 fcsm = cmd->cmd_fcsm;
3142 ASSERT(fcsm != NULL);
3144 if (pkt->pkt_state != FC_PKT_SUCCESS) {
3145 fcsm_display(CE_WARN, SM_LOG, fcsm, pkt,
3146 "xlogi_intr: login to DID 0x%x failed",
3147 pkt->pkt_cmd_fhdr.d_id);
3148 } else {
3149 /* Get the Login parameters of the Management Server */
3150 FCSM_REP_RD(pkt->pkt_resp_acc, &fcsm->sm_ms_service_params,
3151 pkt->pkt_resp, sizeof (la_els_logi_t));
3154 job->job_result =
3155 fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
3157 fcsm_free_cmd(cmd);
3159 fcsm_jobdone(job);
3162 static void
3163 fcsm_job_login_mgmt_server(fcsm_job_t *job)
3165 fcsm_t *fcsm;
3166 fcsm_cmd_t *cmd;
3167 int status;
3169 ASSERT(job != NULL);
3170 ASSERT(job->job_port_instance != -1);
3172 fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
3173 if (fcsm == NULL) {
3174 job->job_result = FC_NOMEM;
3175 fcsm_jobdone(job);
3176 return;
3180 * Issue the Login command to the management server.
3182 cmd = fcsm_alloc_cmd(fcsm, sizeof (la_els_logi_t),
3183 sizeof (la_els_logi_t), KM_SLEEP);
3184 if (cmd == NULL) {
3185 job->job_result = FC_NOMEM;
3186 fcsm_jobdone(job);
3187 return;
3190 FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
3191 fcsm_max_cmd_retries, fcsm_xlogi_intr);
3193 status = fcsm_xlogi_init(fcsm, cmd, FS_MANAGEMENT_SERVER,
3194 fcsm_pkt_common_intr, LA_ELS_PLOGI);
3196 if (status != FC_SUCCESS) {
3197 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
3198 "job_login_mgmt_server: plogi init failed. status 0x%x",
3199 status));
3200 job->job_result = status;
3201 fcsm_free_cmd(cmd);
3202 fcsm_jobdone(job);
3203 return;
3206 if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
3207 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3208 "job_ct_passthru: issue login cmd failed, status 0x%x",
3209 status));
3210 job->job_result = status;
3211 fcsm_free_cmd(cmd);
3212 fcsm_jobdone(job);
3213 return;
3219 fcsm_ct_passthru(int instance, fcio_t *fcio, int sleep, int job_flags,
3220 void (*func)(fcio_t *))
3222 fcsm_job_t *job;
3223 int status;
3225 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3226 "ct_passthru: instance 0x%x fcio 0x%p", instance, fcio));
3227 job = fcsm_alloc_job(sleep);
3228 ASSERT(sleep == KM_NOSLEEP || job != NULL);
3230 fcsm_init_job(job, instance, FCSM_JOB_CT_PASSTHRU, job_flags,
3231 (opaque_t)fcio, (opaque_t)func, fcsm_ct_passthru_comp, NULL);
3232 status = fcsm_process_job(job, 0);
3233 if (status != FC_SUCCESS) {
3234 /* Job could not be issued. So free the job and return */
3235 fcsm_dealloc_job(job);
3236 return (status);
3239 if (job_flags & FCSM_JOBFLAG_SYNC) {
3240 status = job->job_result;
3241 fcsm_dealloc_job(job);
3244 return (status);
3248 /* ARGSUSED */
3249 static void
3250 fcsm_ct_passthru_comp(opaque_t comp_arg, fcsm_job_t *job, int result)
3252 ASSERT(job != NULL);
3253 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3254 "ct_passthru_comp: result 0x%x port 0x%x",
3255 job->job_result, job->job_port_instance));
3259 static void
3260 fcsm_pkt_common_intr(fc_packet_t *pkt)
3262 fcsm_cmd_t *cmd;
3263 int jobstatus;
3264 fcsm_t *fcsm;
3266 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3267 "pkt_common_intr"));
3269 cmd = (fcsm_cmd_t *)pkt->pkt_ulp_private;
3270 ASSERT(cmd != NULL);
3272 if (pkt->pkt_state == FC_PKT_SUCCESS) {
3273 /* Command completed successfully. Just complete the command */
3274 cmd->cmd_comp(cmd);
3275 return;
3278 fcsm = cmd->cmd_fcsm;
3279 ASSERT(fcsm != NULL);
3281 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, cmd->cmd_fcsm, pkt,
3282 "fc packet to DID 0x%x failed for pkt 0x%p",
3283 pkt->pkt_cmd_fhdr.d_id, pkt));
3285 mutex_enter(&fcsm->sm_mutex);
3286 if (fcsm->sm_flags & FCSM_LINK_DOWN) {
3288 * No need to retry the command. The link previously
3289 * suffered an offline timeout.
3291 mutex_exit(&fcsm->sm_mutex);
3292 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3293 "pkt_common_intr: end. Link is down"));
3294 cmd->cmd_comp(cmd);
3295 return;
3297 mutex_exit(&fcsm->sm_mutex);
3299 jobstatus = fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
3300 if (jobstatus == FC_LOGINREQ) {
3302 * Login to the destination is required. No need to
3303 * retry this cmd again.
3305 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3306 "pkt_common_intr: end. LOGIN required"));
3307 cmd->cmd_comp(cmd);
3308 return;
3311 switch (pkt->pkt_state) {
3312 case FC_PKT_PORT_OFFLINE:
3313 case FC_PKT_LOCAL_RJT:
3314 case FC_PKT_TIMEOUT: {
3315 uchar_t pkt_state;
3317 pkt_state = pkt->pkt_state;
3318 cmd->cmd_retry_interval = fcsm_retry_interval;
3319 if (fcsm_retry_cmd(cmd) != 0) {
3320 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
3321 cmd->cmd_fcsm, NULL,
3322 "common_intr: max retries(%d) reached, status 0x%x",
3323 cmd->cmd_retry_count));
3326 * Restore the pkt_state to the actual failure status
3327 * received at the time of pkt completion.
3329 pkt->pkt_state = pkt_state;
3330 pkt->pkt_reason = 0;
3331 cmd->cmd_comp(cmd);
3332 } else {
3333 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
3334 cmd->cmd_fcsm, NULL,
3335 "pkt_common_intr: retry(%d) on pkt state (0x%x)",
3336 cmd->cmd_retry_count, pkt_state));
3338 break;
3340 default:
3341 cmd->cmd_comp(cmd);
3342 break;
3346 static int
3347 fcsm_issue_cmd(fcsm_cmd_t *cmd)
3349 fc_packet_t *pkt;
3350 fcsm_t *fcsm;
3351 int status;
3353 pkt = cmd->cmd_fp_pkt;
3354 fcsm = cmd->cmd_fcsm;
3356 /* Explicitly invalidate this field till fcsm decides to use it */
3357 pkt->pkt_ulp_rscn_infop = NULL;
3359 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3360 "issue_cmd: entry"));
3362 ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3363 mutex_enter(&fcsm->sm_mutex);
3364 if (fcsm->sm_flags & FCSM_LINK_DOWN) {
3366 * Update the pkt_state/pkt_reason appropriately.
3367 * Caller of this function can decide whether to call
3368 * 'pkt->pkt_comp' or use the 'status' returned by this func.
3370 mutex_exit(&fcsm->sm_mutex);
3371 pkt->pkt_state = FC_PKT_PORT_OFFLINE;
3372 pkt->pkt_reason = FC_REASON_OFFLINE;
3373 return (FC_OFFLINE);
3375 mutex_exit(&fcsm->sm_mutex);
3377 ASSERT(cmd->cmd_transport != NULL);
3378 status = cmd->cmd_transport(fcsm->sm_port_info.port_handle, pkt);
3379 if (status != FC_SUCCESS) {
3380 switch (status) {
3381 case FC_LOGINREQ:
3383 * No need to retry. Return the cause of failure.
3384 * Also update the pkt_state/pkt_reason. Caller of
3385 * this function can decide, whether to call
3386 * 'pkt->pkt_comp' or use the 'status' code returned
3387 * by this function.
3389 pkt->pkt_state = FC_PKT_LOCAL_RJT;
3390 pkt->pkt_reason = FC_REASON_LOGIN_REQUIRED;
3391 break;
3393 case FC_DEVICE_BUSY_NEW_RSCN:
3395 * There was a newer RSCN than what fcsm knows about.
3396 * So, just retry again
3398 cmd->cmd_retry_count = 0;
3399 /*FALLTHROUGH*/
3400 case FC_OFFLINE:
3401 case FC_STATEC_BUSY:
3403 * TODO: set flag, so that command is retried after
3404 * port is back online.
3405 * FALL Through for now.
3408 case FC_TRAN_BUSY:
3409 case FC_NOMEM:
3410 case FC_DEVICE_BUSY:
3411 cmd->cmd_retry_interval = fcsm_retry_interval;
3412 if (fcsm_retry_cmd(cmd) != 0) {
3413 FCSM_DEBUG(SMDL_TRACE,
3414 (CE_WARN, SM_LOG, fcsm, NULL,
3415 "issue_cmd: max retries (%d) reached",
3416 cmd->cmd_retry_count));
3419 * status variable is not changed here.
3420 * Return the cause of the original
3421 * cmd_transport failure.
3422 * Update the pkt_state/pkt_reason. Caller
3423 * of this function can decide whether to
3424 * call 'pkt->pkt_comp' or use the 'status'
3425 * code returned by this function.
3427 pkt->pkt_state = FC_PKT_TRAN_BSY;
3428 pkt->pkt_reason = 0;
3429 } else {
3430 FCSM_DEBUG(SMDL_TRACE,
3431 (CE_WARN, SM_LOG, fcsm, NULL,
3432 "issue_cmd: retry (%d) on fc status (0x%x)",
3433 cmd->cmd_retry_count, status));
3435 status = FC_SUCCESS;
3437 break;
3439 default:
3440 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
3441 "issue_cmd: failure status 0x%x", status));
3443 pkt->pkt_state = FC_PKT_TRAN_ERROR;
3444 pkt->pkt_reason = 0;
3445 break;
3451 return (status);
3455 static int
3456 fcsm_retry_cmd(fcsm_cmd_t *cmd)
3458 if (cmd->cmd_retry_count < cmd->cmd_max_retries) {
3459 cmd->cmd_retry_count++;
3460 fcsm_enque_cmd(cmd->cmd_fcsm, cmd);
3461 return (0);
3464 return (1);
3467 static void
3468 fcsm_enque_cmd(fcsm_t *fcsm, fcsm_cmd_t *cmd)
3470 ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3472 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "enque_cmd"));
3474 cmd->cmd_next = NULL;
3475 mutex_enter(&fcsm->sm_mutex);
3476 if (fcsm->sm_retry_tail) {
3477 ASSERT(fcsm->sm_retry_head != NULL);
3478 fcsm->sm_retry_tail->cmd_next = cmd;
3479 fcsm->sm_retry_tail = cmd;
3480 } else {
3481 ASSERT(fcsm->sm_retry_tail == NULL);
3482 fcsm->sm_retry_head = fcsm->sm_retry_tail = cmd;
3484 /* Schedule retry thread, if not already running */
3485 if (fcsm->sm_retry_tid == NULL) {
3486 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3487 "enque_cmd: schedule retry thread"));
3488 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
3489 (caddr_t)fcsm, fcsm_retry_ticks);
3492 mutex_exit(&fcsm->sm_mutex);
3496 static fcsm_cmd_t *
3497 fcsm_deque_cmd(fcsm_t *fcsm)
3499 fcsm_cmd_t *cmd;
3501 ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3503 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "deque_cmd"));
3505 mutex_enter(&fcsm->sm_mutex);
3506 if (fcsm->sm_retry_head == NULL) {
3507 ASSERT(fcsm->sm_retry_tail == NULL);
3508 cmd = NULL;
3509 } else {
3510 cmd = fcsm->sm_retry_head;
3511 fcsm->sm_retry_head = cmd->cmd_next;
3512 if (fcsm->sm_retry_head == NULL) {
3513 fcsm->sm_retry_tail = NULL;
3515 cmd->cmd_next = NULL;
3517 mutex_exit(&fcsm->sm_mutex);
3519 return (cmd);
3522 static void
3523 fcsm_retry_timeout(void *handle)
3525 fcsm_t *fcsm;
3526 fcsm_cmd_t *curr_tail;
3527 fcsm_cmd_t *cmd;
3528 int done = 0;
3529 int linkdown;
3531 fcsm = (fcsm_t *)handle;
3533 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "retry_timeout"));
3536 * If retry cmd queue is suspended, then go away.
3537 * This retry thread will be restarted, when cmd queue resumes.
3539 mutex_enter(&fcsm->sm_mutex);
3540 if (fcsm->sm_flags & FCSM_CMD_RETRY_Q_SUSPENDED) {
3542 * Clear the retry_tid, to indicate that this routine is not
3543 * currently being rescheduled.
3545 fcsm->sm_retry_tid = (timeout_id_t)NULL;
3546 mutex_exit(&fcsm->sm_mutex);
3547 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3548 "retry_timeout: end. No processing. "
3549 "Queue is currently suspended for this instance"));
3550 return;
3553 linkdown = (fcsm->sm_flags & FCSM_LINK_DOWN) ? 1 : 0;
3556 * Save the curr_tail, so that we only process the commands
3557 * which are in the queue at this time.
3559 curr_tail = fcsm->sm_retry_tail;
3560 mutex_exit(&fcsm->sm_mutex);
3563 * Check for done flag before dequeing the command.
3564 * Dequeing before checking the done flag will cause a command
3565 * to be lost.
3567 while ((!done) && ((cmd = fcsm_deque_cmd(fcsm)) != NULL)) {
3569 if (cmd == curr_tail) {
3570 done = 1;
3573 cmd->cmd_retry_interval -= fcsm_retry_ticker;
3575 if (linkdown) {
3576 fc_packet_t *pkt;
3579 * No need to retry the command. The link has
3580 * suffered an offline timeout.
3582 pkt = cmd->cmd_fp_pkt;
3583 pkt->pkt_state = FC_PKT_PORT_OFFLINE;
3584 pkt->pkt_reason = FC_REASON_OFFLINE;
3585 pkt->pkt_comp(pkt);
3586 continue;
3589 if (cmd->cmd_retry_interval <= 0) {
3590 /* Retry the command */
3591 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3592 "retry_timeout: issue cmd 0x%p", (void *)cmd));
3593 if (fcsm_issue_cmd(cmd) != FC_SUCCESS) {
3594 cmd->cmd_fp_pkt->pkt_comp(cmd->cmd_fp_pkt);
3596 } else {
3598 * Put the command back on the queue. Retry time
3599 * has not yet reached.
3601 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3602 "retry_timeout: queue cmd 0x%p", (void *)cmd));
3603 fcsm_enque_cmd(fcsm, cmd);
3607 mutex_enter(&fcsm->sm_mutex);
3608 if (fcsm->sm_retry_head) {
3609 /* Activate timer */
3610 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
3611 (caddr_t)fcsm, fcsm_retry_ticks);
3612 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3613 "retry_timeout: retry thread rescheduled"));
3614 } else {
3616 * Reset the tid variable. The first thread which queues the
3617 * command, will restart the timer.
3619 fcsm->sm_retry_tid = (timeout_id_t)NULL;
3621 mutex_exit(&fcsm->sm_mutex);