4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * Multiplexed I/O SCSI vHCI implementation
27 */
28
29 #include <sys/conf.h>
30 #include <sys/file.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/scsi/scsi.h>
34 #include <sys/scsi/impl/scsi_reset_notify.h>
35 #include <sys/scsi/impl/services.h>
36 #include <sys/sunmdi.h>
37 #include <sys/mdi_impldefs.h>
38 #include <sys/scsi/adapters/scsi_vhci.h>
39 #include <sys/disp.h>
40 #include <sys/byteorder.h>
41
42 extern uintptr_t scsi_callback_id;
43 extern ddi_dma_attr_t scsi_alloc_attr;
2209 return (NULL);
2210 }
2211
2212 static int
2213 vhci_bind_transport(struct scsi_address *ap, struct vhci_pkt *vpkt, int flags,
2214 int (*func)(caddr_t))
2215 {
2216 struct scsi_vhci *vhci = ADDR2VHCI(ap);
2217 dev_info_t *cdip = ADDR2DIP(ap);
2218 mdi_pathinfo_t *pip = NULL;
2219 mdi_pathinfo_t *npip = NULL;
2220 scsi_vhci_priv_t *svp = NULL;
2221 struct scsi_device *psd = NULL;
2222 struct scsi_address *address = NULL;
2223 struct scsi_pkt *pkt = NULL;
2224 int rval = -1;
2225 int pgr_sema_held = 0;
2226 int held;
2227 int mps_flag = MDI_SELECT_ONLINE_PATH;
2228 struct scsi_vhci_lun *vlun;
2229 time_t tnow;
2230 int path_instance = 0;
2231
2232 vlun = ADDR2VLUN(ap);
2233 ASSERT(vlun != 0);
2234
2235 if ((vpkt->vpkt_tgt_pkt->pkt_cdbp[0] == SCMD_PROUT) &&
2236 (((vpkt->vpkt_tgt_pkt->pkt_cdbp[1] & 0x1f) ==
2237 VHCI_PROUT_REGISTER) ||
2238 ((vpkt->vpkt_tgt_pkt->pkt_cdbp[1] & 0x1f) ==
2239 VHCI_PROUT_R_AND_IGNORE))) {
2240 if (!sema_tryp(&vlun->svl_pgr_sema))
2241 return (TRAN_BUSY);
2242 pgr_sema_held = 1;
2243 if (vlun->svl_first_path != NULL) {
2244 rval = mdi_select_path(cdip, NULL,
2245 MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH,
2246 NULL, &pip);
2247 if ((rval != MDI_SUCCESS) || (pip == NULL)) {
2248 VHCI_DEBUG(4, (CE_NOTE, NULL,
2249 "vhci_bind_transport: path select fail\n"));
2347 * with attach/probe (eg. INQUIRY, block 0 read)
2348 * are completed by targets even on passive paths
2349 * If no ONLINE paths available, it is important
2350 * to set svl_waiting_for_activepath for two
2351 * reasons: (1) avoid sense analysis in the
2352 * "external failure detection" codepath in
2353 * vhci_intr(). Failure to do so will result in
2354 * infinite loop (unless an ONLINE path becomes
2355 * available at some point) (2) avoid
2356 * unnecessary failover (see "---Waiting For Active
2357 * Path---" comment below).
2358 */
2359 VHCI_DEBUG(1, (CE_NOTE, NULL, "!%p in onlining "
2360 "state\n", (void *)cdip));
2361 pip = NULL;
2362 rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp,
2363 mps_flag, NULL, &pip);
2364 if ((rval != MDI_SUCCESS) || (pip == NULL)) {
2365 if (vlun->svl_waiting_for_activepath == 0) {
2366 vlun->svl_waiting_for_activepath = 1;
2367 vlun->svl_wfa_time = ddi_get_time();
2368 }
2369 mps_flag |= MDI_SELECT_STANDBY_PATH;
2370 rval = mdi_select_path(cdip,
2371 vpkt->vpkt_tgt_init_bp,
2372 mps_flag, NULL, &pip);
2373 if ((rval != MDI_SUCCESS) || (pip == NULL)) {
2374 if (pgr_sema_held) {
2375 sema_v(&vlun->svl_pgr_sema);
2376 }
2377 return (TRAN_FATAL_ERROR);
2378 }
2379 goto bind_path;
2380 }
2381 } else if ((rval == MDI_FAILURE) ||
2382 ((rval == MDI_NOPATH) && (path_instance))) {
2383 if (pgr_sema_held) {
2384 sema_v(&vlun->svl_pgr_sema);
2385 }
2386 return (TRAN_FATAL_ERROR);
2387 }
2388
2389 if ((pip == NULL) || (rval == MDI_NOPATH)) {
2390 while (vlun->svl_waiting_for_activepath) {
2391 /*
2392 * ---Waiting For Active Path---
2393 * This device was discovered across a
2394 * passive path; lets wait for a little
2395 * bit, hopefully an active path will
2396 * show up obviating the need for a
2397 * failover
2398 */
2399 tnow = ddi_get_time();
2400 if (tnow - vlun->svl_wfa_time >= 60) {
2401 vlun->svl_waiting_for_activepath = 0;
2402 } else {
2403 drv_usecwait(1000);
2404 if (vlun->svl_waiting_for_activepath
2405 == 0) {
2406 /*
2407 * an active path has come
2408 * online!
2409 */
2410 goto try_again;
2411 }
2412 }
2413 }
2414 VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
2415 if (!held) {
2416 VHCI_DEBUG(4, (CE_NOTE, NULL,
2417 "!Lun not held\n"));
2418 if (pgr_sema_held) {
2419 sema_v(&vlun->svl_pgr_sema);
2420 }
3607 vlun->svl_flags |= VLUN_UPDATE_TPG;
3608 (void) taskq_dispatch(vhci->vhci_update_pathstates_taskq,
3609 vhci_update_pathstates, (void *)vlun, KM_SLEEP);
3610 } else {
3611 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
3612 vhci_log(CE_NOTE, ddi_get_parent(vlun->svl_dip),
3613 "!%s (%s%d): Waiting for externally initiated failover "
3614 "to complete", ddi_pathname(vlun->svl_dip, path),
3615 ddi_driver_name(vlun->svl_dip),
3616 ddi_get_instance(vlun->svl_dip));
3617 kmem_free(path, MAXPATHLEN);
3618 swarg = kmem_alloc(sizeof (*swarg), KM_NOSLEEP);
3619 if (swarg == NULL) {
3620 VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_handle_ext_fo: "
3621 "request packet allocation for %s failed....\n",
3622 vlun->svl_lun_wwn));
3623 VHCI_RELEASE_LUN(vlun);
3624 return (PKT_RETURN);
3625 }
3626 swarg->svs_svp = svp;
3627 swarg->svs_tos = ddi_get_time();
3628 swarg->svs_pi = vpkt->vpkt_path;
3629 swarg->svs_release_lun = 0;
3630 swarg->svs_done = 0;
3631 /*
3632 * place a hold on the path...we don't want it to
3633 * vanish while scsi_watch is in progress
3634 */
3635 mdi_hold_path(vpkt->vpkt_path);
3636 svp->svp_sw_token = scsi_watch_request_submit(svp->svp_psd,
3637 VHCI_FOWATCH_INTERVAL, SENSE_LENGTH, vhci_efo_watch_cb,
3638 (caddr_t)swarg);
3639 }
3640 return (BUSY_RETURN);
3641 }
3642
3643 /*
3644 * vhci_efo_watch_cb:
3645 * Callback from scsi_watch request to check the failover status.
3646 * Completion is either due to successful failover or timeout.
3647 * Upon successful completion, vhci_update_path_states is called.
3668 if (swarg->svs_done) {
3669 /*
3670 * Already completed failover or timedout.
3671 * Waiting for vhci_efo_done to terminate this scsi_watch.
3672 */
3673 return (0);
3674 }
3675
3676 ASSERT(svp != NULL);
3677 vlun = svp->svp_svl;
3678 ASSERT(vlun != NULL);
3679 ASSERT(VHCI_LUN_IS_HELD(vlun));
3680 vlun->svl_efo_update_path = 0;
3681 vdip = ddi_get_parent(vlun->svl_dip);
3682 vhci = ddi_get_soft_state(vhci_softstate,
3683 ddi_get_instance(vdip));
3684
3685 updt_paths = 0;
3686
3687 if (pkt->pkt_reason != CMD_CMPLT) {
3688 if ((ddi_get_time() - swarg->svs_tos) >= VHCI_EXTFO_TIMEOUT) {
3689 swarg->svs_release_lun = 1;
3690 goto done;
3691 }
3692 return (0);
3693 }
3694 if (*((unsigned char *)statusp) == STATUS_CHECK) {
3695 rval = vlun->svl_fops->sfo_analyze_sense(svp->svp_psd, sensep,
3696 vlun->svl_fops_ctpriv);
3697 switch (rval) {
3698 /*
3699 * Only update path states in case path is definitely
3700 * inactive, or no failover occurred. For all other
3701 * check conditions continue pinging. A unexpected
3702 * check condition shouldn't cause pinging to complete
3703 * prematurely.
3704 */
3705 case SCSI_SENSE_INACTIVE:
3706 case SCSI_SENSE_NOFAILOVER:
3707 updt_paths = 1;
3708 break;
3709 default:
3710 if ((ddi_get_time() - swarg->svs_tos)
3711 >= VHCI_EXTFO_TIMEOUT) {
3712 swarg->svs_release_lun = 1;
3713 goto done;
3714 }
3715 return (0);
3716 }
3717 } else if (*((unsigned char *)statusp) ==
3718 STATUS_RESERVATION_CONFLICT) {
3719 updt_paths = 1;
3720 } else if ((*((unsigned char *)statusp)) &
3721 (STATUS_BUSY | STATUS_QFULL)) {
3722 return (0);
3723 }
3724 if ((*((unsigned char *)statusp) == STATUS_GOOD) ||
3725 (updt_paths == 1)) {
3726 /*
3727 * we got here because we had detected an
3728 * externally initiated failover; things
3729 * have settled down now, so let's
3730 * start up a task to update the
3731 * path states and target port group
3732 */
3733 vlun->svl_efo_update_path = 1;
3734 swarg->svs_done = 1;
3735 vlun->svl_swarg = swarg;
3736 vlun->svl_flags |= VLUN_UPDATE_TPG;
3737 (void) taskq_dispatch(vhci->vhci_update_pathstates_taskq,
3738 vhci_update_pathstates, (void *)vlun,
3739 KM_SLEEP);
3740 return (0);
3741 }
3742 if ((ddi_get_time() - swarg->svs_tos) >= VHCI_EXTFO_TIMEOUT) {
3743 swarg->svs_release_lun = 1;
3744 goto done;
3745 }
3746 return (0);
3747 done:
3748 swarg->svs_done = 1;
3749 (void) taskq_dispatch(vhci->vhci_taskq,
3750 vhci_efo_done, (void *)swarg, KM_SLEEP);
3751 return (0);
3752 }
3753
3754 /*
3755 * vhci_efo_done:
3756 * cleanly terminates scsi_watch and free up resources.
3757 * Called as taskq function in vhci_efo_watch_cb for EFO timeout condition
3758 * or by vhci_update_path_states invoked during external initiated
3759 * failover completion.
3760 */
3761 static void
3762 vhci_efo_done(void *arg)
|
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24 /*
25 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
26 */
27
28 /*
29 * Multiplexed I/O SCSI vHCI implementation
30 */
31
32 #include <sys/conf.h>
33 #include <sys/file.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/scsi/scsi.h>
37 #include <sys/scsi/impl/scsi_reset_notify.h>
38 #include <sys/scsi/impl/services.h>
39 #include <sys/sunmdi.h>
40 #include <sys/mdi_impldefs.h>
41 #include <sys/scsi/adapters/scsi_vhci.h>
42 #include <sys/disp.h>
43 #include <sys/byteorder.h>
44
45 extern uintptr_t scsi_callback_id;
46 extern ddi_dma_attr_t scsi_alloc_attr;
2212 return (NULL);
2213 }
2214
2215 static int
2216 vhci_bind_transport(struct scsi_address *ap, struct vhci_pkt *vpkt, int flags,
2217 int (*func)(caddr_t))
2218 {
2219 struct scsi_vhci *vhci = ADDR2VHCI(ap);
2220 dev_info_t *cdip = ADDR2DIP(ap);
2221 mdi_pathinfo_t *pip = NULL;
2222 mdi_pathinfo_t *npip = NULL;
2223 scsi_vhci_priv_t *svp = NULL;
2224 struct scsi_device *psd = NULL;
2225 struct scsi_address *address = NULL;
2226 struct scsi_pkt *pkt = NULL;
2227 int rval = -1;
2228 int pgr_sema_held = 0;
2229 int held;
2230 int mps_flag = MDI_SELECT_ONLINE_PATH;
2231 struct scsi_vhci_lun *vlun;
2232 int path_instance = 0;
2233
2234 vlun = ADDR2VLUN(ap);
2235 ASSERT(vlun != 0);
2236
2237 if ((vpkt->vpkt_tgt_pkt->pkt_cdbp[0] == SCMD_PROUT) &&
2238 (((vpkt->vpkt_tgt_pkt->pkt_cdbp[1] & 0x1f) ==
2239 VHCI_PROUT_REGISTER) ||
2240 ((vpkt->vpkt_tgt_pkt->pkt_cdbp[1] & 0x1f) ==
2241 VHCI_PROUT_R_AND_IGNORE))) {
2242 if (!sema_tryp(&vlun->svl_pgr_sema))
2243 return (TRAN_BUSY);
2244 pgr_sema_held = 1;
2245 if (vlun->svl_first_path != NULL) {
2246 rval = mdi_select_path(cdip, NULL,
2247 MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH,
2248 NULL, &pip);
2249 if ((rval != MDI_SUCCESS) || (pip == NULL)) {
2250 VHCI_DEBUG(4, (CE_NOTE, NULL,
2251 "vhci_bind_transport: path select fail\n"));
2349 * with attach/probe (eg. INQUIRY, block 0 read)
2350 * are completed by targets even on passive paths
2351 * If no ONLINE paths available, it is important
2352 * to set svl_waiting_for_activepath for two
2353 * reasons: (1) avoid sense analysis in the
2354 * "external failure detection" codepath in
2355 * vhci_intr(). Failure to do so will result in
2356 * infinite loop (unless an ONLINE path becomes
2357 * available at some point) (2) avoid
2358 * unnecessary failover (see "---Waiting For Active
2359 * Path---" comment below).
2360 */
2361 VHCI_DEBUG(1, (CE_NOTE, NULL, "!%p in onlining "
2362 "state\n", (void *)cdip));
2363 pip = NULL;
2364 rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp,
2365 mps_flag, NULL, &pip);
2366 if ((rval != MDI_SUCCESS) || (pip == NULL)) {
2367 if (vlun->svl_waiting_for_activepath == 0) {
2368 vlun->svl_waiting_for_activepath = 1;
2369 vlun->svl_wfa_time = gethrtime();
2370 }
2371 mps_flag |= MDI_SELECT_STANDBY_PATH;
2372 rval = mdi_select_path(cdip,
2373 vpkt->vpkt_tgt_init_bp,
2374 mps_flag, NULL, &pip);
2375 if ((rval != MDI_SUCCESS) || (pip == NULL)) {
2376 if (pgr_sema_held) {
2377 sema_v(&vlun->svl_pgr_sema);
2378 }
2379 return (TRAN_FATAL_ERROR);
2380 }
2381 goto bind_path;
2382 }
2383 } else if ((rval == MDI_FAILURE) ||
2384 ((rval == MDI_NOPATH) && (path_instance))) {
2385 if (pgr_sema_held) {
2386 sema_v(&vlun->svl_pgr_sema);
2387 }
2388 return (TRAN_FATAL_ERROR);
2389 }
2390
2391 if ((pip == NULL) || (rval == MDI_NOPATH)) {
2392 while (vlun->svl_waiting_for_activepath) {
2393 /*
2394 * ---Waiting For Active Path---
2395 * This device was discovered across a
2396 * passive path; lets wait for a little
2397 * bit, hopefully an active path will
2398 * show up obviating the need for a
2399 * failover
2400 */
2401 if ((gethrtime() - vlun->svl_wfa_time) >=
2402 (60 * NANOSEC)) {
2403 vlun->svl_waiting_for_activepath = 0;
2404 } else {
2405 drv_usecwait(1000);
2406 if (vlun->svl_waiting_for_activepath
2407 == 0) {
2408 /*
2409 * an active path has come
2410 * online!
2411 */
2412 goto try_again;
2413 }
2414 }
2415 }
2416 VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
2417 if (!held) {
2418 VHCI_DEBUG(4, (CE_NOTE, NULL,
2419 "!Lun not held\n"));
2420 if (pgr_sema_held) {
2421 sema_v(&vlun->svl_pgr_sema);
2422 }
3609 vlun->svl_flags |= VLUN_UPDATE_TPG;
3610 (void) taskq_dispatch(vhci->vhci_update_pathstates_taskq,
3611 vhci_update_pathstates, (void *)vlun, KM_SLEEP);
3612 } else {
3613 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
3614 vhci_log(CE_NOTE, ddi_get_parent(vlun->svl_dip),
3615 "!%s (%s%d): Waiting for externally initiated failover "
3616 "to complete", ddi_pathname(vlun->svl_dip, path),
3617 ddi_driver_name(vlun->svl_dip),
3618 ddi_get_instance(vlun->svl_dip));
3619 kmem_free(path, MAXPATHLEN);
3620 swarg = kmem_alloc(sizeof (*swarg), KM_NOSLEEP);
3621 if (swarg == NULL) {
3622 VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_handle_ext_fo: "
3623 "request packet allocation for %s failed....\n",
3624 vlun->svl_lun_wwn));
3625 VHCI_RELEASE_LUN(vlun);
3626 return (PKT_RETURN);
3627 }
3628 swarg->svs_svp = svp;
3629 swarg->svs_tos = gethrtime();
3630 swarg->svs_pi = vpkt->vpkt_path;
3631 swarg->svs_release_lun = 0;
3632 swarg->svs_done = 0;
3633 /*
3634 * place a hold on the path...we don't want it to
3635 * vanish while scsi_watch is in progress
3636 */
3637 mdi_hold_path(vpkt->vpkt_path);
3638 svp->svp_sw_token = scsi_watch_request_submit(svp->svp_psd,
3639 VHCI_FOWATCH_INTERVAL, SENSE_LENGTH, vhci_efo_watch_cb,
3640 (caddr_t)swarg);
3641 }
3642 return (BUSY_RETURN);
3643 }
3644
3645 /*
3646 * vhci_efo_watch_cb:
3647 * Callback from scsi_watch request to check the failover status.
3648 * Completion is either due to successful failover or timeout.
3649 * Upon successful completion, vhci_update_path_states is called.
3670 if (swarg->svs_done) {
3671 /*
3672 * Already completed failover or timedout.
3673 * Waiting for vhci_efo_done to terminate this scsi_watch.
3674 */
3675 return (0);
3676 }
3677
3678 ASSERT(svp != NULL);
3679 vlun = svp->svp_svl;
3680 ASSERT(vlun != NULL);
3681 ASSERT(VHCI_LUN_IS_HELD(vlun));
3682 vlun->svl_efo_update_path = 0;
3683 vdip = ddi_get_parent(vlun->svl_dip);
3684 vhci = ddi_get_soft_state(vhci_softstate,
3685 ddi_get_instance(vdip));
3686
3687 updt_paths = 0;
3688
3689 if (pkt->pkt_reason != CMD_CMPLT) {
3690 if ((gethrtime() - swarg->svs_tos) >= VHCI_EXTFO_TIMEOUT) {
3691 swarg->svs_release_lun = 1;
3692 goto done;
3693 }
3694 return (0);
3695 }
3696 if (*((unsigned char *)statusp) == STATUS_CHECK) {
3697 rval = vlun->svl_fops->sfo_analyze_sense(svp->svp_psd, sensep,
3698 vlun->svl_fops_ctpriv);
3699 switch (rval) {
3700 /*
3701 * Only update path states in case path is definitely
3702 * inactive, or no failover occurred. For all other
3703 * check conditions continue pinging. A unexpected
3704 * check condition shouldn't cause pinging to complete
3705 * prematurely.
3706 */
3707 case SCSI_SENSE_INACTIVE:
3708 case SCSI_SENSE_NOFAILOVER:
3709 updt_paths = 1;
3710 break;
3711 default:
3712 if ((gethrtime() - swarg->svs_tos)
3713 >= VHCI_EXTFO_TIMEOUT) {
3714 swarg->svs_release_lun = 1;
3715 goto done;
3716 }
3717 return (0);
3718 }
3719 } else if (*((unsigned char *)statusp) ==
3720 STATUS_RESERVATION_CONFLICT) {
3721 updt_paths = 1;
3722 } else if ((*((unsigned char *)statusp)) &
3723 (STATUS_BUSY | STATUS_QFULL)) {
3724 return (0);
3725 }
3726 if ((*((unsigned char *)statusp) == STATUS_GOOD) ||
3727 (updt_paths == 1)) {
3728 /*
3729 * we got here because we had detected an
3730 * externally initiated failover; things
3731 * have settled down now, so let's
3732 * start up a task to update the
3733 * path states and target port group
3734 */
3735 vlun->svl_efo_update_path = 1;
3736 swarg->svs_done = 1;
3737 vlun->svl_swarg = swarg;
3738 vlun->svl_flags |= VLUN_UPDATE_TPG;
3739 (void) taskq_dispatch(vhci->vhci_update_pathstates_taskq,
3740 vhci_update_pathstates, (void *)vlun,
3741 KM_SLEEP);
3742 return (0);
3743 }
3744 if ((gethrtime() - swarg->svs_tos) >= VHCI_EXTFO_TIMEOUT) {
3745 swarg->svs_release_lun = 1;
3746 goto done;
3747 }
3748 return (0);
3749 done:
3750 swarg->svs_done = 1;
3751 (void) taskq_dispatch(vhci->vhci_taskq,
3752 vhci_efo_done, (void *)swarg, KM_SLEEP);
3753 return (0);
3754 }
3755
3756 /*
3757 * vhci_efo_done:
3758 * cleanly terminates scsi_watch and free up resources.
3759 * Called as taskq function in vhci_efo_watch_cb for EFO timeout condition
3760 * or by vhci_update_path_states invoked during external initiated
3761 * failover completion.
3762 */
3763 static void
3764 vhci_efo_done(void *arg)
|