1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * iSCSI logical unit interfaces
  26  */
  27 
  28 #include "iscsi.h"
  29 #include <sys/fs/dv_node.h>       /* devfs_clean */
  30 #include <sys/bootprops.h>
  31 #include <sys/sysevent/eventdefs.h>
  32 #include <sys/sysevent/dev.h>
  33 
  34 /* tpgt bytes in string form */
  35 #define TPGT_EXT_SIZE   5
  36 
  37 /* logical unit number bytes in string form */
  38 #define LUN_EXT_SIZE    10
  39 
  40 /*
  41  * Addition addr size of size of ',' + max str form of tpgt (2 bytes) +
  42  * ',' + max str form of logical unit number (4 bytes).
  43  */
  44 #define ADDR_EXT_SIZE   (1 + TPGT_EXT_SIZE + 1 + LUN_EXT_SIZE)
  45 
  46 /* internal interfaces */
  47 static iscsi_status_t iscsi_lun_virt_create(iscsi_sess_t *isp,
  48     uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq);
  49 static iscsi_status_t iscsi_lun_phys_create(iscsi_sess_t *isp,
 
 
 106         (void) snprintf(addr,
 107             (strlen((char *)isp->sess_name) +
 108             ADDR_EXT_SIZE + 1),
 109             "%02X%02X%s%04X,%d", isp->sess_isid[4],
 110             isp->sess_isid[5], isp->sess_name,
 111             isp->sess_tpgt_nego & 0xFFFF, lun_num);
 112 
 113         /* allocate space for lun struct */
 114         ilp = kmem_zalloc(sizeof (iscsi_lun_t), KM_SLEEP);
 115         ilp->lun_sig = ISCSI_SIG_LUN;
 116         ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
 117         ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
 118 
 119         /* initialize common LU information */
 120         ilp->lun_num     = lun_num;
 121         ilp->lun_addr_type  = lun_addr_type;
 122         ilp->lun_sess            = isp;
 123         ilp->lun_addr            = addr;
 124         ilp->lun_type            = inq->inq_dtype & DTYPE_MASK;
 125         ilp->lun_oid     = oid_tmp;
 126 
 127         bcopy(inq->inq_vid, ilp->lun_vid, sizeof (inq->inq_vid));
 128         bcopy(inq->inq_pid, ilp->lun_pid, sizeof (inq->inq_pid));
 129 
 130         /* store GUID if valid one exists */
 131         if (guid != NULL) {
 132                 ilp->lun_guid_size = strlen(guid) + 1;
 133                 ilp->lun_guid = kmem_zalloc(ilp->lun_guid_size, KM_SLEEP);
 134                 (void) strcpy(ilp->lun_guid, guid);
 135         } else {
 136                 ilp->lun_guid_size = 0;
 137                 ilp->lun_guid = NULL;
 138         }
 139 
 140         /*
 141          * We need to add the lun to our lists now because during the
 142          * lun creation we will get called back into multiple times
 143          * depending on the createion type.  These callbacks will
 144          * occur via our tran_init_lun, tran_get_name, tran_get_bus_addr,
 145          * tran_init_pkt, tran_start.
 
 172                 } else {
 173                         /* if not head, set prev lun's next to our next */
 174                         for (ilp_tmp = isp->sess_lun_list; ilp_tmp;
 175                             ilp_tmp = ilp_tmp->lun_next) {
 176                                 if (ilp_tmp->lun_next == ilp) {
 177                                         ilp_tmp->lun_next = ilp->lun_next;
 178                                         break;
 179                                 }
 180                         }
 181                 }
 182 
 183                 kmem_free(ilp->lun_addr,
 184                     (strlen((char *)isp->sess_name) +
 185                     ADDR_EXT_SIZE + 1));
 186                 ilp->lun_addr = NULL;
 187 
 188                 if (ilp->lun_guid != NULL) {
 189                         kmem_free(ilp->lun_guid, ilp->lun_guid_size);
 190                         ilp->lun_guid = NULL;
 191                 }
 192                 kmem_free(ilp, sizeof (iscsi_lun_t));
 193         } else {
 194                 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
 195                 ilp->lun_state |= ISCSI_LUN_STATE_ONLINE;
 196                 ilp->lun_time_online = ddi_get_time();
 197 
 198                 /* Check whether this is the required LUN for iscsi boot */
 199                 if (iscsiboot_prop != NULL && isp->sess_boot == B_TRUE &&
 200                     iscsiboot_prop->boot_tgt.lun_online == 0) {
 201                         lun_num_ptr =
 202                             (uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun;
 203                         boot_lun_num = (uint16_t)(*lun_num_ptr);
 204                         if (boot_lun_num == ilp->lun_num) {
 205                                 /*
 206                                  * During iscsi boot, the boot lun has been
 207                                  * online, we should set the "online flag".
 208                                  */
 209                                 iscsiboot_prop->boot_tgt.lun_online = 1;
 210                         }
 211                 }
 212         }
 213         rw_exit(&isp->sess_lun_list_rwlock);
 214 
 215         return (rtn);
 216 }
 217 
 218 /*
 219  * iscsi_lun_destroy - offline and remove lun
 220  *
 221  * This interface is called when a name service change has
 222  * occured and the storage is no longer available to this
 223  * initiator.  This function will offline and free the
 224  * solaris node resources.  Then it will free all iscsi lun
 225  * resources.
 226  *
 227  * This function can fail with ISCSI_STATUS_BUSY if the
 228  * logical unit is in use.  The user should unmount or
 229  * close the device and perform the nameservice operation
 230  * again if this occurs.
 231  */
 232 iscsi_status_t
 233 iscsi_lun_destroy(iscsi_hba_t *ihp, iscsi_lun_t *ilp)
 234 {
 235         iscsi_status_t          status          = ISCSI_STATUS_SUCCESS;
 236         iscsi_sess_t            *isp            = NULL;
 237         iscsi_lun_t             *t_ilp          = NULL;
 238 
 239         ASSERT(ilp != NULL);
 240         isp = ilp->lun_sess;
 241         ASSERT(isp != NULL);
 242 
 243         /* attempt to offline and free solaris node */
 244         status = iscsi_lun_offline(ihp, ilp, B_TRUE);
 245 
 246         /* If we successfully unplumbed the lun remove it from our lists */
 247         if (ISCSI_SUCCESS(status)) {
 248                 if (isp->sess_lun_list == ilp) {
 249                         /* target first item in list */
 250                         isp->sess_lun_list = ilp->lun_next;
 251                 } else {
 252                         /*
 253                          * search session list for ilp pointing
 254                          * to lun being removed.  Then
 255                          * update that luns next pointer.
 256                          */
 257                         t_ilp = isp->sess_lun_list;
 258                         while (t_ilp->lun_next != NULL) {
 259                                 if (t_ilp->lun_next == ilp) {
 260                                         break;
 261                                 }
 262                                 t_ilp = t_ilp->lun_next;
 263                         }
 264                         if (t_ilp->lun_next == ilp) {
 265                                 t_ilp->lun_next = ilp->lun_next;
 266                         } else {
 267                                 /* couldn't find session */
 268                                 ASSERT(FALSE);
 269                         }
 270                 }
 271 
 272                 /* release its memory */
 273                 kmem_free(ilp->lun_addr, (strlen((char *)isp->sess_name) +
 274                     ADDR_EXT_SIZE + 1));
 275                 ilp->lun_addr = NULL;
 276                 if (ilp->lun_guid != NULL) {
 277                         kmem_free(ilp->lun_guid, ilp->lun_guid_size);
 278                         ilp->lun_guid = NULL;
 279                 }
 280                 kmem_free(ilp, sizeof (iscsi_lun_t));
 281                 ilp = NULL;
 282         }
 283 
 284         return (status);
 285 }
 286 
 287 /*
 288  * +--------------------------------------------------------------------+
 289  * | External Logical Unit Interfaces                                   |
 290  * +--------------------------------------------------------------------+
 291  */
 292 
 293 /*
 294  * iscsi_lun_virt_create - Creates solaris logical unit via MDI
 295  */
 296 static iscsi_status_t
 297 iscsi_lun_virt_create(iscsi_sess_t *isp, uint16_t lun_num, iscsi_lun_t *ilp,
 298     struct scsi_inquiry *inq)
 299 {
 300         iscsi_status_t          rtn             = ISCSI_STATUS_INTERNAL_ERROR;
 301         int                     mdi_rtn         = MDI_FAILURE;
 302         iscsi_hba_t             *ihp            = NULL;
 
 624  * be called to both offline and free the logical unit.
 625  * (This operates soley on the solaris node states.
 626  * iscsi_lun_destroy() should be called when attempting
 627  * to free all iscsi lun resources.)
 628  *
 629  * This function can fail with ISCSI_STATUS_BUSY if the
 630  * logical unit is in use.  The user should unmount or
 631  * close the device and perform the nameservice operation
 632  * again if this occurs.
 633  *
 634  * If we fail to offline a LUN that we don't want to destroy,
 635  * we will mark it with invalid state. If this LUN still
 636  * exists on the target, we can have another chance to online
 637  * it again when we do the LUN enumeration.
 638  */
 639 iscsi_status_t
 640 iscsi_lun_offline(iscsi_hba_t *ihp, iscsi_lun_t *ilp, boolean_t lun_free)
 641 {
 642         iscsi_status_t          status          = ISCSI_STATUS_SUCCESS;
 643         int                     circ            = 0;
 644         dev_info_t              *cdip, *pdip;
 645         char                    *devname        = NULL;
 646         char                    *pathname       = NULL;
 647         int                     rval;
 648         boolean_t               offline         = B_FALSE;
 649         nvlist_t                *attr_list      = NULL;
 650 
 651         ASSERT(ilp != NULL);
 652         ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL));
 653 
 654         /*
 655          * Since we carry the logical units parent
 656          * lock across the offline call it will not
 657          * issue devfs_clean() and may fail with a
 658          * devi_ref count > 0.
 659          */
 660         if (ilp->lun_pip == NULL) {
 661                 cdip = ilp->lun_dip;
 662         } else {
 663                 cdip = mdi_pi_get_client(ilp->lun_pip);
 664         }
 665 
 666         if ((cdip != NULL) &&
 667             (lun_free == B_TRUE) &&
 668             (ilp->lun_state & ISCSI_LUN_STATE_ONLINE)) {
 669                 /*
 670                  * Make sure node is attached otherwise
 671                  * it won't have related cache nodes to
 672                  * clean up.  i_ddi_devi_attached is
 673                  * similiar to i_ddi_node_state(cdip) >=
 674                  * DS_ATTACHED. We should clean up only
 675                  * when lun_free is set.
 676                  */
 677                 if (i_ddi_devi_attached(cdip)) {
 678 
 679                         /* Get parent dip */
 680                         pdip = ddi_get_parent(cdip);
 681 
 682                         /* Get full devname */
 683                         devname = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
 684                         ndi_devi_enter(pdip, &circ);
 685                         (void) ddi_deviname(cdip, devname);
 686                         /* Release lock before devfs_clean() */
 687                         ndi_devi_exit(pdip, circ);
 688 
 689                         /* Clean cache */
 690                         (void) devfs_clean(pdip, devname + 1, DV_CLEAN_FORCE);
 691                         kmem_free(devname, MAXNAMELEN + 1);
 692                 }
 693         }
 694 
 695         if (cdip != NULL && ilp->lun_type == DTYPE_DIRECT) {
 696                 pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP);
 697                 (void) ddi_pathname(cdip, pathname);
 698         }
 699 
 700         /* Attempt to offline the logical units */
 701         if (ilp->lun_pip != NULL) {
 702 
 703                 /* virt/mdi */
 704                 ndi_devi_enter(scsi_vhci_dip, &circ);
 705                 if ((lun_free == B_TRUE) &&
 706                     (ilp->lun_state & ISCSI_LUN_STATE_ONLINE)) {
 707                         rval = mdi_pi_offline(ilp->lun_pip,
 708                             NDI_DEVI_REMOVE);
 709                 } else {
 710                         rval = mdi_pi_offline(ilp->lun_pip, 0);
 711                 }
 712 
 713                 if (rval == MDI_SUCCESS) {
 714                         ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
 715                         ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
 716                         if (lun_free == B_TRUE) {
 717                                 (void) mdi_prop_remove(ilp->lun_pip, NULL);
 718                                 (void) mdi_pi_free(ilp->lun_pip, 0);
 719                         }
 720                         offline = B_TRUE;
 721                 } else {
 722                         status = ISCSI_STATUS_BUSY;
 723                         if (lun_free == B_FALSE) {
 724                                 ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
 725                                 offline = B_TRUE;
 726                         }
 727                 }
 728                 ndi_devi_exit(scsi_vhci_dip, circ);
 729 
 730         } else  {
 731 
 732                 /* phys/ndi */
 733                 ndi_devi_enter(ihp->hba_dip, &circ);
 734                 if ((lun_free == B_TRUE) &&
 735                     (ilp->lun_state & ISCSI_LUN_STATE_ONLINE)) {
 736                         rval = ndi_devi_offline(
 737                             ilp->lun_dip, NDI_DEVI_REMOVE);
 738                 } else {
 739                         rval = ndi_devi_offline(
 740                             ilp->lun_dip, 0);
 741                 }
 742                 if (rval != NDI_SUCCESS) {
 743                         status = ISCSI_STATUS_BUSY;
 744                         if (lun_free == B_FALSE) {
 745                                 ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
 746                                 offline = B_TRUE;
 747                         }
 748                 } else {
 749                         ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
 750                         ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
 751                         offline = B_TRUE;
 752                 }
 753                 ndi_devi_exit(ihp->hba_dip, circ);
 754         }
 755 
 756         if (offline == B_TRUE && pathname != NULL &&
 757             ilp->lun_type == DTYPE_DIRECT) {
 758                 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) !=
 759                     DDI_SUCCESS) {
 760                         kmem_free(pathname, MAXNAMELEN + 1);
 761                         return (status);
 762                 }
 | 
   1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2019 Nexenta Systems, Inc.
  29  */
  30 
  31 /*
  32  * iSCSI logical unit interfaces
  33  */
  34 
  35 #include "iscsi.h"
  36 #include <sys/bootprops.h>
  37 #include <sys/sysevent/eventdefs.h>
  38 #include <sys/sysevent/dev.h>
  39 
  40 /* tpgt bytes in string form */
  41 #define TPGT_EXT_SIZE   5
  42 
  43 /* logical unit number bytes in string form */
  44 #define LUN_EXT_SIZE    10
  45 
  46 /*
  47  * Addition addr size of size of ',' + max str form of tpgt (2 bytes) +
  48  * ',' + max str form of logical unit number (4 bytes).
  49  */
  50 #define ADDR_EXT_SIZE   (1 + TPGT_EXT_SIZE + 1 + LUN_EXT_SIZE)
  51 
  52 /* internal interfaces */
  53 static iscsi_status_t iscsi_lun_virt_create(iscsi_sess_t *isp,
  54     uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq);
  55 static iscsi_status_t iscsi_lun_phys_create(iscsi_sess_t *isp,
 
 
 112         (void) snprintf(addr,
 113             (strlen((char *)isp->sess_name) +
 114             ADDR_EXT_SIZE + 1),
 115             "%02X%02X%s%04X,%d", isp->sess_isid[4],
 116             isp->sess_isid[5], isp->sess_name,
 117             isp->sess_tpgt_nego & 0xFFFF, lun_num);
 118 
 119         /* allocate space for lun struct */
 120         ilp = kmem_zalloc(sizeof (iscsi_lun_t), KM_SLEEP);
 121         ilp->lun_sig = ISCSI_SIG_LUN;
 122         ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
 123         ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
 124 
 125         /* initialize common LU information */
 126         ilp->lun_num     = lun_num;
 127         ilp->lun_addr_type  = lun_addr_type;
 128         ilp->lun_sess            = isp;
 129         ilp->lun_addr            = addr;
 130         ilp->lun_type            = inq->inq_dtype & DTYPE_MASK;
 131         ilp->lun_oid     = oid_tmp;
 132         /*
 133          * Setting refcnt to 1 is the first hold for the LUN structure.
 134          */
 135         ilp->lun_refcnt          = 1;
 136         mutex_init(&ilp->lun_mutex, NULL, MUTEX_DRIVER, NULL);
 137 
 138         bcopy(inq->inq_vid, ilp->lun_vid, sizeof (inq->inq_vid));
 139         bcopy(inq->inq_pid, ilp->lun_pid, sizeof (inq->inq_pid));
 140 
 141         /* store GUID if valid one exists */
 142         if (guid != NULL) {
 143                 ilp->lun_guid_size = strlen(guid) + 1;
 144                 ilp->lun_guid = kmem_zalloc(ilp->lun_guid_size, KM_SLEEP);
 145                 (void) strcpy(ilp->lun_guid, guid);
 146         } else {
 147                 ilp->lun_guid_size = 0;
 148                 ilp->lun_guid = NULL;
 149         }
 150 
 151         /*
 152          * We need to add the lun to our lists now because during the
 153          * lun creation we will get called back into multiple times
 154          * depending on the createion type.  These callbacks will
 155          * occur via our tran_init_lun, tran_get_name, tran_get_bus_addr,
 156          * tran_init_pkt, tran_start.
 
 183                 } else {
 184                         /* if not head, set prev lun's next to our next */
 185                         for (ilp_tmp = isp->sess_lun_list; ilp_tmp;
 186                             ilp_tmp = ilp_tmp->lun_next) {
 187                                 if (ilp_tmp->lun_next == ilp) {
 188                                         ilp_tmp->lun_next = ilp->lun_next;
 189                                         break;
 190                                 }
 191                         }
 192                 }
 193 
 194                 kmem_free(ilp->lun_addr,
 195                     (strlen((char *)isp->sess_name) +
 196                     ADDR_EXT_SIZE + 1));
 197                 ilp->lun_addr = NULL;
 198 
 199                 if (ilp->lun_guid != NULL) {
 200                         kmem_free(ilp->lun_guid, ilp->lun_guid_size);
 201                         ilp->lun_guid = NULL;
 202                 }
 203                 mutex_destroy(&ilp->lun_mutex);
 204                 kmem_free(ilp, sizeof (iscsi_lun_t));
 205         } else {
 206                 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
 207                 ilp->lun_state |= ISCSI_LUN_STATE_ONLINE;
 208                 ilp->lun_time_online = ddi_get_time();
 209 
 210                 /* Check whether this is the required LUN for iscsi boot */
 211                 if (iscsiboot_prop != NULL && isp->sess_boot == B_TRUE &&
 212                     iscsiboot_prop->boot_tgt.lun_online == 0) {
 213                         lun_num_ptr =
 214                             (uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun;
 215                         boot_lun_num = (uint16_t)(*lun_num_ptr);
 216                         if (boot_lun_num == ilp->lun_num) {
 217                                 /*
 218                                  * During iscsi boot, the boot lun has been
 219                                  * online, we should set the "online flag".
 220                                  */
 221                                 iscsiboot_prop->boot_tgt.lun_online = 1;
 222                         }
 223                 }
 224         }
 225         rw_exit(&isp->sess_lun_list_rwlock);
 226 
 227         return (rtn);
 228 }
 229 
 230 void
 231 iscsi_lun_hold(iscsi_lun_t *ilp)
 232 {
 233         mutex_enter(&ilp->lun_mutex);
 234         /*
 235          * By design lun_refcnt should never be zero when this routine
 236          * is called. When the LUN is created the refcnt is set to 1.
 237          * If iscsi_lun_rele is called and the refcnt goes to zero the
 238          * structure will be freed so this method shouldn't be called
 239          * afterwards.
 240          */
 241         ASSERT(ilp->lun_refcnt > 0);
 242         ilp->lun_refcnt++;
 243         mutex_exit(&ilp->lun_mutex);
 244 }
 245 
 246 void
 247 iscsi_lun_rele(iscsi_lun_t *ilp)
 248 {
 249         ASSERT(ilp != NULL);
 250 
 251         mutex_enter(&ilp->lun_mutex);
 252         ASSERT(ilp->lun_refcnt > 0);
 253         if (--ilp->lun_refcnt == 0) {
 254                 iscsi_sess_t            *isp;
 255 
 256                 isp = ilp->lun_sess;
 257                 ASSERT(isp != NULL);
 258 
 259                 /* ---- release its memory ---- */
 260                 kmem_free(ilp->lun_addr, (strlen((char *)isp->sess_name) +
 261                     ADDR_EXT_SIZE + 1));
 262 
 263                 if (ilp->lun_guid != NULL) {
 264                         kmem_free(ilp->lun_guid, ilp->lun_guid_size);
 265                 }
 266                 mutex_destroy(&ilp->lun_mutex);
 267                 kmem_free(ilp, sizeof (iscsi_lun_t));
 268         } else {
 269                 mutex_exit(&ilp->lun_mutex);
 270         }
 271 }
 272 
 273 /*
 274  * iscsi_lun_cmd_cancel -- as the name implies, cancel all commands for the lun
 275  *
 276  * This code is similar to the timeout function with a lot less checking of
 277  * state before sending the ABORT event for commands on the pending queue.
 278  *
 279  * This function is only used by iscsi_lun_destroy().
 280  */
 281 static void
 282 iscsi_lun_cmd_cancel(iscsi_lun_t *ilp)
 283 {
 284         iscsi_sess_t    *isp;
 285         iscsi_cmd_t     *icmdp, *nicmdp;
 286 
 287         isp = ilp->lun_sess;
 288         rw_enter(&isp->sess_state_rwlock, RW_READER);
 289         mutex_enter(&isp->sess_queue_pending.mutex);
 290         for (icmdp = isp->sess_queue_pending.head;
 291              icmdp; icmdp = nicmdp) {
 292                 nicmdp = icmdp->cmd_next;
 293 
 294                 /*
 295                  * For commands on the pending queue we can go straight
 296                  * to and abort request which will free the command
 297                  * and call back to the complete function.
 298                  */
 299                 iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E4, isp);
 300         }
 301         mutex_exit(&isp->sess_queue_pending.mutex);
 302         rw_exit(&isp->sess_state_rwlock);
 303 }
 304 
 305 /*
 306  * iscsi_lun_destroy - offline and remove lun
 307  *
 308  * This interface is called when a name service change has
 309  * occured and the storage is no longer available to this
 310  * initiator.  This function will offline and free the
 311  * solaris node resources.  Then it will free all iscsi lun
 312  * resources.
 313  *
 314  * This function can fail with ISCSI_STATUS_BUSY if the
 315  * logical unit is in use.  The user should unmount or
 316  * close the device and perform the nameservice operation
 317  * again if this occurs.
 318  */
 319 iscsi_status_t
 320 iscsi_lun_destroy(iscsi_hba_t *ihp, iscsi_lun_t *ilp)
 321 {
 322         iscsi_status_t          status          = ISCSI_STATUS_SUCCESS;
 323         iscsi_sess_t            *isp            = NULL;
 324         iscsi_lun_t             *t_ilp          = NULL;
 325 
 326         ASSERT(ilp != NULL);
 327         isp = ilp->lun_sess;
 328         ASSERT(isp != NULL);
 329 
 330         /* flush all outstanding commands first */
 331         iscsi_lun_cmd_cancel(ilp);
 332 
 333         /* attempt to offline and free solaris node */
 334         status = iscsi_lun_offline(ihp, ilp, B_TRUE);
 335 
 336         /* If we successfully unplumbed the lun remove it from our lists */
 337         if (ISCSI_SUCCESS(status)) {
 338                 if (isp->sess_lun_list == ilp) {
 339                         /* target first item in list */
 340                         isp->sess_lun_list = ilp->lun_next;
 341                 } else {
 342                         /*
 343                          * search session list for ilp pointing
 344                          * to lun being removed.  Then
 345                          * update that luns next pointer.
 346                          */
 347                         t_ilp = isp->sess_lun_list;
 348                         while (t_ilp->lun_next != NULL) {
 349                                 if (t_ilp->lun_next == ilp) {
 350                                         break;
 351                                 }
 352                                 t_ilp = t_ilp->lun_next;
 353                         }
 354                         if (t_ilp->lun_next == ilp) {
 355                                 t_ilp->lun_next = ilp->lun_next;
 356                         } else {
 357                                 /* couldn't find session */
 358                                 ASSERT(FALSE);
 359                         }
 360                 }
 361 
 362                 iscsi_lun_rele(ilp);
 363         }
 364 
 365         return (status);
 366 }
 367 
 368 /*
 369  * +--------------------------------------------------------------------+
 370  * | External Logical Unit Interfaces                                   |
 371  * +--------------------------------------------------------------------+
 372  */
 373 
 374 /*
 375  * iscsi_lun_virt_create - Creates solaris logical unit via MDI
 376  */
 377 static iscsi_status_t
 378 iscsi_lun_virt_create(iscsi_sess_t *isp, uint16_t lun_num, iscsi_lun_t *ilp,
 379     struct scsi_inquiry *inq)
 380 {
 381         iscsi_status_t          rtn             = ISCSI_STATUS_INTERNAL_ERROR;
 382         int                     mdi_rtn         = MDI_FAILURE;
 383         iscsi_hba_t             *ihp            = NULL;
 
 705  * be called to both offline and free the logical unit.
 706  * (This operates soley on the solaris node states.
 707  * iscsi_lun_destroy() should be called when attempting
 708  * to free all iscsi lun resources.)
 709  *
 710  * This function can fail with ISCSI_STATUS_BUSY if the
 711  * logical unit is in use.  The user should unmount or
 712  * close the device and perform the nameservice operation
 713  * again if this occurs.
 714  *
 715  * If we fail to offline a LUN that we don't want to destroy,
 716  * we will mark it with invalid state. If this LUN still
 717  * exists on the target, we can have another chance to online
 718  * it again when we do the LUN enumeration.
 719  */
 720 iscsi_status_t
 721 iscsi_lun_offline(iscsi_hba_t *ihp, iscsi_lun_t *ilp, boolean_t lun_free)
 722 {
 723         iscsi_status_t          status          = ISCSI_STATUS_SUCCESS;
 724         int                     circ            = 0;
 725         dev_info_t              *cdip;
 726         char                    *pathname       = NULL;
 727         boolean_t               offline         = B_FALSE;
 728         nvlist_t                *attr_list      = NULL;
 729 
 730         ASSERT(ilp != NULL);
 731         ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL));
 732 
 733         if (ilp->lun_pip == NULL)
 734                 cdip = ilp->lun_dip;
 735         else
 736                 cdip = mdi_pi_get_client(ilp->lun_pip);
 737 
 738         if (cdip != NULL && ilp->lun_type == DTYPE_DIRECT) {
 739                 pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP);
 740                 (void) ddi_pathname(cdip, pathname);
 741         }
 742 
 743         /* Attempt to offline the logical units */
 744         if (ilp->lun_pip != NULL) {
 745                 /* virt/mdi */
 746                 ndi_devi_enter(scsi_vhci_dip, &circ);
 747                 if (mdi_pi_offline(ilp->lun_pip, 0) == MDI_SUCCESS) {
 748                         ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
 749                         ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
 750                         if (lun_free == B_TRUE) {
 751                                 (void) mdi_prop_remove(ilp->lun_pip, NULL);
 752                                 (void) mdi_pi_free(ilp->lun_pip, 0);
 753                         }
 754                         offline = B_TRUE;
 755                 } else {
 756                         status = ISCSI_STATUS_BUSY;
 757                         if (lun_free == B_FALSE) {
 758                                 ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
 759                                 offline = B_TRUE;
 760                         }
 761                 }
 762                 ndi_devi_exit(scsi_vhci_dip, circ);
 763 
 764         } else  {
 765                 /* phys/ndi */
 766                 int flags = NDI_DEVFS_CLEAN;
 767 
 768                 ndi_devi_enter(ihp->hba_dip, &circ);
 769                 if (lun_free == B_TRUE &&
 770                     (ilp->lun_state & ISCSI_LUN_STATE_ONLINE))
 771                         flags |= NDI_DEVI_REMOVE;
 772                 if (ndi_devi_offline(ilp->lun_dip, flags) != NDI_SUCCESS) {
 773                         status = ISCSI_STATUS_BUSY;
 774                         if (lun_free == B_FALSE) {
 775                                 ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
 776                                 offline = B_TRUE;
 777                         }
 778                 } else {
 779                         ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
 780                         ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
 781                         offline = B_TRUE;
 782                 }
 783                 ndi_devi_exit(ihp->hba_dip, circ);
 784         }
 785 
 786         if (offline == B_TRUE && pathname != NULL &&
 787             ilp->lun_type == DTYPE_DIRECT) {
 788                 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) !=
 789                     DDI_SUCCESS) {
 790                         kmem_free(pathname, MAXNAMELEN + 1);
 791                         return (status);
 792                 }
 |