Print this page
usr/src/common/zfs/zprop_common.c

@@ -20,237 +20,83 @@
  */
 
 /*
  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
  */
+
 /*
+ * Copyright 2011 cyril.galibern@opensvc.com
  * Copyright (c) 2011 Bayard G. Bell.  All rights reserved.
  * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
- * Copyright 2017 Nexenta Systems, Inc.
+ * Copyright 2019 Nexenta Systems, Inc.
  */
-/*
- * Copyright 2011 cyril.galibern@opensvc.com
- */
 
 /*
  * SCSI disk target driver.
  */
-#include <sys/scsi/scsi.h>
+#include <sys/aio_req.h>
+#include <sys/byteorder.h>
+#include <sys/cdio.h>
+#include <sys/cmlb.h>
+#include <sys/debug.h>
 #include <sys/dkbad.h>
-#include <sys/dklabel.h>
 #include <sys/dkio.h>
-#include <sys/fdio.h>
-#include <sys/cdio.h>
-#include <sys/mhd.h>
-#include <sys/vtoc.h>
+#include <sys/dkioc_free_util.h>
+#include <sys/dklabel.h>
 #include <sys/dktp/fdisk.h>
+#include <sys/efi_partition.h>
+#include <sys/fdio.h>
+#include <sys/fm/protocol.h>
+#include <sys/fs/dv_node.h>
 #include <sys/kstat.h>
-#include <sys/vtrace.h>
-#include <sys/note.h>
-#include <sys/thread.h>
+#include <sys/mhd.h>
 #include <sys/proc.h>
-#include <sys/efi_partition.h>
-#include <sys/var.h>
-#include <sys/aio_req.h>
-
-#ifdef __lock_lint
-#define _LP64
-#define __amd64
-#endif
-
-#if (defined(__fibre))
-/* Note: is there a leadville version of the following? */
-#include <sys/fc4/fcal_linkapp.h>
-#endif
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/targets/sddef.h>
+#include <sys/sdt.h>
+#include <sys/sysevent/dev.h>
+#include <sys/sysevent/eventdefs.h>
 #include <sys/taskq.h>
+#include <sys/thread.h>
 #include <sys/uuid.h>
-#include <sys/byteorder.h>
-#include <sys/sdt.h>
+#include <sys/var.h>
+#include <sys/vtoc.h>
+#include <sys/vtrace.h>
 
 #include "sd_xbuf.h"
 
-#include <sys/scsi/targets/sddef.h>
-#include <sys/cmlb.h>
-#include <sys/sysevent/eventdefs.h>
-#include <sys/sysevent/dev.h>
-
-#include <sys/fm/protocol.h>
-
-/*
- * Loadable module info.
- */
-#if (defined(__fibre))
-#define SD_MODULE_NAME  "SCSI SSA/FCAL Disk Driver"
-#else /* !__fibre */
 #define SD_MODULE_NAME  "SCSI Disk Driver"
-#endif /* !__fibre */
+static  char *sd_label = "sd";
 
 /*
- * Define the interconnect type, to allow the driver to distinguish
- * between parallel SCSI (sd) and fibre channel (ssd) behaviors.
- *
- * This is really for backward compatibility. In the future, the driver
- * should actually check the "interconnect-type" property as reported by
- * the HBA; however at present this property is not defined by all HBAs,
- * so we will use this #define (1) to permit the driver to run in
- * backward-compatibility mode; and (2) to print a notification message
- * if an FC HBA does not support the "interconnect-type" property.  The
- * behavior of the driver will be to assume parallel SCSI behaviors unless
- * the "interconnect-type" property is defined by the HBA **AND** has a
- * value of either INTERCONNECT_FIBRE, INTERCONNECT_SSA, or
- * INTERCONNECT_FABRIC, in which case the driver will assume Fibre
- * Channel behaviors (as per the old ssd).  (Note that the
- * INTERCONNECT_1394 and INTERCONNECT_USB types are not supported and
- * will result in the driver assuming parallel SCSI behaviors.)
- *
- * (see common/sys/scsi/impl/services.h)
- *
- * Note: For ssd semantics, don't use INTERCONNECT_FABRIC as the default
- * since some FC HBAs may already support that, and there is some code in
- * the driver that already looks for it.  Using INTERCONNECT_FABRIC as the
- * default would confuse that code, and besides things should work fine
- * anyways if the FC HBA already reports INTERCONNECT_FABRIC for the
- * "interconnect_type" property.
- *
- */
-#if (defined(__fibre))
-#define SD_DEFAULT_INTERCONNECT_TYPE    SD_INTERCONNECT_FIBRE
-#else
-#define SD_DEFAULT_INTERCONNECT_TYPE    SD_INTERCONNECT_PARALLEL
-#endif
-
-/*
- * The name of the driver, established from the module name in _init.
- */
-static  char *sd_label                  = NULL;
-
-/*
- * Driver name is unfortunately prefixed on some driver.conf properties.
- */
-#if (defined(__fibre))
-#define sd_max_xfer_size                ssd_max_xfer_size
-#define sd_config_list                  ssd_config_list
-static  char *sd_max_xfer_size          = "ssd_max_xfer_size";
-static  char *sd_config_list            = "ssd-config-list";
-#else
-static  char *sd_max_xfer_size          = "sd_max_xfer_size";
-static  char *sd_config_list            = "sd-config-list";
-#endif
-
-/*
  * Driver global variables
  */
 
-#if (defined(__fibre))
-/*
- * These #defines are to avoid namespace collisions that occur because this
- * code is currently used to compile two separate driver modules: sd and ssd.
- * All global variables need to be treated this way (even if declared static)
- * in order to allow the debugger to resolve the names properly.
- * It is anticipated that in the near future the ssd module will be obsoleted,
- * at which time this namespace issue should go away.
- */
-#define sd_state                        ssd_state
-#define sd_io_time                      ssd_io_time
-#define sd_failfast_enable              ssd_failfast_enable
-#define sd_ua_retry_count               ssd_ua_retry_count
-#define sd_report_pfa                   ssd_report_pfa
-#define sd_max_throttle                 ssd_max_throttle
-#define sd_min_throttle                 ssd_min_throttle
-#define sd_rot_delay                    ssd_rot_delay
-
-#define sd_retry_on_reservation_conflict        \
-                                        ssd_retry_on_reservation_conflict
-#define sd_reinstate_resv_delay         ssd_reinstate_resv_delay
-#define sd_resv_conflict_name           ssd_resv_conflict_name
-
-#define sd_component_mask               ssd_component_mask
-#define sd_level_mask                   ssd_level_mask
-#define sd_debug_un                     ssd_debug_un
-#define sd_error_level                  ssd_error_level
-
-#define sd_xbuf_active_limit            ssd_xbuf_active_limit
-#define sd_xbuf_reserve_limit           ssd_xbuf_reserve_limit
-
-#define sd_tr                           ssd_tr
-#define sd_reset_throttle_timeout       ssd_reset_throttle_timeout
-#define sd_qfull_throttle_timeout       ssd_qfull_throttle_timeout
-#define sd_qfull_throttle_enable        ssd_qfull_throttle_enable
-#define sd_check_media_time             ssd_check_media_time
-#define sd_wait_cmds_complete           ssd_wait_cmds_complete
-#define sd_label_mutex                  ssd_label_mutex
-#define sd_detach_mutex                 ssd_detach_mutex
-#define sd_log_buf                      ssd_log_buf
-#define sd_log_mutex                    ssd_log_mutex
-
-#define sd_disk_table                   ssd_disk_table
-#define sd_disk_table_size              ssd_disk_table_size
-#define sd_sense_mutex                  ssd_sense_mutex
-#define sd_cdbtab                       ssd_cdbtab
-
-#define sd_cb_ops                       ssd_cb_ops
-#define sd_ops                          ssd_ops
-#define sd_additional_codes             ssd_additional_codes
-#define sd_tgops                        ssd_tgops
-
-#define sd_minor_data                   ssd_minor_data
-#define sd_minor_data_efi               ssd_minor_data_efi
-
-#define sd_tq                           ssd_tq
-#define sd_wmr_tq                       ssd_wmr_tq
-#define sd_taskq_name                   ssd_taskq_name
-#define sd_wmr_taskq_name               ssd_wmr_taskq_name
-#define sd_taskq_minalloc               ssd_taskq_minalloc
-#define sd_taskq_maxalloc               ssd_taskq_maxalloc
-
-#define sd_dump_format_string           ssd_dump_format_string
-
-#define sd_iostart_chain                ssd_iostart_chain
-#define sd_iodone_chain                 ssd_iodone_chain
-
-#define sd_pm_idletime                  ssd_pm_idletime
-
-#define sd_force_pm_supported           ssd_force_pm_supported
-
-#define sd_dtype_optical_bind           ssd_dtype_optical_bind
-
-#define sd_ssc_init                     ssd_ssc_init
-#define sd_ssc_send                     ssd_ssc_send
-#define sd_ssc_fini                     ssd_ssc_fini
-#define sd_ssc_assessment               ssd_ssc_assessment
-#define sd_ssc_post                     ssd_ssc_post
-#define sd_ssc_print                    ssd_ssc_print
-#define sd_ssc_ereport_post             ssd_ssc_ereport_post
-#define sd_ssc_set_info                 ssd_ssc_set_info
-#define sd_ssc_extract_info             ssd_ssc_extract_info
-
-#endif
-
 #ifdef  SDDEBUG
 int     sd_force_pm_supported           = 0;
 #endif  /* SDDEBUG */
 
 void *sd_state                          = NULL;
 int sd_io_time                          = SD_IO_TIME;
-int sd_failfast_enable                  = 1;
 int sd_ua_retry_count                   = SD_UA_RETRY_COUNT;
 int sd_report_pfa                       = 1;
 int sd_max_throttle                     = SD_MAX_THROTTLE;
 int sd_min_throttle                     = SD_MIN_THROTTLE;
 int sd_rot_delay                        = 4; /* Default 4ms Rotation delay */
 int sd_qfull_throttle_enable            = TRUE;
 
 int sd_retry_on_reservation_conflict    = 1;
 int sd_reinstate_resv_delay             = SD_REINSTATE_RESV_DELAY;
-_NOTE(SCHEME_PROTECTS_DATA("safe sharing", sd_reinstate_resv_delay))
+int sd_enable_lun_reset                 = FALSE;
 
-static int sd_dtype_optical_bind        = -1;
+/*
+ * Default safe I/O delay threshold of 30s for all devices.
+ * Can be overriden for vendor/device id in sd.conf
+ */
+hrtime_t sd_slow_io_threshold           = 30LL * NANOSEC;
 
-/* Note: the following is not a bug, it really is "sd_" and not "ssd_" */
-static  char *sd_resv_conflict_name     = "sd_retry_on_reservation_conflict";
-
 /*
  * Global data for debug logging. To enable debug printing, sd_component_mask
  * and sd_level_mask should be set to the desired bit patterns as outlined in
  * sddef.h.
  */

@@ -292,13 +138,10 @@
  * sd_detach_mutex protects un_layer_count, un_detach_count, and
  * un_opens_in_progress in the sd_lun structure.
  */
 static kmutex_t sd_detach_mutex;
 
-_NOTE(MUTEX_PROTECTS_DATA(sd_detach_mutex,
-        sd_lun::{un_layer_count un_detach_count un_opens_in_progress}))
-
 /*
  * Global buffer and mutex for debug logging
  */
 static char     sd_log_buf[1024];
 static kmutex_t sd_log_mutex;

@@ -323,16 +166,10 @@
 #define SD_SCSI_LUN_DETACH      1
 
 static kmutex_t sd_scsi_target_lun_mutex;
 static struct sd_scsi_hba_tgt_lun       *sd_scsi_target_lun_head = NULL;
 
-_NOTE(MUTEX_PROTECTS_DATA(sd_scsi_target_lun_mutex,
-    sd_scsi_hba_tgt_lun::next sd_scsi_hba_tgt_lun::pdip))
-
-_NOTE(MUTEX_PROTECTS_DATA(sd_scsi_target_lun_mutex,
-    sd_scsi_target_lun_head))
-
 /*
  * "Smart" Probe Caching structs, globals, #defines, etc.
  * For parallel scsi and non-self-identify device only.
  */
 

@@ -349,20 +186,46 @@
 
 static kmutex_t sd_scsi_probe_cache_mutex;
 static struct   sd_scsi_probe_cache *sd_scsi_probe_cache_head = NULL;
 
 /*
- * Really we only need protection on the head of the linked list, but
- * better safe than sorry.
+ * Create taskq for all targets in the system. This is created at
+ * _init(9E) and destroyed at _fini(9E).
+ *
+ * Note: here we set the minalloc to a reasonably high number to ensure that
+ * we will have an adequate supply of task entries available at interrupt time.
+ * This is used in conjunction with the TASKQ_PREPOPULATE flag in
+ * sd_create_taskq().  Since we do not want to sleep for allocations at
+ * interrupt time, set maxalloc equal to minalloc. That way we will just fail
+ * the command if we ever try to dispatch more than SD_TASKQ_MAXALLOC taskq
+ * requests any one instant in time.
  */
-_NOTE(MUTEX_PROTECTS_DATA(sd_scsi_probe_cache_mutex,
-    sd_scsi_probe_cache::next sd_scsi_probe_cache::pdip))
+#define SD_TASKQ_NUMTHREADS     8
+#define SD_TASKQ_MINALLOC       256
+#define SD_TASKQ_MAXALLOC       256
 
-_NOTE(MUTEX_PROTECTS_DATA(sd_scsi_probe_cache_mutex,
-    sd_scsi_probe_cache_head))
+static taskq_t  *sd_tq = NULL;
 
+static int      sd_taskq_minalloc = SD_TASKQ_MINALLOC;
+static int      sd_taskq_maxalloc = SD_TASKQ_MAXALLOC;
+
+#define SD_BAIL_CHECK(a) if ((a)->un_detach_count != 0) { \
+        mutex_exit(SD_MUTEX((a))); \
+        return (ENXIO); \
+        }
 /*
+ * The following task queue is being created for the write part of
+ * read-modify-write of non-512 block size devices.
+ * Limit the number of threads to 1 for now. This number has been chosen
+ * considering the fact that it applies only to dvd ram drives/MO drives
+ * currently. Performance for which is not main criteria at this stage.
+ * Note: It needs to be explored if we can use a single taskq in future
+ */
+#define SD_WMR_TASKQ_NUMTHREADS 1
+static taskq_t  *sd_wmr_tq = NULL;
+
+/*
  * Power attribute table
  */
 static sd_power_attr_ss sd_pwr_ss = {
         { "NAME=spindle-motor", "0=off", "1=on", NULL },
         {0, 100},

@@ -390,12 +253,10 @@
 
 /*
  * Vendor specific data name property declarations
  */
 
-#if defined(__fibre) || defined(__i386) ||defined(__amd64)
-
 static sd_tunables seagate_properties = {
         SEAGATE_THROTTLE_VALUE,
         0,
         0,
         0,

@@ -404,11 +265,10 @@
         0,
         0,
         0
 };
 
-
 static sd_tunables fujitsu_properties = {
         FUJITSU_THROTTLE_VALUE,
         0,
         0,
         0,

@@ -477,16 +337,10 @@
         PIRUS_MIN_THROTTLE_VALUE,
         PIRUS_DISKSORT_DISABLED_FLAG,
         PIRUS_LUN_RESET_ENABLED_FLAG
 };
 
-#endif
-
-#if (defined(__sparc) && !defined(__fibre)) || \
-        (defined(__i386) || defined(__amd64))
-
-
 static sd_tunables elite_properties = {
         ELITE_THROTTLE_VALUE,
         0,
         0,
         0,

@@ -507,12 +361,10 @@
         0,
         0,
         0
 };
 
-#endif /* Fibre or not */
-
 static sd_tunables lsi_properties_scsi = {
         LSI_THROTTLE_VALUE,
         0,
         LSI_NOTREADY_RETRIES,
         0,

@@ -558,21 +410,17 @@
         0,
         0,
         1
 };
 
-
-
 #if (defined(SD_PROP_TST))
-
 #define SD_TST_CTYPE_VAL        CTYPE_CDROM
 #define SD_TST_THROTTLE_VAL     16
 #define SD_TST_NOTREADY_VAL     12
 #define SD_TST_BUSY_VAL         60
 #define SD_TST_RST_RETRY_VAL    36
 #define SD_TST_RSV_REL_TIME     60
-
 static sd_tunables tst_properties = {
         SD_TST_THROTTLE_VAL,
         SD_TST_CTYPE_VAL,
         SD_TST_NOTREADY_VAL,
         SD_TST_BUSY_VAL,

@@ -613,11 +461,10 @@
  *       ST318202F is a Legacy device
  *       MAM3182FC, MAM3364FC, MAM3738FC do not appear to have ever been
  *       made with an FC connection. The entries here are a legacy.
  */
 static sd_disk_config_t sd_disk_table[] = {
-#if defined(__fibre) || defined(__i386) || defined(__amd64)
         { "SEAGATE ST34371FC", SD_CONF_BSET_THROTTLE, &seagate_properties },
         { "SEAGATE ST19171FC", SD_CONF_BSET_THROTTLE, &seagate_properties },
         { "SEAGATE ST39102FC", SD_CONF_BSET_THROTTLE, &seagate_properties },
         { "SEAGATE ST39103FC", SD_CONF_BSET_THROTTLE, &seagate_properties },
         { "SEAGATE ST118273F", SD_CONF_BSET_THROTTLE, &seagate_properties },

@@ -663,77 +510,79 @@
         { "DELL    MD3000i",    SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
         { "LSI     INF",        SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
         { "ENGENIO INF",        SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
         { "SGI     TP",         SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
         { "SGI     IS",         SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
-        { "*CSM100_*",          SD_CONF_BSET_NRR_COUNT |
-                        SD_CONF_BSET_CACHE_IS_NV, &lsi_oem_properties },
-        { "*CSM200_*",          SD_CONF_BSET_NRR_COUNT |
-                        SD_CONF_BSET_CACHE_IS_NV, &lsi_oem_properties },
+        { "*CSM100_*",          SD_CONF_BSET_NRR_COUNT|
+                                SD_CONF_BSET_CACHE_IS_NV,
+                                &lsi_oem_properties },
+        { "*CSM200_*",          SD_CONF_BSET_NRR_COUNT|
+                                SD_CONF_BSET_CACHE_IS_NV,
+                                &lsi_oem_properties },
         { "Fujitsu SX300",      SD_CONF_BSET_THROTTLE,  &lsi_oem_properties },
         { "LSI",                SD_CONF_BSET_NRR_COUNT, &lsi_properties },
-        { "SUN     T3", SD_CONF_BSET_THROTTLE |
+        { "SUN     T3",         SD_CONF_BSET_THROTTLE|
                         SD_CONF_BSET_BSY_RETRY_COUNT|
                         SD_CONF_BSET_RST_RETRIES|
                         SD_CONF_BSET_RSV_REL_TIME,
                 &purple_properties },
-        { "SUN     SESS01", SD_CONF_BSET_THROTTLE |
+        { "SUN     SESS01",     SD_CONF_BSET_THROTTLE|
                 SD_CONF_BSET_BSY_RETRY_COUNT|
                 SD_CONF_BSET_RST_RETRIES|
                 SD_CONF_BSET_RSV_REL_TIME|
                 SD_CONF_BSET_MIN_THROTTLE|
                 SD_CONF_BSET_DISKSORT_DISABLED,
                 &sve_properties },
-        { "SUN     T4", SD_CONF_BSET_THROTTLE |
+        { "SUN     T4",         SD_CONF_BSET_THROTTLE|
                         SD_CONF_BSET_BSY_RETRY_COUNT|
                         SD_CONF_BSET_RST_RETRIES|
                         SD_CONF_BSET_RSV_REL_TIME,
                 &purple_properties },
-        { "SUN     SVE01", SD_CONF_BSET_DISKSORT_DISABLED |
+        { "SUN     SVE01",      SD_CONF_BSET_DISKSORT_DISABLED|
                 SD_CONF_BSET_LUN_RESET_ENABLED,
                 &maserati_properties },
-        { "SUN     SE6920", SD_CONF_BSET_THROTTLE |
+        { "SUN     SE6920",     SD_CONF_BSET_THROTTLE|
                 SD_CONF_BSET_NRR_COUNT|
                 SD_CONF_BSET_BSY_RETRY_COUNT|
                 SD_CONF_BSET_RST_RETRIES|
                 SD_CONF_BSET_MIN_THROTTLE|
                 SD_CONF_BSET_DISKSORT_DISABLED|
                 SD_CONF_BSET_LUN_RESET_ENABLED,
                 &pirus_properties },
-        { "SUN     SE6940", SD_CONF_BSET_THROTTLE |
+        { "SUN     SE6940",     SD_CONF_BSET_THROTTLE|
                 SD_CONF_BSET_NRR_COUNT|
                 SD_CONF_BSET_BSY_RETRY_COUNT|
                 SD_CONF_BSET_RST_RETRIES|
                 SD_CONF_BSET_MIN_THROTTLE|
                 SD_CONF_BSET_DISKSORT_DISABLED|
                 SD_CONF_BSET_LUN_RESET_ENABLED,
                 &pirus_properties },
-        { "SUN     StorageTek 6920", SD_CONF_BSET_THROTTLE |
+        { "SUN     StorageTek 6920", SD_CONF_BSET_THROTTLE|
                 SD_CONF_BSET_NRR_COUNT|
                 SD_CONF_BSET_BSY_RETRY_COUNT|
                 SD_CONF_BSET_RST_RETRIES|
                 SD_CONF_BSET_MIN_THROTTLE|
                 SD_CONF_BSET_DISKSORT_DISABLED|
                 SD_CONF_BSET_LUN_RESET_ENABLED,
                 &pirus_properties },
-        { "SUN     StorageTek 6940", SD_CONF_BSET_THROTTLE |
+        { "SUN     StorageTek 6940", SD_CONF_BSET_THROTTLE|
                 SD_CONF_BSET_NRR_COUNT|
                 SD_CONF_BSET_BSY_RETRY_COUNT|
                 SD_CONF_BSET_RST_RETRIES|
                 SD_CONF_BSET_MIN_THROTTLE|
                 SD_CONF_BSET_DISKSORT_DISABLED|
                 SD_CONF_BSET_LUN_RESET_ENABLED,
                 &pirus_properties },
-        { "SUN     PSX1000", SD_CONF_BSET_THROTTLE |
+        { "SUN     PSX1000",    SD_CONF_BSET_THROTTLE|
                 SD_CONF_BSET_NRR_COUNT|
                 SD_CONF_BSET_BSY_RETRY_COUNT|
                 SD_CONF_BSET_RST_RETRIES|
                 SD_CONF_BSET_MIN_THROTTLE|
                 SD_CONF_BSET_DISKSORT_DISABLED|
                 SD_CONF_BSET_LUN_RESET_ENABLED,
                 &pirus_properties },
-        { "SUN     SE6330", SD_CONF_BSET_THROTTLE |
+        { "SUN     SE6330",     SD_CONF_BSET_THROTTLE|
                 SD_CONF_BSET_NRR_COUNT|
                 SD_CONF_BSET_BSY_RETRY_COUNT|
                 SD_CONF_BSET_RST_RETRIES|
                 SD_CONF_BSET_MIN_THROTTLE|
                 SD_CONF_BSET_DISKSORT_DISABLED|

@@ -744,13 +593,10 @@
         { "STK     OPENstorage", SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
         { "STK     OpenStorage", SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
         { "STK     BladeCtlr",  SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
         { "STK     FLEXLINE",   SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
         { "SYMBIOS", SD_CONF_BSET_NRR_COUNT, &symbios_properties },
-#endif /* fibre or NON-sparc platforms */
-#if ((defined(__sparc) && !defined(__fibre)) ||\
-        (defined(__i386) || defined(__amd64)))
         { "SEAGATE ST42400N", SD_CONF_BSET_THROTTLE, &elite_properties },
         { "SEAGATE ST31200N", SD_CONF_BSET_THROTTLE, &st31200n_properties },
         { "SEAGATE ST41600N", SD_CONF_BSET_TUR_CHECK, NULL },
         { "CONNER  CP30540",  SD_CONF_BSET_NOCACHE,  NULL },
         { "*SUN0104*", SD_CONF_BSET_FAB_DEVID, NULL },

@@ -758,46 +604,46 @@
         { "*SUN0327*", SD_CONF_BSET_FAB_DEVID, NULL },
         { "*SUN0340*", SD_CONF_BSET_FAB_DEVID, NULL },
         { "*SUN0424*", SD_CONF_BSET_FAB_DEVID, NULL },
         { "*SUN0669*", SD_CONF_BSET_FAB_DEVID, NULL },
         { "*SUN1.0G*", SD_CONF_BSET_FAB_DEVID, NULL },
-        { "SYMBIOS INF-01-00       ", SD_CONF_BSET_FAB_DEVID, NULL },
-        { "SYMBIOS", SD_CONF_BSET_THROTTLE|SD_CONF_BSET_NRR_COUNT,
+        { "SYMBIOS INF-01-00",  SD_CONF_BSET_FAB_DEVID, NULL },
+        { "SYMBIOS",            SD_CONF_BSET_THROTTLE|
+                                SD_CONF_BSET_NRR_COUNT,
             &symbios_properties },
-        { "LSI", SD_CONF_BSET_THROTTLE | SD_CONF_BSET_NRR_COUNT,
+        { "LSI",                SD_CONF_BSET_THROTTLE|
+                                SD_CONF_BSET_NRR_COUNT,
             &lsi_properties_scsi },
-#if defined(__i386) || defined(__amd64)
-        { " NEC CD-ROM DRIVE:260 ", (SD_CONF_BSET_PLAYMSF_BCD
-                                    | SD_CONF_BSET_READSUB_BCD
-                                    | SD_CONF_BSET_READ_TOC_ADDR_BCD
-                                    | SD_CONF_BSET_NO_READ_HEADER
-                                    | SD_CONF_BSET_READ_CD_XD4), NULL },
-
-        { " NEC CD-ROM DRIVE:270 ", (SD_CONF_BSET_PLAYMSF_BCD
-                                    | SD_CONF_BSET_READSUB_BCD
-                                    | SD_CONF_BSET_READ_TOC_ADDR_BCD
-                                    | SD_CONF_BSET_NO_READ_HEADER
-                                    | SD_CONF_BSET_READ_CD_XD4), NULL },
-#endif /* __i386 || __amd64 */
-#endif /* sparc NON-fibre or NON-sparc platforms */
-
+        { " NEC CD-ROM DRIVE:260 ", SD_CONF_BSET_PLAYMSF_BCD|
+                                SD_CONF_BSET_READSUB_BCD|
+                                SD_CONF_BSET_READ_TOC_ADDR_BCD|
+                                SD_CONF_BSET_NO_READ_HEADER|
+                                SD_CONF_BSET_READ_CD_XD4,
+                                NULL },
+        { " NEC CD-ROM DRIVE:270 ", SD_CONF_BSET_PLAYMSF_BCD|
+                                SD_CONF_BSET_READSUB_BCD|
+                                SD_CONF_BSET_READ_TOC_ADDR_BCD|
+                                SD_CONF_BSET_NO_READ_HEADER|
+                                SD_CONF_BSET_READ_CD_XD4,
+                                NULL },
 #if (defined(SD_PROP_TST))
-        { "VENDOR  PRODUCT ", (SD_CONF_BSET_THROTTLE
-                                | SD_CONF_BSET_CTYPE
-                                | SD_CONF_BSET_NRR_COUNT
-                                | SD_CONF_BSET_FAB_DEVID
-                                | SD_CONF_BSET_NOCACHE
-                                | SD_CONF_BSET_BSY_RETRY_COUNT
-                                | SD_CONF_BSET_PLAYMSF_BCD
-                                | SD_CONF_BSET_READSUB_BCD
-                                | SD_CONF_BSET_READ_TOC_TRK_BCD
-                                | SD_CONF_BSET_READ_TOC_ADDR_BCD
-                                | SD_CONF_BSET_NO_READ_HEADER
-                                | SD_CONF_BSET_READ_CD_XD4
-                                | SD_CONF_BSET_RST_RETRIES
-                                | SD_CONF_BSET_RSV_REL_TIME
-                                | SD_CONF_BSET_TUR_CHECK), &tst_properties},
+        { "VENDOR  PRODUCT ",   SD_CONF_BSET_THROTTLE|
+                                SD_CONF_BSET_CTYPE|
+                                SD_CONF_BSET_NRR_COUNT|
+                                SD_CONF_BSET_FAB_DEVID|
+                                SD_CONF_BSET_NOCACHE|
+                                SD_CONF_BSET_BSY_RETRY_COUNT|
+                                SD_CONF_BSET_PLAYMSF_BCD|
+                                SD_CONF_BSET_READSUB_BCD|
+                                SD_CONF_BSET_READ_TOC_TRK_BCD|
+                                SD_CONF_BSET_READ_TOC_ADDR_BCD|
+                                SD_CONF_BSET_NO_READ_HEADER|
+                                SD_CONF_BSET_READ_CD_XD4|
+                                SD_CONF_BSET_RST_RETRIES|
+                                SD_CONF_BSET_RSV_REL_TIME|
+                                SD_CONF_BSET_TUR_CHECK,
+                                &tst_properties},
 #endif
 };
 
 static const int sd_disk_table_size =
         sizeof (sd_disk_table)/ sizeof (sd_disk_config_t);

@@ -858,302 +704,29 @@
 static int sd_pm_idletime = 1;
 
 /*
  * Internal function prototypes
  */
+typedef struct unmap_param_hdr_s {
+        uint16_t        uph_data_len;
+        uint16_t        uph_descr_data_len;
+        uint32_t        uph_reserved;
+} unmap_param_hdr_t;
 
-#if (defined(__fibre))
-/*
- * These #defines are to avoid namespace collisions that occur because this
- * code is currently used to compile two separate driver modules: sd and ssd.
- * All function names need to be treated this way (even if declared static)
- * in order to allow the debugger to resolve the names properly.
- * It is anticipated that in the near future the ssd module will be obsoleted,
- * at which time this ugliness should go away.
- */
-#define sd_log_trace                    ssd_log_trace
-#define sd_log_info                     ssd_log_info
-#define sd_log_err                      ssd_log_err
-#define sdprobe                         ssdprobe
-#define sdinfo                          ssdinfo
-#define sd_prop_op                      ssd_prop_op
-#define sd_scsi_probe_cache_init        ssd_scsi_probe_cache_init
-#define sd_scsi_probe_cache_fini        ssd_scsi_probe_cache_fini
-#define sd_scsi_clear_probe_cache       ssd_scsi_clear_probe_cache
-#define sd_scsi_probe_with_cache        ssd_scsi_probe_with_cache
-#define sd_scsi_target_lun_init         ssd_scsi_target_lun_init
-#define sd_scsi_target_lun_fini         ssd_scsi_target_lun_fini
-#define sd_scsi_get_target_lun_count    ssd_scsi_get_target_lun_count
-#define sd_scsi_update_lun_on_target    ssd_scsi_update_lun_on_target
-#define sd_spin_up_unit                 ssd_spin_up_unit
-#define sd_enable_descr_sense           ssd_enable_descr_sense
-#define sd_reenable_dsense_task         ssd_reenable_dsense_task
-#define sd_set_mmc_caps                 ssd_set_mmc_caps
-#define sd_read_unit_properties         ssd_read_unit_properties
-#define sd_process_sdconf_file          ssd_process_sdconf_file
-#define sd_process_sdconf_table         ssd_process_sdconf_table
-#define sd_sdconf_id_match              ssd_sdconf_id_match
-#define sd_blank_cmp                    ssd_blank_cmp
-#define sd_chk_vers1_data               ssd_chk_vers1_data
-#define sd_set_vers1_properties         ssd_set_vers1_properties
-#define sd_check_bdc_vpd                ssd_check_bdc_vpd
-#define sd_check_emulation_mode         ssd_check_emulation_mode
+typedef struct unmap_blk_descr_s {
+        uint64_t        ubd_lba;
+        uint32_t        ubd_lba_cnt;
+        uint32_t        ubd_reserved;
+} unmap_blk_descr_t;
 
-#define sd_get_physical_geometry        ssd_get_physical_geometry
-#define sd_get_virtual_geometry         ssd_get_virtual_geometry
-#define sd_update_block_info            ssd_update_block_info
-#define sd_register_devid               ssd_register_devid
-#define sd_get_devid                    ssd_get_devid
-#define sd_create_devid                 ssd_create_devid
-#define sd_write_deviceid               ssd_write_deviceid
-#define sd_check_vpd_page_support       ssd_check_vpd_page_support
-#define sd_setup_pm                     ssd_setup_pm
-#define sd_create_pm_components         ssd_create_pm_components
-#define sd_ddi_suspend                  ssd_ddi_suspend
-#define sd_ddi_resume                   ssd_ddi_resume
-#define sd_pm_state_change              ssd_pm_state_change
-#define sdpower                         ssdpower
-#define sdattach                        ssdattach
-#define sddetach                        ssddetach
-#define sd_unit_attach                  ssd_unit_attach
-#define sd_unit_detach                  ssd_unit_detach
-#define sd_set_unit_attributes          ssd_set_unit_attributes
-#define sd_create_errstats              ssd_create_errstats
-#define sd_set_errstats                 ssd_set_errstats
-#define sd_set_pstats                   ssd_set_pstats
-#define sddump                          ssddump
-#define sd_scsi_poll                    ssd_scsi_poll
-#define sd_send_polled_RQS              ssd_send_polled_RQS
-#define sd_ddi_scsi_poll                ssd_ddi_scsi_poll
-#define sd_init_event_callbacks         ssd_init_event_callbacks
-#define sd_event_callback               ssd_event_callback
-#define sd_cache_control                ssd_cache_control
-#define sd_get_write_cache_enabled      ssd_get_write_cache_enabled
-#define sd_get_write_cache_changeable   ssd_get_write_cache_changeable
-#define sd_get_nv_sup                   ssd_get_nv_sup
-#define sd_make_device                  ssd_make_device
-#define sdopen                          ssdopen
-#define sdclose                         ssdclose
-#define sd_ready_and_valid              ssd_ready_and_valid
-#define sdmin                           ssdmin
-#define sdread                          ssdread
-#define sdwrite                         ssdwrite
-#define sdaread                         ssdaread
-#define sdawrite                        ssdawrite
-#define sdstrategy                      ssdstrategy
-#define sdioctl                         ssdioctl
-#define sd_mapblockaddr_iostart         ssd_mapblockaddr_iostart
-#define sd_mapblocksize_iostart         ssd_mapblocksize_iostart
-#define sd_checksum_iostart             ssd_checksum_iostart
-#define sd_checksum_uscsi_iostart       ssd_checksum_uscsi_iostart
-#define sd_pm_iostart                   ssd_pm_iostart
-#define sd_core_iostart                 ssd_core_iostart
-#define sd_mapblockaddr_iodone          ssd_mapblockaddr_iodone
-#define sd_mapblocksize_iodone          ssd_mapblocksize_iodone
-#define sd_checksum_iodone              ssd_checksum_iodone
-#define sd_checksum_uscsi_iodone        ssd_checksum_uscsi_iodone
-#define sd_pm_iodone                    ssd_pm_iodone
-#define sd_initpkt_for_buf              ssd_initpkt_for_buf
-#define sd_destroypkt_for_buf           ssd_destroypkt_for_buf
-#define sd_setup_rw_pkt                 ssd_setup_rw_pkt
-#define sd_setup_next_rw_pkt            ssd_setup_next_rw_pkt
-#define sd_buf_iodone                   ssd_buf_iodone
-#define sd_uscsi_strategy               ssd_uscsi_strategy
-#define sd_initpkt_for_uscsi            ssd_initpkt_for_uscsi
-#define sd_destroypkt_for_uscsi         ssd_destroypkt_for_uscsi
-#define sd_uscsi_iodone                 ssd_uscsi_iodone
-#define sd_xbuf_strategy                ssd_xbuf_strategy
-#define sd_xbuf_init                    ssd_xbuf_init
-#define sd_pm_entry                     ssd_pm_entry
-#define sd_pm_exit                      ssd_pm_exit
+/* Max number of block descriptors in UNMAP command */
+#define SD_UNMAP_MAX_DESCR \
+        ((UINT16_MAX - sizeof (unmap_param_hdr_t)) / sizeof (unmap_blk_descr_t))
+/* Max size of the UNMAP parameter list in bytes */
+#define SD_UNMAP_PARAM_LIST_MAXSZ       (sizeof (unmap_param_hdr_t) + \
+        SD_UNMAP_MAX_DESCR * sizeof (unmap_blk_descr_t))
 
-#define sd_pm_idletimeout_handler       ssd_pm_idletimeout_handler
-#define sd_pm_timeout_handler           ssd_pm_timeout_handler
-
-#define sd_add_buf_to_waitq             ssd_add_buf_to_waitq
-#define sdintr                          ssdintr
-#define sd_start_cmds                   ssd_start_cmds
-#define sd_send_scsi_cmd                ssd_send_scsi_cmd
-#define sd_bioclone_alloc               ssd_bioclone_alloc
-#define sd_bioclone_free                ssd_bioclone_free
-#define sd_shadow_buf_alloc             ssd_shadow_buf_alloc
-#define sd_shadow_buf_free              ssd_shadow_buf_free
-#define sd_print_transport_rejected_message     \
-                                        ssd_print_transport_rejected_message
-#define sd_retry_command                ssd_retry_command
-#define sd_set_retry_bp                 ssd_set_retry_bp
-#define sd_send_request_sense_command   ssd_send_request_sense_command
-#define sd_start_retry_command          ssd_start_retry_command
-#define sd_start_direct_priority_command        \
-                                        ssd_start_direct_priority_command
-#define sd_return_failed_command        ssd_return_failed_command
-#define sd_return_failed_command_no_restart     \
-                                        ssd_return_failed_command_no_restart
-#define sd_return_command               ssd_return_command
-#define sd_sync_with_callback           ssd_sync_with_callback
-#define sdrunout                        ssdrunout
-#define sd_mark_rqs_busy                ssd_mark_rqs_busy
-#define sd_mark_rqs_idle                ssd_mark_rqs_idle
-#define sd_reduce_throttle              ssd_reduce_throttle
-#define sd_restore_throttle             ssd_restore_throttle
-#define sd_print_incomplete_msg         ssd_print_incomplete_msg
-#define sd_init_cdb_limits              ssd_init_cdb_limits
-#define sd_pkt_status_good              ssd_pkt_status_good
-#define sd_pkt_status_check_condition   ssd_pkt_status_check_condition
-#define sd_pkt_status_busy              ssd_pkt_status_busy
-#define sd_pkt_status_reservation_conflict      \
-                                        ssd_pkt_status_reservation_conflict
-#define sd_pkt_status_qfull             ssd_pkt_status_qfull
-#define sd_handle_request_sense         ssd_handle_request_sense
-#define sd_handle_auto_request_sense    ssd_handle_auto_request_sense
-#define sd_print_sense_failed_msg       ssd_print_sense_failed_msg
-#define sd_validate_sense_data          ssd_validate_sense_data
-#define sd_decode_sense                 ssd_decode_sense
-#define sd_print_sense_msg              ssd_print_sense_msg
-#define sd_sense_key_no_sense           ssd_sense_key_no_sense
-#define sd_sense_key_recoverable_error  ssd_sense_key_recoverable_error
-#define sd_sense_key_not_ready          ssd_sense_key_not_ready
-#define sd_sense_key_medium_or_hardware_error   \
-                                        ssd_sense_key_medium_or_hardware_error
-#define sd_sense_key_illegal_request    ssd_sense_key_illegal_request
-#define sd_sense_key_unit_attention     ssd_sense_key_unit_attention
-#define sd_sense_key_fail_command       ssd_sense_key_fail_command
-#define sd_sense_key_blank_check        ssd_sense_key_blank_check
-#define sd_sense_key_aborted_command    ssd_sense_key_aborted_command
-#define sd_sense_key_default            ssd_sense_key_default
-#define sd_print_retry_msg              ssd_print_retry_msg
-#define sd_print_cmd_incomplete_msg     ssd_print_cmd_incomplete_msg
-#define sd_pkt_reason_cmd_incomplete    ssd_pkt_reason_cmd_incomplete
-#define sd_pkt_reason_cmd_tran_err      ssd_pkt_reason_cmd_tran_err
-#define sd_pkt_reason_cmd_reset         ssd_pkt_reason_cmd_reset
-#define sd_pkt_reason_cmd_aborted       ssd_pkt_reason_cmd_aborted
-#define sd_pkt_reason_cmd_timeout       ssd_pkt_reason_cmd_timeout
-#define sd_pkt_reason_cmd_unx_bus_free  ssd_pkt_reason_cmd_unx_bus_free
-#define sd_pkt_reason_cmd_tag_reject    ssd_pkt_reason_cmd_tag_reject
-#define sd_pkt_reason_default           ssd_pkt_reason_default
-#define sd_reset_target                 ssd_reset_target
-#define sd_start_stop_unit_callback     ssd_start_stop_unit_callback
-#define sd_start_stop_unit_task         ssd_start_stop_unit_task
-#define sd_taskq_create                 ssd_taskq_create
-#define sd_taskq_delete                 ssd_taskq_delete
-#define sd_target_change_task           ssd_target_change_task
-#define sd_log_dev_status_event         ssd_log_dev_status_event
-#define sd_log_lun_expansion_event      ssd_log_lun_expansion_event
-#define sd_log_eject_request_event      ssd_log_eject_request_event
-#define sd_media_change_task            ssd_media_change_task
-#define sd_handle_mchange               ssd_handle_mchange
-#define sd_send_scsi_DOORLOCK           ssd_send_scsi_DOORLOCK
-#define sd_send_scsi_READ_CAPACITY      ssd_send_scsi_READ_CAPACITY
-#define sd_send_scsi_READ_CAPACITY_16   ssd_send_scsi_READ_CAPACITY_16
-#define sd_send_scsi_GET_CONFIGURATION  ssd_send_scsi_GET_CONFIGURATION
-#define sd_send_scsi_feature_GET_CONFIGURATION  \
-                                        sd_send_scsi_feature_GET_CONFIGURATION
-#define sd_send_scsi_START_STOP_UNIT    ssd_send_scsi_START_STOP_UNIT
-#define sd_send_scsi_INQUIRY            ssd_send_scsi_INQUIRY
-#define sd_send_scsi_TEST_UNIT_READY    ssd_send_scsi_TEST_UNIT_READY
-#define sd_send_scsi_PERSISTENT_RESERVE_IN      \
-                                        ssd_send_scsi_PERSISTENT_RESERVE_IN
-#define sd_send_scsi_PERSISTENT_RESERVE_OUT     \
-                                        ssd_send_scsi_PERSISTENT_RESERVE_OUT
-#define sd_send_scsi_SYNCHRONIZE_CACHE  ssd_send_scsi_SYNCHRONIZE_CACHE
-#define sd_send_scsi_SYNCHRONIZE_CACHE_biodone  \
-                                        ssd_send_scsi_SYNCHRONIZE_CACHE_biodone
-#define sd_send_scsi_MODE_SENSE         ssd_send_scsi_MODE_SENSE
-#define sd_send_scsi_MODE_SELECT        ssd_send_scsi_MODE_SELECT
-#define sd_send_scsi_RDWR               ssd_send_scsi_RDWR
-#define sd_send_scsi_LOG_SENSE          ssd_send_scsi_LOG_SENSE
-#define sd_send_scsi_GET_EVENT_STATUS_NOTIFICATION      \
-                                ssd_send_scsi_GET_EVENT_STATUS_NOTIFICATION
-#define sd_gesn_media_data_valid        ssd_gesn_media_data_valid
-#define sd_alloc_rqs                    ssd_alloc_rqs
-#define sd_free_rqs                     ssd_free_rqs
-#define sd_dump_memory                  ssd_dump_memory
-#define sd_get_media_info_com           ssd_get_media_info_com
-#define sd_get_media_info               ssd_get_media_info
-#define sd_get_media_info_ext           ssd_get_media_info_ext
-#define sd_dkio_ctrl_info               ssd_dkio_ctrl_info
-#define sd_nvpair_str_decode            ssd_nvpair_str_decode
-#define sd_strtok_r                     ssd_strtok_r
-#define sd_set_properties               ssd_set_properties
-#define sd_get_tunables_from_conf       ssd_get_tunables_from_conf
-#define sd_setup_next_xfer              ssd_setup_next_xfer
-#define sd_dkio_get_temp                ssd_dkio_get_temp
-#define sd_check_mhd                    ssd_check_mhd
-#define sd_mhd_watch_cb                 ssd_mhd_watch_cb
-#define sd_mhd_watch_incomplete         ssd_mhd_watch_incomplete
-#define sd_sname                        ssd_sname
-#define sd_mhd_resvd_recover            ssd_mhd_resvd_recover
-#define sd_resv_reclaim_thread          ssd_resv_reclaim_thread
-#define sd_take_ownership               ssd_take_ownership
-#define sd_reserve_release              ssd_reserve_release
-#define sd_rmv_resv_reclaim_req         ssd_rmv_resv_reclaim_req
-#define sd_mhd_reset_notify_cb          ssd_mhd_reset_notify_cb
-#define sd_persistent_reservation_in_read_keys  \
-                                        ssd_persistent_reservation_in_read_keys
-#define sd_persistent_reservation_in_read_resv  \
-                                        ssd_persistent_reservation_in_read_resv
-#define sd_mhdioc_takeown               ssd_mhdioc_takeown
-#define sd_mhdioc_failfast              ssd_mhdioc_failfast
-#define sd_mhdioc_release               ssd_mhdioc_release
-#define sd_mhdioc_register_devid        ssd_mhdioc_register_devid
-#define sd_mhdioc_inkeys                ssd_mhdioc_inkeys
-#define sd_mhdioc_inresv                ssd_mhdioc_inresv
-#define sr_change_blkmode               ssr_change_blkmode
-#define sr_change_speed                 ssr_change_speed
-#define sr_atapi_change_speed           ssr_atapi_change_speed
-#define sr_pause_resume                 ssr_pause_resume
-#define sr_play_msf                     ssr_play_msf
-#define sr_play_trkind                  ssr_play_trkind
-#define sr_read_all_subcodes            ssr_read_all_subcodes
-#define sr_read_subchannel              ssr_read_subchannel
-#define sr_read_tocentry                ssr_read_tocentry
-#define sr_read_tochdr                  ssr_read_tochdr
-#define sr_read_cdda                    ssr_read_cdda
-#define sr_read_cdxa                    ssr_read_cdxa
-#define sr_read_mode1                   ssr_read_mode1
-#define sr_read_mode2                   ssr_read_mode2
-#define sr_read_cd_mode2                ssr_read_cd_mode2
-#define sr_sector_mode                  ssr_sector_mode
-#define sr_eject                        ssr_eject
-#define sr_ejected                      ssr_ejected
-#define sr_check_wp                     ssr_check_wp
-#define sd_watch_request_submit         ssd_watch_request_submit
-#define sd_check_media                  ssd_check_media
-#define sd_media_watch_cb               ssd_media_watch_cb
-#define sd_delayed_cv_broadcast         ssd_delayed_cv_broadcast
-#define sr_volume_ctrl                  ssr_volume_ctrl
-#define sr_read_sony_session_offset     ssr_read_sony_session_offset
-#define sd_log_page_supported           ssd_log_page_supported
-#define sd_check_for_writable_cd        ssd_check_for_writable_cd
-#define sd_wm_cache_constructor         ssd_wm_cache_constructor
-#define sd_wm_cache_destructor          ssd_wm_cache_destructor
-#define sd_range_lock                   ssd_range_lock
-#define sd_get_range                    ssd_get_range
-#define sd_free_inlist_wmap             ssd_free_inlist_wmap
-#define sd_range_unlock                 ssd_range_unlock
-#define sd_read_modify_write_task       ssd_read_modify_write_task
-#define sddump_do_read_of_rmw           ssddump_do_read_of_rmw
-
-#define sd_iostart_chain                ssd_iostart_chain
-#define sd_iodone_chain                 ssd_iodone_chain
-#define sd_initpkt_map                  ssd_initpkt_map
-#define sd_destroypkt_map               ssd_destroypkt_map
-#define sd_chain_type_map               ssd_chain_type_map
-#define sd_chain_index_map              ssd_chain_index_map
-
-#define sd_failfast_flushctl            ssd_failfast_flushctl
-#define sd_failfast_flushq              ssd_failfast_flushq
-#define sd_failfast_flushq_callback     ssd_failfast_flushq_callback
-
-#define sd_is_lsi                       ssd_is_lsi
-#define sd_tg_rdwr                      ssd_tg_rdwr
-#define sd_tg_getinfo                   ssd_tg_getinfo
-#define sd_rmw_msg_print_handler        ssd_rmw_msg_print_handler
-
-#endif  /* #if (defined(__fibre)) */
-
-
 int _init(void);
 int _fini(void);
 int _info(struct modinfo *modinfop);
 
 /*PRINTFLIKE3*/

@@ -1199,11 +772,11 @@
 
 /*
  * Using sd_ssc_assessment to set correct type-of-assessment
  * Using sd_ssc_post to post ereport & system log
  *       sd_ssc_post will call sd_ssc_print to print system log
- *       sd_ssc_post will call sd_ssd_ereport_post to post ereport
+ *       sd_ssc_post will call sd_ssc_ereport_post to post ereport
  */
 static void sd_ssc_assessment(sd_ssc_t *ssc,
     enum sd_type_assessment tp_assess);
 
 static void sd_ssc_post(sd_ssc_t *ssc, enum sd_driver_assessment sd_assess);

@@ -1251,21 +824,23 @@
 static int  sd_get_devid(sd_ssc_t *ssc);
 static ddi_devid_t sd_create_devid(sd_ssc_t *ssc);
 static int  sd_write_deviceid(sd_ssc_t *ssc);
 static int  sd_check_vpd_page_support(sd_ssc_t *ssc);
 
+#ifdef notyet
 static void sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi);
 static void sd_create_pm_components(dev_info_t *devi, struct sd_lun *un);
+#endif
 
 static int  sd_ddi_suspend(dev_info_t *devi);
 static int  sd_ddi_resume(dev_info_t *devi);
 static int  sd_pm_state_change(struct sd_lun *un, int level, int flag);
 static int  sdpower(dev_info_t *devi, int component, int level);
 
 static int  sdattach(dev_info_t *devi, ddi_attach_cmd_t cmd);
 static int  sddetach(dev_info_t *devi, ddi_detach_cmd_t cmd);
-static int  sd_unit_attach(dev_info_t *devi);
+static void sd_unit_attach(void *arg);
 static int  sd_unit_detach(dev_info_t *devi);
 
 static void sd_set_unit_attributes(struct sd_lun *un, dev_info_t *devi);
 static void sd_create_errstats(struct sd_lun *un, int instance);
 static void sd_set_errstats(struct sd_lun *un);

@@ -1274,22 +849,13 @@
 static int  sddump(dev_t dev, caddr_t addr, daddr_t blkno, int nblk);
 static int  sd_scsi_poll(struct sd_lun *un, struct scsi_pkt *pkt);
 static int  sd_send_polled_RQS(struct sd_lun *un);
 static int  sd_ddi_scsi_poll(struct scsi_pkt *pkt);
 
-#if (defined(__fibre))
 /*
- * Event callbacks (photon)
- */
-static void sd_init_event_callbacks(struct sd_lun *un);
-static void  sd_event_callback(dev_info_t *, ddi_eventcookie_t, void *, void *);
-#endif
-
-/*
  * Defines for sd_cache_control
  */
-
 #define SD_CACHE_ENABLE         1
 #define SD_CACHE_DISABLE        0
 #define SD_CACHE_NOCHANGE       -1
 
 static int   sd_cache_control(sd_ssc_t *ssc, int rcd_flag, int wce_flag);

@@ -1408,11 +974,11 @@
 
 static void sd_set_retry_bp(struct sd_lun *un, struct buf *bp,
         clock_t retry_delay, void (*statp)(kstat_io_t *));
 
 static void sd_send_request_sense_command(struct sd_lun *un, struct buf *bp,
-        struct scsi_pkt *pktp);
+        int retry_check_flag, struct scsi_pkt *pktp);
 static void sd_start_retry_command(void *arg);
 static void sd_start_direct_priority_command(void *arg);
 static void sd_return_failed_command(struct sd_lun *un, struct buf *bp,
         int errcode);
 static void sd_return_failed_command_no_restart(struct sd_lun *un,

@@ -1531,10 +1097,12 @@
 static int sd_send_scsi_PERSISTENT_RESERVE_OUT(sd_ssc_t *ssc,
         uchar_t usr_cmd, uchar_t *usr_bufp);
 static int sd_send_scsi_SYNCHRONIZE_CACHE(struct sd_lun *un,
         struct dk_callback *dkc);
 static int sd_send_scsi_SYNCHRONIZE_CACHE_biodone(struct buf *bp);
+static int sd_send_scsi_UNMAP(dev_t dev, sd_ssc_t *ssc, dkioc_free_list_t *dfl,
+        int flag);
 static int sd_send_scsi_GET_CONFIGURATION(sd_ssc_t *ssc,
         struct uscsi_cmd *ucmdbuf, uchar_t *rqbuf, uint_t rqbuflen,
         uchar_t *bufaddr, uint_t buflen, int path_flag);
 static int sd_send_scsi_feature_GET_CONFIGURATION(sd_ssc_t *ssc,
         struct uscsi_cmd *ucmdbuf, uchar_t *rqbuf, uint_t rqbuflen,

@@ -1625,11 +1193,13 @@
 static int sd_media_watch_cb(caddr_t arg, struct scsi_watch_result *resultp);
 static void sd_delayed_cv_broadcast(void *arg);
 static int sr_volume_ctrl(dev_t dev, caddr_t data, int flag);
 static int sr_read_sony_session_offset(dev_t dev, caddr_t data, int flag);
 
+#ifdef notyet
 static int sd_log_page_supported(sd_ssc_t *ssc, int log_page);
+#endif
 
 /*
  * Function Prototype for the non-512 support (DVDRAM, MO etc.) functions.
  */
 static void sd_check_for_writable_cd(sd_ssc_t *ssc, int path_flag);

@@ -1648,11 +1218,11 @@
 
 
 /*
  * Function prototypes for failfast support.
  */
-static void sd_failfast_flushq(struct sd_lun *un);
+static void sd_failfast_flushq(struct sd_lun *un, boolean_t flush_all);
 static int sd_failfast_flushq_callback(struct buf *bp);
 
 /*
  * Function prototypes to check for lsi devices
  */

@@ -1688,10 +1258,42 @@
 
 #define SD_FAILFAST_INACTIVE            0
 #define SD_FAILFAST_ACTIVE              1
 
 /*
+ * Bitmask to control behaviour in failfast active state:
+ *
+ * SD_FAILFAST_ENABLE_FORCE_INACTIVE: When set, allow retries without
+ * SD_RETRIES_FAILFAST to cause transition to failfast inactive state.
+ *
+ * SD_FAILFAST_ENABLE_FAIL_RETRIES: When set, cause retries with the flag
+ * SD_RETRIES_FAILFAST set (following a timeout) to fail when in failfast
+ * active state.
+ *
+ * SD_FAILFAST_ENABLE_FAIL_ALL_RETRIES: When set, cause ALL retries,
+ * regardless of reason, to fail when in failfast active state. This takes
+ * precedence over SD_FAILFAST_FAIL_RETRIES.
+ *
+ * SD_FAILFAST_ENABLE_FAIL_USCSI: When set, discard all commands in the USCSI
+ * chain (sdioctl or driver generated) when in failfast active state.
+ * To prevent problems with sdopen, this is limited to when there are
+ * multiple pending commands.
+ */
+
+#define SD_FAILFAST_ENABLE_FORCE_INACTIVE       0x01
+#define SD_FAILFAST_ENABLE_FAIL_RETRIES         0x02
+#define SD_FAILFAST_ENABLE_FAIL_ALL_RETRIES     0x04
+#define SD_FAILFAST_ENABLE_FAIL_USCSI           0x08
+
+/*
+ * The default behaviour is to fail all retries due to timeout when in failfast
+ * active state, and not allow other retries to transition to inactive.
+ */
+static int sd_failfast_enable = SD_FAILFAST_ENABLE_FAIL_RETRIES |
+    SD_FAILFAST_ENABLE_FAIL_USCSI;
+
+/*
  * Bitmask to control behavior of buf(9S) flushes when a transition to
  * the failfast state occurs. Optional bits include:
  *
  * SD_FAILFAST_FLUSH_ALL_BUFS: When set, flush ALL bufs including those that
  * do NOT have B_FAILFAST set. When clear, only bufs with B_FAILFAST will

@@ -1703,22 +1305,26 @@
  */
 #define SD_FAILFAST_FLUSH_ALL_BUFS      0x01
 #define SD_FAILFAST_FLUSH_ALL_QUEUES    0x02
 
 /*
- * The default behavior is to only flush bufs that have B_FAILFAST set, but
- * to flush all queues within the driver.
+ * The default behavior is to flush all bufs in all queues within the driver.
  */
-static int sd_failfast_flushctl = SD_FAILFAST_FLUSH_ALL_QUEUES;
+static int sd_failfast_flushctl =
+    SD_FAILFAST_FLUSH_ALL_BUFS | SD_FAILFAST_FLUSH_ALL_QUEUES;
 
+#ifdef SD_FAULT_INJECTION
+static uint_t   sd_fault_injection_on = 0;
+#endif
 
 /*
  * SD Testing Fault Injection
  */
 #ifdef SD_FAULT_INJECTION
-static void sd_faultinjection_ioctl(int cmd, intptr_t arg, struct sd_lun *un);
+static int sd_faultinjection_ioctl(int cmd, intptr_t arg, struct sd_lun *un);
 static void sd_faultinjection(struct scsi_pkt *pktp);
+static void sd_prefaultinjection(struct scsi_pkt *pktp);
 static void sd_injection_log(char *buf, struct sd_lun *un);
 #endif
 
 /*
  * Device driver ops vector

@@ -2370,14 +1976,10 @@
 #define MAX_INQUIRY_SIZE                0xF0
 
 /*
  * Macros used by functions to pass a given buf(9S) struct along to the
  * next function in the layering chain for further processing.
- *
- * In the following macros, passing more than three arguments to the called
- * routines causes the optimizer for the SPARC compiler to stop doing tail
- * call elimination which results in significant performance degradation.
  */
 #define SD_BEGIN_IOSTART(index, un, bp) \
         ((*(sd_iostart_chain[index]))(index, un, bp))
 
 #define SD_BEGIN_IODONE(index, un, bp)  \

@@ -2566,11 +2168,10 @@
                 va_end(ap);
                 scsi_log(dev, sd_label, CE_CONT, "%s", sd_log_buf);
                 mutex_exit(&sd_log_mutex);
         }
 #ifdef SD_FAULT_INJECTION
-        _NOTE(DATA_READABLE_WITHOUT_LOCK(sd_lun::sd_injection_mask));
         if (un->sd_injection_mask & comp) {
                 mutex_enter(&sd_log_mutex);
                 va_start(ap, fmt);
                 (void) vsprintf(sd_log_buf, fmt, ap);
                 va_end(ap);

@@ -2616,11 +2217,10 @@
                 va_end(ap);
                 scsi_log(dev, sd_label, CE_CONT, "%s", sd_log_buf);
                 mutex_exit(&sd_log_mutex);
         }
 #ifdef SD_FAULT_INJECTION
-        _NOTE(DATA_READABLE_WITHOUT_LOCK(sd_lun::sd_injection_mask));
         if (un->sd_injection_mask & component) {
                 mutex_enter(&sd_log_mutex);
                 va_start(ap, fmt);
                 (void) vsprintf(sd_log_buf, fmt, ap);
                 va_end(ap);

@@ -2666,11 +2266,10 @@
                 va_end(ap);
                 scsi_log(dev, sd_label, CE_CONT, "%s", sd_log_buf);
                 mutex_exit(&sd_log_mutex);
         }
 #ifdef SD_FAULT_INJECTION
-        _NOTE(DATA_READABLE_WITHOUT_LOCK(sd_lun::sd_injection_mask));
         if (un->sd_injection_mask & component) {
                 mutex_enter(&sd_log_mutex);
                 va_start(ap, fmt);
                 (void) vsprintf(sd_log_buf, fmt, ap);
                 va_end(ap);

@@ -2699,14 +2298,10 @@
 {
         struct scsi_device      *devp;
         int                     rval;
         int                     instance = ddi_get_instance(devi);
 
-        /*
-         * if it wasn't for pln, sdprobe could actually be nulldev
-         * in the "__fibre" case.
-         */
         if (ddi_dev_is_sid(devi) == DDI_SUCCESS) {
                 return (DDI_PROBE_DONTCARE);
         }
 
         devp = ddi_get_driver_private(devi);

@@ -2729,40 +2324,20 @@
                 switch (devp->sd_inq->inq_dtype) {
                 case DTYPE_DIRECT:
                         rval = DDI_PROBE_SUCCESS;
                         break;
                 case DTYPE_RODIRECT:
-                        /* CDs etc. Can be removable media */
+                        /* CDs etc. Can be removable media. */
                         rval = DDI_PROBE_SUCCESS;
                         break;
                 case DTYPE_OPTICAL:
                         /*
-                         * Rewritable optical driver HP115AA
-                         * Can also be removable media
+                         * Rewritable optical driver HP115AA.
+                         * Can also be removable media.
                          */
-
-                        /*
-                         * Do not attempt to bind to  DTYPE_OPTICAL if
-                         * pre solaris 9 sparc sd behavior is required
-                         *
-                         * If first time through and sd_dtype_optical_bind
-                         * has not been set in /etc/system check properties
-                         */
-
-                        if (sd_dtype_optical_bind  < 0) {
-                                sd_dtype_optical_bind = ddi_prop_get_int
-                                    (DDI_DEV_T_ANY, devi, 0,
-                                    "optical-device-bind", 1);
-                        }
-
-                        if (sd_dtype_optical_bind == 0) {
-                                rval = DDI_PROBE_FAILURE;
-                        } else {
                                 rval = DDI_PROBE_SUCCESS;
-                        }
                         break;
-
                 case DTYPE_NOTPRESENT:
                 default:
                         rval = DDI_PROBE_FAILURE;
                         break;
                 }

@@ -2858,16 +2433,29 @@
     char *name, caddr_t valuep, int *lengthp)
 {
         struct sd_lun   *un;
 
         if ((un = ddi_get_soft_state(sd_state, ddi_get_instance(dip))) == NULL)
-                return (ddi_prop_op(dev, dip, prop_op, mod_flags,
-                    name, valuep, lengthp));
+                goto fallback;
 
+        mutex_enter(SD_MUTEX(un));
+        while ((un->un_state == SD_STATE_ATTACHING))
+                cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
+
+        if (un->un_state == SD_STATE_ATTACH_FAILED) {
+                mutex_exit(SD_MUTEX(un));
+                goto fallback;
+        }
+        mutex_exit(SD_MUTEX(un));
+
         return (cmlb_prop_op(un->un_cmlbhandle,
             dev, dip, prop_op, mod_flags, name, valuep, lengthp,
             SDPART(dev), (void *)SD_PATH_DIRECT));
+
+fallback:
+        return (ddi_prop_op(dev, dip, prop_op, mod_flags, name, valuep,
+            lengthp));
 }
 
 /*
  * The following functions are for smart probing:
  * sd_scsi_probe_cache_init()

@@ -3211,13 +2799,17 @@
          * this stage, use START STOP bit.
          */
         status = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP,
             SD_TARGET_START, SD_PATH_DIRECT);
 
-        if (status != 0) {
-                if (status == EACCES)
+        switch (status) {
+        case EIO:
+                sd_ssc_assessment(ssc, SD_FMT_STATUS_CHECK);
+                return (status);
+        case EACCES:
                         has_conflict = TRUE;
+        default: /*FALLTHROUGH*/
                 sd_ssc_assessment(ssc, SD_FMT_IGNORE);
         }
 
         /*
          * Send another INQUIRY command to the target. This is necessary for

@@ -3832,11 +3424,11 @@
 
         ASSERT(un != NULL);
 
         /* Obtain the configuration list associated with the .conf file */
         if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, SD_DEVINFO(un),
-            DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, sd_config_list,
+            DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "sd-config-list",
             &config_list, &nelements) != DDI_PROP_SUCCESS) {
                 return (SD_FAILURE);
         }
 
         /*

@@ -4196,17 +3788,42 @@
         if (strcasecmp(name, "physical-block-size") == 0) {
                 if (ddi_strtol(value, &endptr, 0, &val) == 0 &&
                     ISP2(val) && val >= un->un_tgt_blocksize &&
                     val >= un->un_sys_blocksize) {
                         un->un_phy_blocksize = val;
+                        un->un_f_sdconf_phy_blocksize = TRUE;
                 } else {
                         goto value_invalid;
                 }
                 SD_INFO(SD_LOG_ATTACH_DETACH, un, "sd_set_properties: "
                     "physical block size set to %d\n", un->un_phy_blocksize);
         }
 
+        if (strcasecmp(name, "slow-io-threshold") == 0) {
+                if (ddi_strtol(value, &endptr, 0, &val) == 0) {
+                        un->un_slow_io_threshold = (hrtime_t)val * NANOSEC;
+                } else {
+                        un->un_slow_io_threshold =
+                            (hrtime_t)sd_slow_io_threshold;
+                        goto value_invalid;
+                }
+                SD_INFO(SD_LOG_ATTACH_DETACH, un, "sd_set_properties: "
+                    "slow IO threshold set to %llu\n",
+                    un->un_slow_io_threshold);
+        }
+
+        if (strcasecmp(name, "io-time") == 0) {
+                if (ddi_strtol(value, &endptr, 0, &val) == 0) {
+                        un->un_io_time = val;
+                } else {
+                        un->un_io_time = sd_io_time;
+                        goto value_invalid;
+                }
+                SD_INFO(SD_LOG_ATTACH_DETACH, un, "sd_set_properties: "
+                    "IO time set to %llu\n", un->un_io_time);
+        }
+
         if (strcasecmp(name, "retries-victim") == 0) {
                 if (ddi_strtol(value, &endptr, 0, &val) == 0) {
                         un->un_victim_retry_count = val;
                 } else {
                         goto value_invalid;

@@ -4964,11 +4581,10 @@
          *
          * These pages are also reserved in SBC-2 and later.  We assume SBC-2
          * or later for a direct-attached block device if the SCSI version is
          * at least SPC-3.
          */
-
         if (ISCD(un) ||
             un->un_interconnect_type == SD_INTERCONNECT_SATA ||
             (un->un_ctype == CTYPE_CCS && SD_INQUIRY(un)->inq_ansi >= 5))
                 return (ret);
 

@@ -5306,12 +4922,94 @@
                                 stp->sd_capacity.value.ui64 = capacity;
                 }
         }
 }
 
+/*
+ * Parses the SCSI Block Limits VPD page (0xB0). It's legal to pass NULL for
+ * vpd_pg, in which case all the block limits will be reset to the defaults.
+ */
+static void
+sd_parse_blk_limits_vpd(struct sd_lun *un, uchar_t *vpd_pg)
+{
+        sd_blk_limits_t *lim = &un->un_blk_lim;
+        unsigned pg_len;
 
+        if (vpd_pg != NULL)
+                pg_len = BE_IN16(&vpd_pg[2]);
+        else
+                pg_len = 0;
+
+        /* Block Limits VPD can be 16 bytes or 64 bytes long - support both */
+        if (pg_len >= 0x10) {
+                lim->lim_opt_xfer_len_gran = BE_IN16(&vpd_pg[6]);
+                lim->lim_max_xfer_len = BE_IN32(&vpd_pg[8]);
+                lim->lim_opt_xfer_len = BE_IN32(&vpd_pg[12]);
+        } else {
+                lim->lim_opt_xfer_len_gran = 0;
+                lim->lim_max_xfer_len = UINT32_MAX;
+                lim->lim_opt_xfer_len = UINT32_MAX;
+        }
+        if (pg_len >= 0x3c) {
+                lim->lim_max_pfetch_len = BE_IN32(&vpd_pg[16]);
+                /*
+                 * A zero in either of the following two fields indicates lack
+                 * of UNMAP support.
+                 */
+                lim->lim_max_unmap_lba_cnt = BE_IN32(&vpd_pg[20]);
+                lim->lim_max_unmap_descr_cnt = BE_IN32(&vpd_pg[24]);
+                lim->lim_opt_unmap_gran = BE_IN32(&vpd_pg[28]);
+                if ((vpd_pg[32] >> 7) == 1) {
+                        /* left-most bit on each byte is a flag */
+                        lim->lim_unmap_gran_align =
+                            ((vpd_pg[32] & 0x7f) << 24) | (vpd_pg[33] << 16) |
+                            (vpd_pg[34] << 8) | vpd_pg[35];
+                } else {
+                        lim->lim_unmap_gran_align = 0;
+                }
+                lim->lim_max_write_same_len = BE_IN64(&vpd_pg[36]);
+        } else {
+                lim->lim_max_pfetch_len = UINT32_MAX;
+                lim->lim_max_unmap_lba_cnt = UINT32_MAX;
+                lim->lim_max_unmap_descr_cnt = SD_UNMAP_MAX_DESCR;
+                lim->lim_opt_unmap_gran = 0;
+                lim->lim_unmap_gran_align = 0;
+                lim->lim_max_write_same_len = UINT64_MAX;
+        }
+}
+
 /*
+ * Collects VPD page B0 data if available (block limits). If the data is
+ * not available or querying the device failed, we revert to the defaults.
+ */
+static void
+sd_setup_blk_limits(sd_ssc_t *ssc)
+{
+        struct sd_lun   *un             = ssc->ssc_un;
+        uchar_t         *inqB0          = NULL;
+        size_t          inqB0_resid     = 0;
+        int             rval;
+
+        if (un->un_vpd_page_mask & SD_VPD_BLK_LIMITS_PG) {
+                inqB0 = kmem_zalloc(MAX_INQUIRY_SIZE, KM_SLEEP);
+                rval = sd_send_scsi_INQUIRY(ssc, inqB0, MAX_INQUIRY_SIZE, 0x01,
+                    0xB0, &inqB0_resid);
+                if (rval != 0) {
+                        sd_ssc_assessment(ssc, SD_FMT_IGNORE);
+                        kmem_free(inqB0, MAX_INQUIRY_SIZE);
+                        inqB0 = NULL;
+                }
+        }
+        /* passing NULL inqB0 will reset to defaults */
+        sd_parse_blk_limits_vpd(ssc->ssc_un, inqB0);
+        if (inqB0)
+                kmem_free(inqB0, MAX_INQUIRY_SIZE);
+}
+
+#define DEVID_IF_KNOWN(d) "devid", DATA_TYPE_STRING, (d) ? (d) : "unknown"
+
+/*
  *    Function: sd_register_devid
  *
  * Description: This routine will obtain the device id information from the
  *              target, obtain the serial number, and register the device
  *              id with the ddi framework.

@@ -5438,24 +5136,18 @@
          * FAB_DEVID property set in the disk_table.  These drives
          * manage the devid's by storing them in last 2 available sectors
          * on the drive and have them fabricated by the ddi layer by calling
          * ddi_devid_init and passing the DEVID_FAB flag.
          */
-        if (un->un_f_opt_fab_devid == TRUE) {
+        if (un->un_f_opt_fab_devid == TRUE &&
+            reservation_flag != SD_TARGET_IS_RESERVED) {
+                if (sd_get_devid(ssc) == EINVAL)
                 /*
-                 * Depending on EINVAL isn't reliable, since a reserved disk
-                 * may result in invalid geometry, so check to make sure a
-                 * reservation conflict did not occur during attach.
-                 */
-                if ((sd_get_devid(ssc) == EINVAL) &&
-                    (reservation_flag != SD_TARGET_IS_RESERVED)) {
-                        /*
                          * The devid is invalid AND there is no reservation
                          * conflict.  Fabricate a new devid.
                          */
                         (void) sd_create_devid(ssc);
-                }
 
                 /* Register the devid if it exists */
                 if (un->un_devid != NULL) {
                         (void) ddi_devid_register(SD_DEVINFO(un),
                             un->un_devid);

@@ -5473,11 +5165,11 @@
             inq83_resid, &un->un_devid) == DDI_SUCCESS) {
 
                 /* devid successfully encoded, register devid */
                 (void) ddi_devid_register(SD_DEVINFO(un), un->un_devid);
 
-        } else {
+        } else if (reservation_flag != SD_TARGET_IS_RESERVED) {
                 /*
                  * Unable to encode a devid based on data available.
                  * This is not a Sun qualified disk.  Older Sun disk
                  * drives that have the SD_FAB_DEVID property
                  * set in the disk_table and non Sun qualified

@@ -5835,10 +5527,13 @@
                                 un->un_vpd_page_mask |= SD_VPD_DEVID_WWN_PG;
                                 break;
                         case 0x86:
                                 un->un_vpd_page_mask |= SD_VPD_EXTENDED_DATA_PG;
                                 break;
+                        case 0xB0:
+                                un->un_vpd_page_mask |= SD_VPD_BLK_LIMITS_PG;
+                                break;
                         case 0xB1:
                                 un->un_vpd_page_mask |= SD_VPD_DEV_CHARACTER_PG;
                                 break;
                         }
                         counter++;

@@ -5863,11 +5558,11 @@
  *
  * Description: Initialize Power Management on the device
  *
  *     Context: Kernel Thread
  */
-
+#ifdef notyet
 static void
 sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi)
 {
         uint_t          log_page_size;
         uchar_t         *log_page_data;

@@ -5918,10 +5613,11 @@
                             SD_PATH_DIRECT);
                         if (rval != 0) {
                                 un->un_f_power_condition_supported = FALSE;
                         }
                 }
+                /* WTF? this fails for optical drives with no media */
                 if (!un->un_f_power_condition_supported) {
                         rval = sd_send_scsi_START_STOP_UNIT(ssc,
                             SD_START_STOP, SD_TARGET_START, SD_PATH_DIRECT);
                 }
                 if (rval != 0) {

@@ -6119,12 +5815,12 @@
                 un->un_pm_count = -1;
         }
         mutex_exit(&un->un_pm_mutex);
         mutex_exit(SD_MUTEX(un));
 }
+#endif
 
-
 /*
  *    Function: sd_ddi_suspend
  *
  * Description: Performs system power-down operations. This includes
  *              setting the drive state to indicate its suspended so

@@ -6293,31 +5989,12 @@
                 mutex_exit(SD_MUTEX(un));
                 (void) untimeout(temp_id);
                 mutex_enter(SD_MUTEX(un));
         }
 
-        if (un->un_f_is_fibre == TRUE) {
-                /*
-                 * Remove callbacks for insert and remove events
-                 */
-                if (un->un_insert_event != NULL) {
                         mutex_exit(SD_MUTEX(un));
-                        (void) ddi_remove_event_handler(un->un_insert_cb_id);
-                        mutex_enter(SD_MUTEX(un));
-                        un->un_insert_event = NULL;
-                }
 
-                if (un->un_remove_event != NULL) {
-                        mutex_exit(SD_MUTEX(un));
-                        (void) ddi_remove_event_handler(un->un_remove_cb_id);
-                        mutex_enter(SD_MUTEX(un));
-                        un->un_remove_event = NULL;
-                }
-        }
-
-        mutex_exit(SD_MUTEX(un));
-
         SD_TRACE(SD_LOG_IO_PM, un, "sd_ddi_suspend: exit\n");
 
         return (DDI_SUCCESS);
 }
 

@@ -6393,22 +6070,11 @@
         /* restart thread */
         if (SD_OK_TO_RESUME_SCSI_WATCHER(un)) {
                 scsi_watch_resume(un->un_swr_token);
         }
 
-#if (defined(__fibre))
-        if (un->un_f_is_fibre == TRUE) {
                 /*
-                 * Add callbacks for insert and remove events
-                 */
-                if (strcmp(un->un_node_type, DDI_NT_BLOCK_CHAN)) {
-                        sd_init_event_callbacks(un);
-                }
-        }
-#endif
-
-        /*
          * Transport any pending commands to the target.
          *
          * If this is a low-activity device commands in queue will have to wait
          * until new commands come in, which may take awhile. Also, we
          * specifically don't check un_ncmds_in_transport because we know that

@@ -7090,119 +6756,29 @@
  */
 
 static int
 sdattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 {
-        switch (cmd) {
-        case DDI_ATTACH:
-                return (sd_unit_attach(devi));
-        case DDI_RESUME:
-                return (sd_ddi_resume(devi));
-        default:
-                break;
-        }
-        return (DDI_FAILURE);
-}
-
-
-/*
- *    Function: sddetach
- *
- * Description: Driver's detach(9E) entry point function.
- *
- *   Arguments: devi - opaque device info handle
- *              cmd  - detach  type
- *
- * Return Code: DDI_SUCCESS
- *              DDI_FAILURE
- *
- *     Context: Kernel thread context
- */
-
-static int
-sddetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
-{
-        switch (cmd) {
-        case DDI_DETACH:
-                return (sd_unit_detach(devi));
-        case DDI_SUSPEND:
-                return (sd_ddi_suspend(devi));
-        default:
-                break;
-        }
-        return (DDI_FAILURE);
-}
-
-
-/*
- *     Function: sd_sync_with_callback
- *
- *  Description: Prevents sd_unit_attach or sd_unit_detach from freeing the soft
- *               state while the callback routine is active.
- *
- *    Arguments: un: softstate structure for the instance
- *
- *      Context: Kernel thread context
- */
-
-static void
-sd_sync_with_callback(struct sd_lun *un)
-{
-        ASSERT(un != NULL);
-
-        mutex_enter(SD_MUTEX(un));
-
-        ASSERT(un->un_in_callback >= 0);
-
-        while (un->un_in_callback > 0) {
-                mutex_exit(SD_MUTEX(un));
-                delay(2);
-                mutex_enter(SD_MUTEX(un));
-        }
-
-        mutex_exit(SD_MUTEX(un));
-}
-
-/*
- *    Function: sd_unit_attach
- *
- * Description: Performs DDI_ATTACH processing for sdattach(). Allocates
- *              the soft state structure for the device and performs
- *              all necessary structure and device initializations.
- *
- *   Arguments: devi: the system's dev_info_t for the device.
- *
- * Return Code: DDI_SUCCESS if attach is successful.
- *              DDI_FAILURE if any part of the attach fails.
- *
- *     Context: Called at attach(9e) time for the DDI_ATTACH flag.
- *              Kernel thread context only.  Can sleep.
- */
-
-static int
-sd_unit_attach(dev_info_t *devi)
-{
         struct  scsi_device     *devp;
         struct  sd_lun          *un;
         char                    *variantp;
-        char                    name_str[48];
-        int     reservation_flag = SD_TARGET_IS_UNRESERVED;
         int     instance;
-        int     rval;
-        int     wc_enabled;
-        int     wc_changeable;
         int     tgt;
-        uint64_t        capacity;
-        uint_t          lbasize = 0;
         dev_info_t      *pdip = ddi_get_parent(devi);
-        int             offbyone = 0;
-        int             geom_label_valid = 0;
+        int             max_xfer_size;
         sd_ssc_t        *ssc;
-        int             status;
         struct sd_fm_internal   *sfip = NULL;
-        int             max_xfer_size;
 
+        switch (cmd) {
+        case DDI_ATTACH:
+                break;
+        case DDI_RESUME:
+                return (sd_ddi_resume(devi));
+        default:
+                return (DDI_FAILURE);
+        }
+
         /*
          * Retrieve the target driver's private data area. This was set
          * up by the HBA.
          */
         devp = ddi_get_driver_private(devi);

@@ -7293,11 +6869,11 @@
          * was successful, unless something has gone horribly wrong and the
          * ddi's soft state internals are corrupt (in which case it is
          * probably better to halt here than just fail the attach....)
          */
         if ((un = ddi_get_soft_state(sd_state, instance)) == NULL) {
-                panic("sd_unit_attach: NULL soft state on instance:0x%x",
+                panic("sdattach: NULL soft state on instance:0x%x",
                     instance);
                 /*NOTREACHED*/
         }
 
         /*

@@ -7339,96 +6915,55 @@
                 un->un_node_type = DDI_NT_BLOCK_CHAN;
                 un->un_ctype     = CTYPE_CCS;
                 break;
         }
 
-        /*
-         * Try to read the interconnect type from the HBA.
-         *
-         * Note: This driver is currently compiled as two binaries, a parallel
-         * scsi version (sd) and a fibre channel version (ssd). All functional
-         * differences are determined at compile time. In the future a single
-         * binary will be provided and the interconnect type will be used to
-         * differentiate between fibre and parallel scsi behaviors. At that time
-         * it will be necessary for all fibre channel HBAs to support this
-         * property.
-         *
-         * set un_f_is_fiber to TRUE ( default fiber )
-         */
-        un->un_f_is_fibre = TRUE;
+        /* Try to read the interconnect type from the HBA */
+        un->un_f_is_fibre = FALSE;
         switch (scsi_ifgetcap(SD_ADDRESS(un), "interconnect-type", -1)) {
         case INTERCONNECT_SSA:
+                un->un_f_is_fibre = TRUE;
                 un->un_interconnect_type = SD_INTERCONNECT_SSA;
                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                    "sd_unit_attach: un:0x%p SD_INTERCONNECT_SSA\n", un);
+                    "sdattach: un:0x%p SD_INTERCONNECT_SSA\n", un);
                 break;
         case INTERCONNECT_PARALLEL:
-                un->un_f_is_fibre = FALSE;
                 un->un_interconnect_type = SD_INTERCONNECT_PARALLEL;
                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                    "sd_unit_attach: un:0x%p SD_INTERCONNECT_PARALLEL\n", un);
+                    "sdattach: un:0x%p SD_INTERCONNECT_PARALLEL\n", un);
                 break;
         case INTERCONNECT_SAS:
-                un->un_f_is_fibre = FALSE;
                 un->un_interconnect_type = SD_INTERCONNECT_SAS;
                 un->un_node_type = DDI_NT_BLOCK_SAS;
                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                    "sd_unit_attach: un:0x%p SD_INTERCONNECT_SAS\n", un);
+                    "sdattach: un:0x%p SD_INTERCONNECT_SAS\n", un);
                 break;
         case INTERCONNECT_SATA:
-                un->un_f_is_fibre = FALSE;
                 un->un_interconnect_type = SD_INTERCONNECT_SATA;
                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                    "sd_unit_attach: un:0x%p SD_INTERCONNECT_SATA\n", un);
+                    "sdattach: un:0x%p SD_INTERCONNECT_SATA\n", un);
                 break;
         case INTERCONNECT_FIBRE:
+                un->un_f_is_fibre = TRUE;
                 un->un_interconnect_type = SD_INTERCONNECT_FIBRE;
                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                    "sd_unit_attach: un:0x%p SD_INTERCONNECT_FIBRE\n", un);
+                    "sdattach: un:0x%p SD_INTERCONNECT_FIBRE\n", un);
                 break;
         case INTERCONNECT_FABRIC:
+                un->un_f_is_fibre = TRUE;
                 un->un_interconnect_type = SD_INTERCONNECT_FABRIC;
                 un->un_node_type = DDI_NT_BLOCK_FABRIC;
                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                    "sd_unit_attach: un:0x%p SD_INTERCONNECT_FABRIC\n", un);
+                    "sdattach: un:0x%p SD_INTERCONNECT_FABRIC\n", un);
                 break;
         default:
-#ifdef SD_DEFAULT_INTERCONNECT_TYPE
                 /*
-                 * The HBA does not support the "interconnect-type" property
-                 * (or did not provide a recognized type).
-                 *
-                 * Note: This will be obsoleted when a single fibre channel
-                 * and parallel scsi driver is delivered. In the meantime the
-                 * interconnect type will be set to the platform default.If that
-                 * type is not parallel SCSI, it means that we should be
-                 * assuming "ssd" semantics. However, here this also means that
-                 * the FC HBA is not supporting the "interconnect-type" property
-                 * like we expect it to, so log this occurrence.
+                 * The default is to assume that if a device does not support
+                 * the "interconnect-type" property it is a parallel SCSI HBA
+                 * and set the interconnect type for parallel SCSI.
                  */
-                un->un_interconnect_type = SD_DEFAULT_INTERCONNECT_TYPE;
-                if (!SD_IS_PARALLEL_SCSI(un)) {
-                        SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                            "sd_unit_attach: un:0x%p Assuming "
-                            "INTERCONNECT_FIBRE\n", un);
-                } else {
-                        SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                            "sd_unit_attach: un:0x%p Assuming "
-                            "INTERCONNECT_PARALLEL\n", un);
-                        un->un_f_is_fibre = FALSE;
-                }
-#else
-                /*
-                 * Note: This source will be implemented when a single fibre
-                 * channel and parallel scsi driver is delivered. The default
-                 * will be to assume that if a device does not support the
-                 * "interconnect-type" property it is a parallel SCSI HBA and
-                 * we will set the interconnect type for parallel scsi.
-                 */
                 un->un_interconnect_type = SD_INTERCONNECT_PARALLEL;
-                un->un_f_is_fibre = FALSE;
-#endif
                 break;
         }
 
         if (un->un_f_is_fibre == TRUE) {
                 if (scsi_ifgetcap(SD_ADDRESS(un), "scsi-version", 1) ==

@@ -7442,33 +6977,22 @@
                                 break;
                         }
                 }
         }
 
+        (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi,
+            "allow-unconstrained-retire", 1);
+
         /*
          * Initialize the Request Sense command for the target
          */
         if (sd_alloc_rqs(devp, un) != DDI_SUCCESS) {
                 goto alloc_rqs_failed;
         }
 
-        /*
-         * Set un_retry_count with SD_RETRY_COUNT, this is ok for Sparc
-         * with separate binary for sd and ssd.
-         *
-         * x86 has 1 binary, un_retry_count is set base on connection type.
-         * The hardcoded values will go away when Sparc uses 1 binary
-         * for sd and ssd.  This hardcoded values need to match
-         * SD_RETRY_COUNT in sddef.h
-         * The value used is base on interconnect type.
-         * fibre = 3, parallel = 5
-         */
-#if defined(__i386) || defined(__amd64)
+        /* The value used is base on interconnect type */
         un->un_retry_count = un->un_f_is_fibre ? 3 : 5;
-#else
-        un->un_retry_count = SD_RETRY_COUNT;
-#endif
 
         /*
          * Set the per disk retry count to the default number of retries
          * for disks and CDROMs. This value can be overridden by the
          * disk property list or an entry in sd.conf.

@@ -7491,32 +7015,37 @@
          * in sd.conf or the device config table.
          */
         un->un_reset_retry_count = (un->un_retry_count / 2);
 
         /*
-         * Set the victim_retry_count to the default un_retry_count
+         * Set the victim_retry_count to the default un_retry_count.
+         * This value is used in addition to the standard retry count.
+         * This can be overridden by entries in sd.conf or the device
+         * config table.
          */
-        un->un_victim_retry_count = (2 * un->un_retry_count);
+        un->un_victim_retry_count = un->un_retry_count;
 
         /*
          * Set the reservation release timeout to the default value of
-         * 5 seconds. This can be overridden by entries in ssd.conf or the
+         * 5 seconds. This can be overridden by entries in sd.conf or the
          * device config table.
          */
         un->un_reserve_release_time = 5;
 
+        un->un_io_time = sd_io_time;
+
+        un->un_slow_io_threshold = sd_slow_io_threshold;
+
+        un->un_f_lun_reset_enabled = sd_enable_lun_reset;
+
         /*
          * Set up the default maximum transfer size. Note that this may
          * get updated later in the attach, when setting up default wide
          * operations for disks.
          */
-#if defined(__i386) || defined(__amd64)
         un->un_max_xfer_size = (uint_t)SD_DEFAULT_MAX_XFER_SIZE;
         un->un_partial_dma_supported = 1;
-#else
-        un->un_max_xfer_size = (uint_t)maxphys;
-#endif
 
         /*
          * Get "allow bus device reset" property (defaults to "enabled" if
          * the property was not defined). This is to disable bus resets for
          * certain kinds of error recovery. Note: In the future when a run-time

@@ -7528,16 +7057,16 @@
         } else {
                 if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
                     "allow-bus-device-reset", 1) != 0) {
                         un->un_f_allow_bus_device_reset = TRUE;
                         SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                            "sd_unit_attach: un:0x%p Bus device reset "
+                            "sdattach: un:0x%p Bus device reset "
                             "enabled\n", un);
                 } else {
                         un->un_f_allow_bus_device_reset = FALSE;
                         SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                            "sd_unit_attach: un:0x%p Bus device reset "
+                            "sdattach: un:0x%p Bus device reset "
                             "disabled\n", un);
                 }
         }
 
         /*

@@ -7552,28 +7081,34 @@
          * new property.
          */
         if (ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, "atapi", -1) != -1) {
                 un->un_f_cfg_is_atapi = TRUE;
                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                    "sd_unit_attach: un:0x%p Atapi device\n", un);
+                    "sdattach: un:0x%p Atapi device\n", un);
         }
         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, 0, "variant",
             &variantp) == DDI_PROP_SUCCESS) {
                 if (strcmp(variantp, "atapi") == 0) {
                         un->un_f_cfg_is_atapi = TRUE;
                         SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                            "sd_unit_attach: un:0x%p Atapi device\n", un);
+                            "sdattach: un:0x%p Atapi device\n", un);
                 }
                 ddi_prop_free(variantp);
         }
 
-        un->un_cmd_timeout      = SD_IO_TIME;
-
+        un->un_cmd_timeout = ((ISCD(un)) ? 2 : 1) * (ushort_t)un->un_io_time;
+        un->un_uscsi_timeout = un->un_cmd_timeout;
         un->un_busy_timeout  = SD_BSY_TIMEOUT;
 
-        /* Info on current states, statuses, etc. (Updated frequently) */
-        un->un_state            = SD_STATE_NORMAL;
+        /*
+         * Info on current states, statuses, etc. (Updated frequently)
+         *
+         * Current state is ATTACHING until we finished sd_unit_attach.
+         * Last state is NORMAL so that sd_unit_attach can Restore_state()
+         * when it finishes successfully.
+         */
+        un->un_state            = SD_STATE_ATTACHING;
         un->un_last_state       = SD_STATE_NORMAL;
 
         /* Control & status info for command throttling */
         un->un_throttle         = sd_max_throttle;
         un->un_saved_throttle   = sd_max_throttle;

@@ -7583,10 +7118,14 @@
                 un->un_f_use_adaptive_throttle = TRUE;
         } else {
                 un->un_f_use_adaptive_throttle = FALSE;
         }
 
+        /* Unit detach has to pause until outstanding commands abort */
+        un->un_f_detach_waiting = 0;
+        cv_init(&un->un_detach_cv, NULL, CV_DRIVER, NULL);
+
         /* Removable media support. */
         cv_init(&un->un_state_cv, NULL, CV_DRIVER, NULL);
         un->un_mediastate               = DKIO_NONE;
         un->un_specified_mediastate     = DKIO_NONE;
 

@@ -7625,10 +7164,11 @@
          * physical sector size defaults to DEV_BSIZE currently. We can
          * override this value via the driver configuration file so we must
          * set it before calling sd_read_unit_properties().
          */
         un->un_phy_blocksize = DEV_BSIZE;
+        un->un_f_sdconf_phy_blocksize = FALSE;
 
         /*
          * Retrieve the properties from the static driver table or the driver
          * configuration file (.conf) for this unit and update the soft state
          * for the device as needed for the indicated properties.

@@ -7636,11 +7176,11 @@
          * following routines may have dependencies on soft state flags set
          * as part of the driver property configuration.
          */
         sd_read_unit_properties(un);
         SD_TRACE(SD_LOG_ATTACH_DETACH, un,
-            "sd_unit_attach: un:0x%p property configuration complete.\n", un);
+            "sdattach: un:0x%p property configuration complete.\n", un);
 
         /*
          * Only if a device has "hotpluggable" property, it is
          * treated as hotpluggable device. Otherwise, it is
          * regarded as non-hotpluggable one.

@@ -7722,18 +7262,51 @@
         if (un->un_stats != NULL) {
                 un->un_stats->ks_lock = SD_MUTEX(un);
                 kstat_install(un->un_stats);
         }
         SD_TRACE(SD_LOG_ATTACH_DETACH, un,
-            "sd_unit_attach: un:0x%p un_stats created\n", un);
+            "sdattach: un:0x%p un_stats created\n", un);
 
+        un->un_unmapstats_ks = kstat_create(sd_label, instance, "unmapstats",
+            "misc", KSTAT_TYPE_NAMED, sizeof (*un->un_unmapstats) /
+            sizeof (kstat_named_t), 0);
+        if (un->un_unmapstats_ks) {
+                un->un_unmapstats = un->un_unmapstats_ks->ks_data;
+
+                kstat_named_init(&un->un_unmapstats->us_cmds,
+                    "commands", KSTAT_DATA_UINT64);
+                kstat_named_init(&un->un_unmapstats->us_errs,
+                    "errors", KSTAT_DATA_UINT64);
+                kstat_named_init(&un->un_unmapstats->us_extents,
+                    "extents", KSTAT_DATA_UINT64);
+                kstat_named_init(&un->un_unmapstats->us_bytes,
+                    "bytes", KSTAT_DATA_UINT64);
+
+                kstat_install(un->un_unmapstats_ks);
+        } else {
+                cmn_err(CE_NOTE, "!Cannot create unmap kstats for disk %d",
+                    instance);
+        }
+
+        un->un_lat_ksp = kstat_create(sd_label, instance, "io_latency",
+            "io_latency", KSTAT_TYPE_RAW, sizeof (un_lat_stat_t),
+            KSTAT_FLAG_PERSISTENT);
+
+        if (un->un_lat_ksp != NULL) {
+                un->un_lat_ksp->ks_lock = SD_MUTEX(un);
+                un->un_lat_stats = (un_lat_stat_t *)un->un_lat_ksp->ks_data;
+                kstat_install(un->un_lat_ksp);
+        } else {
+                un->un_lat_stats = NULL;
+        }
+
         sd_create_errstats(un, instance);
         if (un->un_errstats == NULL) {
                 goto create_errstats_failed;
         }
         SD_TRACE(SD_LOG_ATTACH_DETACH, un,
-            "sd_unit_attach: un:0x%p errstats created\n", un);
+            "sdattach: un:0x%p errstats created\n", un);
 
         /*
          * The following if/else code was relocated here from below as part
          * of the fix for bug (4430280). However with the default setup added
          * on entry to this routine, it's no longer absolutely necessary for

@@ -7753,11 +7326,11 @@
                     (un->un_f_arq_enabled == TRUE)) {
                         if (scsi_ifsetcap(SD_ADDRESS(un), "tagged-qing",
                             1, 1) == 1) {
                                 un->un_tagflags = FLAG_STAG;
                                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                                    "sd_unit_attach: un:0x%p tag queueing "
+                                    "sdattach: un:0x%p tag queueing "
                                     "enabled\n", un);
                         } else if (scsi_ifgetcap(SD_ADDRESS(un),
                             "untagged-qing", 0) == 1) {
                                 un->un_f_opt_queueing = TRUE;
                                 un->un_saved_throttle = un->un_throttle =

@@ -7774,42 +7347,34 @@
                             min(un->un_throttle, 3);
                 } else {
                         un->un_f_opt_queueing = FALSE;
                         un->un_saved_throttle = un->un_throttle = 1;
                         SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                            "sd_unit_attach: un:0x%p no tag queueing\n", un);
+                            "sdattach: un:0x%p no tag queueing\n", un);
                 }
 
                 /*
                  * Enable large transfers for SATA/SAS drives
                  */
                 if (SD_IS_SERIAL(un)) {
                         un->un_max_xfer_size =
                             ddi_getprop(DDI_DEV_T_ANY, devi, 0,
-                            sd_max_xfer_size, SD_MAX_XFER_SIZE);
+                            "sd_max_xfer_size", SD_MAX_XFER_SIZE);
                         SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                            "sd_unit_attach: un:0x%p max transfer "
+                            "sdattach: un:0x%p max transfer "
                             "size=0x%x\n", un, un->un_max_xfer_size);
 
                 }
 
                 /* Setup or tear down default wide operations for disks */
-
-                /*
-                 * Note: Legacy: it may be possible for both "sd_max_xfer_size"
-                 * and "ssd_max_xfer_size" to exist simultaneously on the same
-                 * system and be set to different values. In the future this
-                 * code may need to be updated when the ssd module is
-                 * obsoleted and removed from the system. (4299588)
-                 */
                 if (SD_IS_PARALLEL_SCSI(un) &&
                     (devp->sd_inq->inq_rdf == RDF_SCSI2) &&
                     (devp->sd_inq->inq_wbus16 || devp->sd_inq->inq_wbus32)) {
                         if (scsi_ifsetcap(SD_ADDRESS(un), "wide-xfer",
                             1, 1) == 1) {
                                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                                    "sd_unit_attach: un:0x%p Wide Transfer "
+                                    "sdattach: un:0x%p Wide Transfer "
                                     "enabled\n", un);
                         }
 
                         /*
                          * If tagged queuing has also been enabled, then

@@ -7816,63 +7381,60 @@
                          * enable large xfers
                          */
                         if (un->un_saved_throttle == sd_max_throttle) {
                                 un->un_max_xfer_size =
                                     ddi_getprop(DDI_DEV_T_ANY, devi, 0,
-                                    sd_max_xfer_size, SD_MAX_XFER_SIZE);
+                                    "sd_max_xfer_size", SD_MAX_XFER_SIZE);
                                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                                    "sd_unit_attach: un:0x%p max transfer "
+                                    "sdattach: un:0x%p max transfer "
                                     "size=0x%x\n", un, un->un_max_xfer_size);
                         }
                 } else {
                         if (scsi_ifsetcap(SD_ADDRESS(un), "wide-xfer",
                             0, 1) == 1) {
                                 SD_INFO(SD_LOG_ATTACH_DETACH, un,
-                                    "sd_unit_attach: un:0x%p "
+                                    "sdattach: un:0x%p "
                                     "Wide Transfer disabled\n", un);
                         }
                 }
         } else {
                 un->un_tagflags = FLAG_STAG;
                 un->un_max_xfer_size = ddi_getprop(DDI_DEV_T_ANY,
-                    devi, 0, sd_max_xfer_size, SD_MAX_XFER_SIZE);
+                    devi, 0, "sd_max_xfer_size", SD_MAX_XFER_SIZE);
         }
 
         /*
          * If this target supports LUN reset, try to enable it.
          */
         if (un->un_f_lun_reset_enabled) {
                 if (scsi_ifsetcap(SD_ADDRESS(un), "lun-reset", 1, 1) == 1) {
-                        SD_INFO(SD_LOG_ATTACH_DETACH, un, "sd_unit_attach: "
+                        SD_INFO(SD_LOG_ATTACH_DETACH, un, "sdattach: "
                             "un:0x%p lun_reset capability set\n", un);
                 } else {
-                        SD_INFO(SD_LOG_ATTACH_DETACH, un, "sd_unit_attach: "
+                        SD_INFO(SD_LOG_ATTACH_DETACH, un, "sdattach: "
                             "un:0x%p lun-reset capability not set\n", un);
                 }
         }
 
         /*
-         * Adjust the maximum transfer size. This is to fix
+         * XXX Adjust the maximum transfer size. This was to fix
          * the problem of partial DMA support on SPARC. Some
          * HBA driver, like aac, has very small dma_attr_maxxfer
          * size, which requires partial DMA support on SPARC.
-         * In the future the SPARC pci nexus driver may solve
-         * the problem instead of this fix.
          */
         max_xfer_size = scsi_ifgetcap(SD_ADDRESS(un), "dma-max", 1);
         if ((max_xfer_size > 0) && (max_xfer_size < un->un_max_xfer_size)) {
-                /* We need DMA partial even on sparc to ensure sddump() works */
                 un->un_max_xfer_size = max_xfer_size;
                 if (un->un_partial_dma_supported == 0)
                         un->un_partial_dma_supported = 1;
         }
         if (ddi_prop_get_int(DDI_DEV_T_ANY, SD_DEVINFO(un),
             DDI_PROP_DONTPASS, "buf_break", 0) == 1) {
                 if (ddi_xbuf_attr_setup_brk(un->un_xbuf_attr,
                     un->un_max_xfer_size) == 1) {
                         un->un_buf_breakup_supported = 1;
-                        SD_INFO(SD_LOG_ATTACH_DETACH, un, "sd_unit_attach: "
+                        SD_INFO(SD_LOG_ATTACH_DETACH, un, "sdattach: "
                             "un:0x%p Buf breakup enabled\n", un);
                 }
         }
 
         /*

@@ -7882,16 +7444,14 @@
                 un->un_pkt_flags = PKT_DMA_PARTIAL;
         } else {
                 un->un_pkt_flags = 0;
         }
 
-        /* Initialize sd_ssc_t for internal uscsi commands */
-        ssc = sd_ssc_init(un);
         scsi_fm_init(devp);
 
         /*
-         * Allocate memory for SCSI FMA stuffs.
+         * Allocate memory for SCSI FMA stuff.
          */
         un->un_fm_private =
             kmem_zalloc(sizeof (struct sd_fm_internal), KM_SLEEP);
         sfip = (struct sd_fm_internal *)un->un_fm_private;
         sfip->fm_ssc.ssc_uscsi_cmd = &sfip->fm_ucmd;

@@ -7922,19 +7482,287 @@
                         sfip->fm_log_level = SD_FM_LOG_EREPORT;
                 else
                         sfip->fm_log_level = SD_FM_LOG_SILENT;
         }
 
+        /* Initialize sd_ssc_t for internal uscsi commands */
+        ssc = sd_ssc_init(un);
+
+        mutex_enter(SD_MUTEX(un));
         /*
+         * Initialize the devid for the unit. Indicate target reservation so
+         * that no real I/O is done for devices that need devid fabrication.
+         * We will try again in sd_unit_attach() if necessary.
+         */
+        if (un->un_f_devid_supported) {
+                sd_register_devid(ssc, devi, SD_TARGET_IS_RESERVED);
+        }
+        mutex_exit(SD_MUTEX(un));
+
+        /* Uninitialize sd_ssc_t pointer */
+        sd_ssc_fini(ssc);
+
+        cmlb_alloc_handle(&un->un_cmlbhandle);
+
+        if (cmlb_attach(devi, &sd_tgops, (int)devp->sd_inq->inq_dtype,
+            VOID2BOOLEAN(un->un_f_has_removable_media != 0),
+            VOID2BOOLEAN(un->un_f_is_hotpluggable != 0),
+            un->un_node_type, 0, un->un_cmlbhandle,
+            (void *)SD_PATH_DIRECT) != 0) {
+                goto cmlb_attach_failed;
+        }
+
+        /*
          * At this point in the attach, we have enough info in the
          * soft state to be able to issue commands to the target.
          *
+         * Schedule a taskq to finish attach to avoid holding the
+         * device tree lock for too long. If this fails, rollback
+         * and fail the attach.
+         */
+
+        if (taskq_dispatch(sd_tq, sd_unit_attach, devi, KM_PUSHPAGE) != NULL)
+                return (DDI_SUCCESS);
+
+        cmlb_detach(un->un_cmlbhandle, (void *)SD_PATH_DIRECT);
+        cmlb_free_handle(&un->un_cmlbhandle);
+
+cmlb_attach_failed:
+        mutex_enter(SD_MUTEX(un));
+
+        /* Deallocate SCSI FMA memory spaces */
+        kmem_free(un->un_fm_private, sizeof (struct sd_fm_internal));
+
+        /* Cancel callback for SD_PATH_DIRECT_PRIORITY cmd. restart */
+        if (un->un_direct_priority_timeid != NULL) {
+                timeout_id_t temp_id = un->un_direct_priority_timeid;
+                un->un_direct_priority_timeid = NULL;
+                mutex_exit(SD_MUTEX(un));
+                (void) untimeout(temp_id);
+                mutex_enter(SD_MUTEX(un));
+        }
+
+        /* Cancel any pending start/stop timeouts */
+        if (un->un_startstop_timeid != NULL) {
+                timeout_id_t temp_id = un->un_startstop_timeid;
+                un->un_startstop_timeid = NULL;
+                mutex_exit(SD_MUTEX(un));
+                (void) untimeout(temp_id);
+                mutex_enter(SD_MUTEX(un));
+        }
+
+        /* Cancel any pending reset-throttle timeouts */
+        if (un->un_reset_throttle_timeid != NULL) {
+                timeout_id_t temp_id = un->un_reset_throttle_timeid;
+                un->un_reset_throttle_timeid = NULL;
+                mutex_exit(SD_MUTEX(un));
+                (void) untimeout(temp_id);
+                mutex_enter(SD_MUTEX(un));
+        }
+
+        /* Cancel rmw warning message timeouts */
+        if (un->un_rmw_msg_timeid != NULL) {
+                timeout_id_t temp_id = un->un_rmw_msg_timeid;
+                un->un_rmw_msg_timeid = NULL;
+                mutex_exit(SD_MUTEX(un));
+                (void) untimeout(temp_id);
+                mutex_enter(SD_MUTEX(un));
+        }
+
+        /* Cancel any pending retry timeouts */
+        if (un->un_retry_timeid != NULL) {
+                timeout_id_t temp_id = un->un_retry_timeid;
+                un->un_retry_timeid = NULL;
+                mutex_exit(SD_MUTEX(un));
+                (void) untimeout(temp_id);
+                mutex_enter(SD_MUTEX(un));
+        }
+
+        /* Cancel any pending delayed cv broadcast timeouts */
+        if (un->un_dcvb_timeid != NULL) {
+                timeout_id_t temp_id = un->un_dcvb_timeid;
+                un->un_dcvb_timeid = NULL;
+                mutex_exit(SD_MUTEX(un));
+                (void) untimeout(temp_id);
+                mutex_enter(SD_MUTEX(un));
+        }
+
+        mutex_exit(SD_MUTEX(un));
+
+        /* There should not be any in-progress I/O so ASSERT this check */
+        ASSERT(un->un_ncmds_in_transport == 0);
+        ASSERT(un->un_ncmds_in_driver == 0);
+
+        /* Do not free the softstate if the callback routine is active */
+        sd_sync_with_callback(un);
+
+        /*
+         * Partition stats apparently are not used with removables. These would
+         * not have been created during attach, so no need to clean them up...
+         */
+        if (un->un_errstats != NULL) {
+                kstat_delete(un->un_errstats);
+                un->un_errstats = NULL;
+        }
+
+create_errstats_failed:
+
+        if (un->un_stats != NULL) {
+                kstat_delete(un->un_stats);
+                un->un_stats = NULL;
+        }
+
+        if (un->un_unmapstats != NULL) {
+                kstat_delete(un->un_unmapstats_ks);
+                un->un_unmapstats_ks = NULL;
+                un->un_unmapstats = NULL;
+        }
+
+        if (un->un_lat_ksp != NULL) {
+                kstat_delete(un->un_lat_ksp);
+                un->un_lat_ksp = NULL;
+                un->un_lat_stats = NULL;
+        }
+
+        ddi_xbuf_attr_unregister_devinfo(un->un_xbuf_attr, devi);
+        ddi_xbuf_attr_destroy(un->un_xbuf_attr);
+
+        ddi_prop_remove_all(devi);
+        sema_destroy(&un->un_semoclose);
+        cv_destroy(&un->un_state_cv);
+        cv_destroy(&un->un_detach_cv);
+        sd_free_rqs(un);
+
+alloc_rqs_failed:
+
+        devp->sd_private = NULL;
+        bzero(un, sizeof (struct sd_lun));      /* Clear any stale data! */
+
+        /*
+         * Note: the man pages are unclear as to whether or not doing a
+         * ddi_soft_state_free(sd_state, instance) is the right way to
+         * clean up after the ddi_soft_state_zalloc() if the subsequent
+         * ddi_get_soft_state() fails.  The implication seems to be
+         * that the get_soft_state cannot fail if the zalloc succeeds.
+         */
+        ddi_soft_state_free(sd_state, instance);
+
+probe_failed:
+        scsi_unprobe(devp);
+
+        return (DDI_FAILURE);
+}
+
+
+/*
+ *    Function: sddetach
+ *
+ * Description: Driver's detach(9E) entry point function.
+ *
+ *   Arguments: devi - opaque device info handle
+ *              cmd  - detach  type
+ *
+ * Return Code: DDI_SUCCESS
+ *              DDI_FAILURE
+ *
+ *     Context: Kernel thread context
+ */
+
+static int
+sddetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+        switch (cmd) {
+        case DDI_DETACH:
+                return (sd_unit_detach(devi));
+        case DDI_SUSPEND:
+                return (sd_ddi_suspend(devi));
+        default:
+                break;
+        }
+        return (DDI_FAILURE);
+}
+
+
+/*
+ *     Function: sd_sync_with_callback
+ *
+ *  Description: Prevents sd_unit_attach or sd_unit_detach from freeing the soft
+ *               state while the callback routine is active.
+ *
+ *    Arguments: un: softstate structure for the instance
+ *
+ *      Context: Kernel thread context
+ */
+
+static void
+sd_sync_with_callback(struct sd_lun *un)
+{
+        ASSERT(un != NULL);
+
+        mutex_enter(SD_MUTEX(un));
+
+        ASSERT(un->un_in_callback >= 0);
+
+        while (un->un_in_callback > 0) {
+                mutex_exit(SD_MUTEX(un));
+                delay(2);
+                mutex_enter(SD_MUTEX(un));
+        }
+
+        mutex_exit(SD_MUTEX(un));
+}
+
+/*
+ *    Function: sd_unit_attach
+ *
+ * Description: Performs DDI_ATTACH processing for sdattach(). Allocates
+ *              the soft state structure for the device and performs
+ *              all necessary structure and device initializations.
+ *
+ *   Arguments: devi: the system's dev_info_t for the device.
+ *
+ * Return Code: DDI_SUCCESS if attach is successful.
+ *              DDI_FAILURE if any part of the attach fails.
+ *
+ *     Context: Called at attach(9e) time for the DDI_ATTACH flag.
+ *              Kernel thread context only.  Can sleep.
+ */
+void
+sd_unit_attach(void *arg)
+{
+        dev_info_t              *devi = arg;
+        struct  scsi_device     *devp = ddi_get_driver_private(devi);
+        struct  sd_lun          *un = (struct sd_lun *)devp->sd_private;
+        char                    name_str[48];
+        int     reservation_flag = SD_TARGET_IS_UNRESERVED;
+        int     rval;
+        int     wc_enabled;
+        int     wc_changeable;
+        int     tgt;
+        uint64_t        capacity;
+        uint_t          lbasize = 0;
+        dev_info_t      *pdip = ddi_get_parent(devi);
+        int             geom_label_valid = 0;
+        sd_ssc_t        *ssc;
+        int             status;
+        char            *devid;
+
+        /*
+         * Retrieve the target ID of the device.
+         */
+        tgt = ddi_prop_get_int(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
+            SCSI_ADDR_PROP_TARGET, -1);
+
+        /*
          * All command paths used below MUST issue their commands as
          * SD_PATH_DIRECT. This is important as intermediate layers
          * are not all initialized yet (such as PM).
          */
 
+        /* Initialize sd_ssc_t for internal uscsi commands */
+        ssc = sd_ssc_init(un);
+
         /*
          * Send a TEST UNIT READY command to the device. This should clear
          * any outstanding UNIT ATTENTION that may be present.
          *
          * Note: Don't check for success, just track if there is a reservation,

@@ -7994,20 +7822,18 @@
                                             sd_label, CE_WARN,
                                             "disk has %llu blocks, which "
                                             "is too large for a 32-bit "
                                             "kernel", capacity);
 
-#if defined(__i386) || defined(__amd64)
                                         /*
                                          * 1TB disk was treated as (1T - 512)B
                                          * in the past, so that it might have
                                          * valid VTOC and solaris partitions,
                                          * we have to allow it to continue to
                                          * work.
                                          */
-                                        if (capacity -1 > DK_MAX_BLOCKS)
-#endif
+                                        if (capacity - 1 > DK_MAX_BLOCKS)
                                         goto spinup_failed;
 #endif
                                 }
 
                                 /*

@@ -8068,16 +7894,18 @@
                                 /*
                                  * Likewise, should never get here if the
                                  * spin-up succeeded. Just continue with
                                  * the attach...
                                  */
-                                if (status == EIO)
+                                if (status == EIO) {
                                         sd_ssc_assessment(ssc,
                                             SD_FMT_STATUS_CHECK);
-                                else
+                                        goto spinup_failed;
+                                } else {
                                         sd_ssc_assessment(ssc,
                                             SD_FMT_IGNORE);
+                                }
                                 break;
                         }
                         break;
                 case EACCES:
                         /*

@@ -8123,11 +7951,13 @@
         /*
          * Initialize power management
          */
         mutex_init(&un->un_pm_mutex, NULL, MUTEX_DRIVER, NULL);
         cv_init(&un->un_pm_busy_cv, NULL, CV_DRIVER, NULL);
+#ifdef notyet
         sd_setup_pm(ssc, devi);
+#endif
         if (un->un_f_pm_is_enabled == FALSE) {
                 /*
                  * For performance, point to a jump table that does
                  * not include pm.
                  * The direct and priority chains don't change with PM.

@@ -8158,11 +7988,11 @@
         sd_retry_on_reservation_conflict = ddi_getprop(DDI_DEV_T_ANY, devi,
             DDI_PROP_DONTPASS, "retry-on-reservation-conflict",
             sd_retry_on_reservation_conflict);
         if (sd_retry_on_reservation_conflict != 0) {
                 sd_retry_on_reservation_conflict = ddi_getprop(DDI_DEV_T_ANY,
-                    devi, DDI_PROP_DONTPASS, sd_resv_conflict_name,
+                    devi, DDI_PROP_DONTPASS, "sd_retry_on_reservation_conflict",
                     sd_retry_on_reservation_conflict);
         }
 
         /* Set up options for QFULL handling. */
         if ((rval = ddi_getprop(DDI_DEV_T_ANY, devi, 0,

@@ -8194,30 +8024,15 @@
         /*
          * Check whether the drive is in emulation mode.
          */
         sd_check_emulation_mode(ssc);
 
-        cmlb_alloc_handle(&un->un_cmlbhandle);
-
-#if defined(__i386) || defined(__amd64)
-        /*
-         * On x86, compensate for off-by-1 legacy error
-         */
+        /* Compensate for off-by-1 legacy error */
         if (!un->un_f_has_removable_media && !un->un_f_is_hotpluggable &&
             (lbasize == un->un_sys_blocksize))
-                offbyone = CMLB_OFF_BY_ONE;
-#endif
+                cmlb_workaround_off_by_one(un->un_cmlbhandle);
 
-        if (cmlb_attach(devi, &sd_tgops, (int)devp->sd_inq->inq_dtype,
-            VOID2BOOLEAN(un->un_f_has_removable_media != 0),
-            VOID2BOOLEAN(un->un_f_is_hotpluggable != 0),
-            un->un_node_type, offbyone, un->un_cmlbhandle,
-            (void *)SD_PATH_DIRECT) != 0) {
-                goto cmlb_attach_failed;
-        }
-
-
         /*
          * Read and validate the device's geometry (ie, disk label)
          * A new unformatted drive will not have a valid geometry, but
          * the driver needs to successfully attach to this device so
          * the drive can be formatted via ioctls.

@@ -8226,34 +8041,17 @@
             (void *)SD_PATH_DIRECT) == 0) ? 1: 0;
 
         mutex_enter(SD_MUTEX(un));
 
         /*
-         * Read and initialize the devid for the unit.
+         * Read and initialize the devid for the unit if not done already.
          */
-        if (un->un_f_devid_supported) {
+        if (un->un_f_devid_supported && un->un_devid == NULL) {
                 sd_register_devid(ssc, devi, reservation_flag);
         }
         mutex_exit(SD_MUTEX(un));
 
-#if (defined(__fibre))
-        /*
-         * Register callbacks for fibre only.  You can't do this solely
-         * on the basis of the devid_type because this is hba specific.
-         * We need to query our hba capabilities to find out whether to
-         * register or not.
-         */
-        if (un->un_f_is_fibre) {
-                if (strcmp(un->un_node_type, DDI_NT_BLOCK_CHAN)) {
-                        sd_init_event_callbacks(un);
-                        SD_TRACE(SD_LOG_ATTACH_DETACH, un,
-                            "sd_unit_attach: un:0x%p event callbacks inserted",
-                            un);
-                }
-        }
-#endif
-
         if (un->un_f_opt_disable_cache == TRUE) {
                 /*
                  * Disable both read cache and write cache.  This is
                  * the historic behavior of the keywords in the config file.
                  */

@@ -8361,10 +8159,11 @@
 
         sd_set_errstats(un);
         SD_TRACE(SD_LOG_ATTACH_DETACH, un,
             "sd_unit_attach: un:0x%p errstats set\n", un);
 
+        sd_setup_blk_limits(ssc);
 
         /*
          * After successfully attaching an instance, we record the information
          * of how many luns have been attached on the relative target and
          * controller for parallel SCSI. This information is used when sd tries

@@ -8378,20 +8177,24 @@
             "sd_unit_attach: un:0x%p exit success\n", un);
 
         /* Uninitialize sd_ssc_t pointer */
         sd_ssc_fini(ssc);
 
-        return (DDI_SUCCESS);
+        /* attach finished, switch to SD_STATE_NORMAL */
+        mutex_enter(SD_MUTEX(un));
+        New_state(un, SD_STATE_NORMAL);
+        cv_broadcast(&un->un_suspend_cv);
+        mutex_exit(SD_MUTEX(un));
 
+        return;
+
         /*
          * An error occurred during the attach; clean up & return failure.
          */
+
 wm_cache_failed:
 devid_failed:
-        ddi_remove_minor_node(devi, NULL);
-
-cmlb_attach_failed:
         /*
          * Cleanup from the scsi_ifsetcap() calls (437868)
          */
         (void) scsi_ifsetcap(SD_ADDRESS(un), "lun-reset", 0, 1);
         (void) scsi_ifsetcap(SD_ADDRESS(un), "wide-xfer", 0, 1);

@@ -8408,127 +8211,27 @@
         if (un->un_f_is_fibre == FALSE) {
                 (void) scsi_ifsetcap(SD_ADDRESS(un), "auto-rqsense", 0, 1);
         }
 
 spinup_failed:
-
-        /* Uninitialize sd_ssc_t pointer */
-        sd_ssc_fini(ssc);
-
+        /* attach failed, switch to SD_STATE_ATTACH_FAILED */
         mutex_enter(SD_MUTEX(un));
-
-        /* Deallocate SCSI FMA memory spaces */
-        kmem_free(un->un_fm_private, sizeof (struct sd_fm_internal));
-
-        /* Cancel callback for SD_PATH_DIRECT_PRIORITY cmd. restart */
-        if (un->un_direct_priority_timeid != NULL) {
-                timeout_id_t temp_id = un->un_direct_priority_timeid;
-                un->un_direct_priority_timeid = NULL;
+        New_state(un, SD_STATE_ATTACH_FAILED);
+        cv_broadcast(&un->un_suspend_cv);
                 mutex_exit(SD_MUTEX(un));
-                (void) untimeout(temp_id);
-                mutex_enter(SD_MUTEX(un));
-        }
 
-        /* Cancel any pending start/stop timeouts */
-        if (un->un_startstop_timeid != NULL) {
-                timeout_id_t temp_id = un->un_startstop_timeid;
-                un->un_startstop_timeid = NULL;
-                mutex_exit(SD_MUTEX(un));
-                (void) untimeout(temp_id);
-                mutex_enter(SD_MUTEX(un));
-        }
+        devid = DEVI(devi)->devi_devid_str;
+        scsi_fm_ereport_post(un->un_sd, 0,
+            NULL, "disk.attach-failure", ssc->ssc_uscsi_info->ui_ena,
+            devid, NULL, DDI_NOSLEEP, NULL,
+            FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
+            DEVID_IF_KNOWN(devid));
 
-        /* Cancel any pending reset-throttle timeouts */
-        if (un->un_reset_throttle_timeid != NULL) {
-                timeout_id_t temp_id = un->un_reset_throttle_timeid;
-                un->un_reset_throttle_timeid = NULL;
-                mutex_exit(SD_MUTEX(un));
-                (void) untimeout(temp_id);
-                mutex_enter(SD_MUTEX(un));
-        }
-
-        /* Cancel rmw warning message timeouts */
-        if (un->un_rmw_msg_timeid != NULL) {
-                timeout_id_t temp_id = un->un_rmw_msg_timeid;
-                un->un_rmw_msg_timeid = NULL;
-                mutex_exit(SD_MUTEX(un));
-                (void) untimeout(temp_id);
-                mutex_enter(SD_MUTEX(un));
-        }
-
-        /* Cancel any pending retry timeouts */
-        if (un->un_retry_timeid != NULL) {
-                timeout_id_t temp_id = un->un_retry_timeid;
-                un->un_retry_timeid = NULL;
-                mutex_exit(SD_MUTEX(un));
-                (void) untimeout(temp_id);
-                mutex_enter(SD_MUTEX(un));
-        }
-
-        /* Cancel any pending delayed cv broadcast timeouts */
-        if (un->un_dcvb_timeid != NULL) {
-                timeout_id_t temp_id = un->un_dcvb_timeid;
-                un->un_dcvb_timeid = NULL;
-                mutex_exit(SD_MUTEX(un));
-                (void) untimeout(temp_id);
-                mutex_enter(SD_MUTEX(un));
-        }
-
-        mutex_exit(SD_MUTEX(un));
-
-        /* There should not be any in-progress I/O so ASSERT this check */
-        ASSERT(un->un_ncmds_in_transport == 0);
-        ASSERT(un->un_ncmds_in_driver == 0);
-
-        /* Do not free the softstate if the callback routine is active */
-        sd_sync_with_callback(un);
-
-        /*
-         * Partition stats apparently are not used with removables. These would
-         * not have been created during attach, so no need to clean them up...
-         */
-        if (un->un_errstats != NULL) {
-                kstat_delete(un->un_errstats);
-                un->un_errstats = NULL;
-        }
-
-create_errstats_failed:
-
-        if (un->un_stats != NULL) {
-                kstat_delete(un->un_stats);
-                un->un_stats = NULL;
-        }
-
-        ddi_xbuf_attr_unregister_devinfo(un->un_xbuf_attr, devi);
-        ddi_xbuf_attr_destroy(un->un_xbuf_attr);
-
-        ddi_prop_remove_all(devi);
-        sema_destroy(&un->un_semoclose);
-        cv_destroy(&un->un_state_cv);
-
-        sd_free_rqs(un);
-
-alloc_rqs_failed:
-
-        devp->sd_private = NULL;
-        bzero(un, sizeof (struct sd_lun));      /* Clear any stale data! */
-
-        /*
-         * Note: the man pages are unclear as to whether or not doing a
-         * ddi_soft_state_free(sd_state, instance) is the right way to
-         * clean up after the ddi_soft_state_zalloc() if the subsequent
-         * ddi_get_soft_state() fails.  The implication seems to be
-         * that the get_soft_state cannot fail if the zalloc succeeds.
-         */
-#ifndef XPV_HVM_DRIVER
-        ddi_soft_state_free(sd_state, instance);
-#endif /* !XPV_HVM_DRIVER */
-
-probe_failed:
-        scsi_unprobe(devp);
-
-        return (DDI_FAILURE);
+        /* Uninitialize sd_ssc_t pointer */
+        sd_ssc_fini(ssc);
+        SD_ERROR(SD_LOG_ATTACH_DETACH, un, "sd_unit_attach failed: un: %p",
+            (void *)un);
 }
 
 
 /*
  *    Function: sd_unit_detach

@@ -8555,24 +8258,31 @@
         mutex_enter(&sd_detach_mutex);
 
         /*
          * Fail the detach for any of the following:
          *  - Unable to get the sd_lun struct for the instance
-         *  - A layered driver has an outstanding open on the instance
+         * - The instance is still attaching
          *  - Another thread is already detaching this instance
          *  - Another thread is currently performing an open
+         *
+         * Additionaly, if "device gone" flag is not set:
+         * - There are outstanding commands in driver
+         * - There are outstanding commands in transport
          */
         devp = ddi_get_driver_private(devi);
-        if ((devp == NULL) ||
-            ((un = (struct sd_lun *)devp->sd_private) == NULL) ||
-            (un->un_ncmds_in_driver != 0) || (un->un_layer_count != 0) ||
-            (un->un_detach_count != 0) || (un->un_opens_in_progress != 0)) {
+        if (devp == NULL || (un = (struct sd_lun *)devp->sd_private) == NULL ||
+            un->un_detach_count != 0 || un->un_opens_in_progress != 0 ||
+            (!DEVI_IS_GONE(devi) &&
+            (un->un_state == SD_STATE_RWAIT ||
+            un->un_state == SD_STATE_ATTACHING ||
+            un->un_ncmds_in_driver != 0 ||
+            un->un_ncmds_in_transport != 0))) {
                 mutex_exit(&sd_detach_mutex);
                 return (DDI_FAILURE);
         }
 
-        SD_TRACE(SD_LOG_ATTACH_DETACH, un, "sd_unit_detach: entry 0x%p\n", un);
+        SD_TRACE(SD_LOG_ATTACH_DETACH, un, "%s: entry 0x%p\n", __func__, un);
 
         /*
          * Mark this instance as currently in a detach, to inhibit any
          * opens from a layered driver.
          */

@@ -8582,14 +8292,10 @@
         tgt = ddi_prop_get_int(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
             SCSI_ADDR_PROP_TARGET, -1);
 
         dev = sd_make_device(SD_DEVINFO(un));
 
-#ifndef lint
-        _NOTE(COMPETING_THREADS_NOW);
-#endif
-
         mutex_enter(SD_MUTEX(un));
 
         /*
          * Fail the detach if there are any outstanding layered
          * opens on this device.

@@ -8599,37 +8305,32 @@
                         goto err_notclosed;
                 }
         }
 
         /*
-         * Verify there are NO outstanding commands issued to this device.
-         * ie, un_ncmds_in_transport == 0.
-         * It's possible to have outstanding commands through the physio
-         * code path, even though everything's closed.
+         * If the attach wasn't successful, some normal cleanup work must not
+         * be done.
          */
-        if ((un->un_ncmds_in_transport != 0) || (un->un_retry_timeid != NULL) ||
-            (un->un_direct_priority_timeid != NULL) ||
-            (un->un_state == SD_STATE_RWAIT)) {
+        if (un->un_state == SD_STATE_ATTACH_FAILED) {
                 mutex_exit(SD_MUTEX(un));
-                SD_ERROR(SD_LOG_ATTACH_DETACH, un,
-                    "sd_dr_detach: Detach failure due to outstanding cmds\n");
-                goto err_stillbusy;
+                goto no_attach_cleanup;
         }
 
         /*
          * If we have the device reserved, release the reservation.
          */
-        if ((un->un_resvd_status & SD_RESERVE) &&
+        if (!DEVI_IS_GONE(devi) &&
+            (un->un_resvd_status & SD_RESERVE) &&
             !(un->un_resvd_status & SD_LOST_RESERVE)) {
                 mutex_exit(SD_MUTEX(un));
                 /*
                  * Note: sd_reserve_release sends a command to the device
                  * via the sd_ioctlcmd() path, and can sleep.
                  */
                 if (sd_reserve_release(dev, SD_RELEASE) != 0) {
                         SD_ERROR(SD_LOG_ATTACH_DETACH, un,
-                            "sd_dr_detach: Cannot release reservation \n");
+                            "%s: cannot release reservation\n", __func__);
                 }
         } else {
                 mutex_exit(SD_MUTEX(un));
         }
 

@@ -8682,11 +8383,36 @@
 
         /* Remove any pending reservation reclaim requests for this device */
         sd_rmv_resv_reclaim_req(dev);
 
         mutex_enter(SD_MUTEX(un));
+        if (un->un_retry_timeid != NULL) {
+                timeout_id_t temp_id = un->un_retry_timeid;
+                un->un_retry_timeid = NULL;
+                mutex_exit(SD_MUTEX(un));
+                (void) untimeout(temp_id);
+                mutex_enter(SD_MUTEX(un));
 
+                if (un->un_retry_bp != NULL) {
+                        un->un_retry_bp->av_forw = un->un_waitq_headp;
+                        un->un_waitq_headp = un->un_retry_bp;
+                        if (un->un_waitq_tailp == NULL)
+                                un->un_waitq_tailp = un->un_retry_bp;
+                        un->un_retry_bp = NULL;
+                        un->un_retry_statp = NULL;
+                }
+        }
+
+        if (DEVI_IS_GONE(SD_DEVINFO(un))) {
+                /* abort in-flight IO */
+                (void) scsi_abort(SD_ADDRESS(un), NULL);
+                /* abort pending IO */
+                un->un_failfast_state = SD_FAILFAST_ACTIVE;
+                un->un_failfast_bp = NULL;
+                sd_failfast_flushq(un, B_TRUE);
+        }
+
         /* Cancel any pending callbacks for SD_PATH_DIRECT_PRIORITY cmd. */
         if (un->un_direct_priority_timeid != NULL) {
                 timeout_id_t temp_id = un->un_direct_priority_timeid;
                 un->un_direct_priority_timeid = NULL;
                 mutex_exit(SD_MUTEX(un));

@@ -8695,63 +8421,53 @@
         }
 
         /* Cancel any active multi-host disk watch thread requests */
         if (un->un_mhd_token != NULL) {
                 mutex_exit(SD_MUTEX(un));
-                 _NOTE(DATA_READABLE_WITHOUT_LOCK(sd_lun::un_mhd_token));
                 if (scsi_watch_request_terminate(un->un_mhd_token,
                     SCSI_WATCH_TERMINATE_NOWAIT)) {
                         SD_ERROR(SD_LOG_ATTACH_DETACH, un,
-                            "sd_dr_detach: Cannot cancel mhd watch request\n");
+                            "%s: cannot cancel mhd watch request\n", __func__);
                         /*
                          * Note: We are returning here after having removed
                          * some driver timeouts above. This is consistent with
                          * the legacy implementation but perhaps the watch
                          * terminate call should be made with the wait flag set.
                          */
-                        goto err_stillbusy;
+                        goto err_remove_event;
                 }
                 mutex_enter(SD_MUTEX(un));
                 un->un_mhd_token = NULL;
         }
 
         if (un->un_swr_token != NULL) {
                 mutex_exit(SD_MUTEX(un));
-                _NOTE(DATA_READABLE_WITHOUT_LOCK(sd_lun::un_swr_token));
                 if (scsi_watch_request_terminate(un->un_swr_token,
                     SCSI_WATCH_TERMINATE_NOWAIT)) {
                         SD_ERROR(SD_LOG_ATTACH_DETACH, un,
-                            "sd_dr_detach: Cannot cancel swr watch request\n");
+                            "%s: cannot cancel swr watch request\n", __func__);
                         /*
                          * Note: We are returning here after having removed
                          * some driver timeouts above. This is consistent with
                          * the legacy implementation but perhaps the watch
                          * terminate call should be made with the wait flag set.
                          */
-                        goto err_stillbusy;
+                        goto err_remove_event;
                 }
                 mutex_enter(SD_MUTEX(un));
                 un->un_swr_token = NULL;
         }
 
-        mutex_exit(SD_MUTEX(un));
-
         /*
          * Clear any scsi_reset_notifies. We clear the reset notifies
          * if we have not registered one.
          * Note: The sd_mhd_reset_notify_cb() fn tries to acquire SD_MUTEX!
          */
+        mutex_exit(SD_MUTEX(un));
         (void) scsi_reset_notify(SD_ADDRESS(un), SCSI_RESET_CANCEL,
             sd_mhd_reset_notify_cb, (caddr_t)un);
 
-        /*
-         * protect the timeout pointers from getting nulled by
-         * their callback functions during the cancellation process.
-         * In such a scenario untimeout can be invoked with a null value.
-         */
-        _NOTE(NO_COMPETING_THREADS_NOW);
-
         mutex_enter(&un->un_pm_mutex);
         if (un->un_pm_idle_timeid != NULL) {
                 timeout_id_t temp_id = un->un_pm_idle_timeid;
                 un->un_pm_idle_timeid = NULL;
                 mutex_exit(&un->un_pm_mutex);

@@ -8789,13 +8505,13 @@
                 mutex_exit(&un->un_pm_mutex);
                 if ((un->un_f_pm_is_enabled == TRUE) &&
                     (pm_lower_power(SD_DEVINFO(un), 0, SD_PM_STATE_STOPPED(un))
                     != DDI_SUCCESS)) {
                         SD_ERROR(SD_LOG_ATTACH_DETACH, un,
-                    "sd_dr_detach: Lower power request failed, ignoring.\n");
+                            "%s: lower power request failed, ignoring\n",
+                            __func__);
                         /*
-                         * Fix for bug: 4297749, item # 13
                          * The above test now includes a check to see if PM is
                          * supported by this device before call
                          * pm_lower_power().
                          * Note, the following is not dead code. The call to
                          * pm_lower_power above will generate a call back into

@@ -8854,11 +8570,11 @@
                          * substantial cleanup above. This is consistent
                          * with the legacy implementation but this may not
                          * be the right thing to do.
                          */
                         SD_ERROR(SD_LOG_ATTACH_DETACH, un,
-                            "sd_dr_detach: Cannot cancel insert event\n");
+                            "%s: cannot cancel insert event\n", __func__);
                         goto err_remove_event;
                 }
                 un->un_insert_event = NULL;
 
                 if ((un->un_remove_event != NULL) &&

@@ -8869,19 +8585,38 @@
                          * substantial cleanup above. This is consistent
                          * with the legacy implementation but this may not
                          * be the right thing to do.
                          */
                         SD_ERROR(SD_LOG_ATTACH_DETACH, un,
-                            "sd_dr_detach: Cannot cancel remove event\n");
+                            "%s: cannot cancel remove event\n", __func__);
                         goto err_remove_event;
                 }
                 un->un_remove_event = NULL;
         }
 
         /* Do not free the softstate if the callback routine is active */
         sd_sync_with_callback(un);
 
+no_attach_cleanup:
+        /*
+         * The driver must wait, at least attempt to wait, for any commands
+         * still in the driver.
+         */
+        mutex_enter(SD_MUTEX(un));
+
+        while (un->un_ncmds_in_driver != 0) {
+                clock_t max_delay = ddi_get_lbolt() + SEC_TO_TICK(30);
+                un->un_f_detach_waiting = 1;
+                if (cv_timedwait(&un->un_detach_cv, SD_MUTEX(un),
+                    max_delay) == -1) {
+                        break;
+                }
+        }
+
+        un->un_f_detach_waiting = 0;
+        mutex_exit(SD_MUTEX(un));
+
         cmlb_detach(un->un_cmlbhandle, (void *)SD_PATH_DIRECT);
         cmlb_free_handle(&un->un_cmlbhandle);
 
         /*
          * Hold the detach mutex here, to make sure that no other threads ever

@@ -8934,10 +8669,20 @@
          */
         if (un->un_stats != NULL) {
                 kstat_delete(un->un_stats);
                 un->un_stats = NULL;
         }
+        if (un->un_unmapstats != NULL) {
+                kstat_delete(un->un_unmapstats_ks);
+                un->un_unmapstats_ks = NULL;
+                un->un_unmapstats = NULL;
+        }
+        if (un->un_lat_ksp != NULL) {
+                kstat_delete(un->un_lat_ksp);
+                un->un_lat_stats = NULL;
+                un->un_lat_ksp = NULL;
+        }
         if (un->un_errstats != NULL) {
                 kstat_delete(un->un_errstats);
                 un->un_errstats = NULL;
         }
 

@@ -8964,10 +8709,13 @@
         cv_destroy(&un->un_wcc_cv);
 
         /* Open/close semaphore */
         sema_destroy(&un->un_semoclose);
 
+        /* Used to wait for outstanding commands */
+        cv_destroy(&un->un_detach_cv);
+
         /* Removable media condvar. */
         cv_destroy(&un->un_state_cv);
 
         /* Suspend/resume condvar. */
         cv_destroy(&un->un_suspend_cv);

@@ -8999,24 +8747,24 @@
          */
         if ((tgt >= 0) && (tgt < NTARGETS_WIDE)) {
                 sd_scsi_update_lun_on_target(pdip, tgt, SD_SCSI_LUN_DETACH);
         }
 
+        ddi_remove_minor_node(devi, NULL);
+        (void) devfs_clean(devi, NULL, DV_CLEAN_FORCE);
+
         return (DDI_SUCCESS);
 
 err_notclosed:
         mutex_exit(SD_MUTEX(un));
 
-err_stillbusy:
-        _NOTE(NO_COMPETING_THREADS_NOW);
-
 err_remove_event:
         mutex_enter(&sd_detach_mutex);
         un->un_detach_count--;
         mutex_exit(&sd_detach_mutex);
 
-        SD_TRACE(SD_LOG_ATTACH_DETACH, un, "sd_unit_detach: exit failure\n");
+        SD_TRACE(SD_LOG_ATTACH_DETACH, un, "%s: exit failure\n", __func__);
         return (DDI_FAILURE);
 }
 
 
 /*

@@ -9219,13 +8967,12 @@
 
         ASSERT(un != NULL);
 
         instance = ddi_get_instance(SD_DEVINFO(un));
 
-        /* Note:x86: is this a VTOC8/VTOC16 difference? */
+        /* XXX is this a VTOC8/VTOC16 difference? */
         for (i = 0; i < NSDMAP; i++) {
-
                 if (cmlb_partinfo(un->un_cmlbhandle, i,
                     &nblks, NULL, &partname, NULL, (void *)SD_PATH_DIRECT) != 0)
                         continue;
                 mutex_enter(SD_MUTEX(un));
 

@@ -9246,112 +8993,11 @@
                 }
                 mutex_exit(SD_MUTEX(un));
         }
 }
 
-
-#if (defined(__fibre))
 /*
- *    Function: sd_init_event_callbacks
- *
- * Description: This routine initializes the insertion and removal event
- *              callbacks. (fibre only)
- *
- *   Arguments: un - driver soft state (unit) structure
- *
- *     Context: Kernel thread context
- */
-
-static void
-sd_init_event_callbacks(struct sd_lun *un)
-{
-        ASSERT(un != NULL);
-
-        if ((un->un_insert_event == NULL) &&
-            (ddi_get_eventcookie(SD_DEVINFO(un), FCAL_INSERT_EVENT,
-            &un->un_insert_event) == DDI_SUCCESS)) {
-                /*
-                 * Add the callback for an insertion event
-                 */
-                (void) ddi_add_event_handler(SD_DEVINFO(un),
-                    un->un_insert_event, sd_event_callback, (void *)un,
-                    &(un->un_insert_cb_id));
-        }
-
-        if ((un->un_remove_event == NULL) &&
-            (ddi_get_eventcookie(SD_DEVINFO(un), FCAL_REMOVE_EVENT,
-            &un->un_remove_event) == DDI_SUCCESS)) {
-                /*
-                 * Add the callback for a removal event
-                 */
-                (void) ddi_add_event_handler(SD_DEVINFO(un),
-                    un->un_remove_event, sd_event_callback, (void *)un,
-                    &(un->un_remove_cb_id));
-        }
-}
-
-
-/*
- *    Function: sd_event_callback
- *
- * Description: This routine handles insert/remove events (photon). The
- *              state is changed to OFFLINE which can be used to supress
- *              error msgs. (fibre only)
- *
- *   Arguments: un - driver soft state (unit) structure
- *
- *     Context: Callout thread context
- */
-/* ARGSUSED */
-static void
-sd_event_callback(dev_info_t *dip, ddi_eventcookie_t event, void *arg,
-    void *bus_impldata)
-{
-        struct sd_lun *un = (struct sd_lun *)arg;
-
-        _NOTE(DATA_READABLE_WITHOUT_LOCK(sd_lun::un_insert_event));
-        if (event == un->un_insert_event) {
-                SD_TRACE(SD_LOG_COMMON, un, "sd_event_callback: insert event");
-                mutex_enter(SD_MUTEX(un));
-                if (un->un_state == SD_STATE_OFFLINE) {
-                        if (un->un_last_state != SD_STATE_SUSPENDED) {
-                                un->un_state = un->un_last_state;
-                        } else {
-                                /*
-                                 * We have gone through SUSPEND/RESUME while
-                                 * we were offline. Restore the last state
-                                 */
-                                un->un_state = un->un_save_state;
-                        }
-                }
-                mutex_exit(SD_MUTEX(un));
-
-        _NOTE(DATA_READABLE_WITHOUT_LOCK(sd_lun::un_remove_event));
-        } else if (event == un->un_remove_event) {
-                SD_TRACE(SD_LOG_COMMON, un, "sd_event_callback: remove event");
-                mutex_enter(SD_MUTEX(un));
-                /*
-                 * We need to handle an event callback that occurs during
-                 * the suspend operation, since we don't prevent it.
-                 */
-                if (un->un_state != SD_STATE_OFFLINE) {
-                        if (un->un_state != SD_STATE_SUSPENDED) {
-                                New_state(un, SD_STATE_OFFLINE);
-                        } else {
-                                un->un_last_state = SD_STATE_OFFLINE;
-                        }
-                }
-                mutex_exit(SD_MUTEX(un));
-        } else {
-                scsi_log(SD_DEVINFO(un), sd_label, CE_NOTE,
-                    "!Unknown event\n");
-        }
-
-}
-#endif
-
-/*
  * Values related to caching mode page depending on whether the unit is ATAPI.
  */
 #define SDC_CDB_GROUP(un) ((un->un_f_cfg_is_atapi == TRUE) ? \
         CDB_GROUP1 : CDB_GROUP0)
 #define SDC_HDRLEN(un) ((un->un_f_cfg_is_atapi == TRUE) ? \

@@ -9750,13 +9396,12 @@
                 rval = sd_send_scsi_INQUIRY(ssc, inq86, inq86_len,
                     0x01, 0x86, &inq86_resid);
 
                 if (rval == 0 && (inq86_len - inq86_resid > 6)) {
                         SD_TRACE(SD_LOG_COMMON, un,
-                            "sd_get_nv_sup: \
-                            successfully get VPD page: %x \
-                            PAGE LENGTH: %x BYTE 6: %x\n",
+                            "sd_get_nv_sup: successfully get VPD page: %x "
+                            "PAGE LENGTH: %x BYTE 6: %x\n",
                             inq86[1], inq86[3], inq86[6]);
 
                         mutex_enter(SD_MUTEX(un));
                         /*
                          * check the value of NV_SUP bit: only if the device

@@ -10094,22 +9739,16 @@
         /*
          * Fail the open if there is no softstate for the instance, or
          * if another thread somewhere is trying to detach the instance.
          */
         if (((un = ddi_get_soft_state(sd_state, instance)) == NULL) ||
-            (un->un_detach_count != 0)) {
+            un->un_detach_count != 0 || DEVI_IS_GONE(SD_DEVINFO(un))) {
                 mutex_exit(&sd_detach_mutex);
                 /*
-                 * The probe cache only needs to be cleared when open (9e) fails
-                 * with ENXIO (4238046).
+                 * The probe cache only needs to be cleared when open (9E) fails
+                 * with ENXIO.
                  */
-                /*
-                 * un-conditionally clearing probe cache is ok with
-                 * separate sd/ssd binaries
-                 * x86 platform can be an issue with both parallel
-                 * and fibre in 1 binary
-                 */
                 sd_scsi_clear_probe_cache();
                 return (ENXIO);
         }
 
         /*

@@ -10156,22 +9795,28 @@
          * status.
          */
 
         if (!nodelay) {
                 while ((un->un_state == SD_STATE_SUSPENDED) ||
-                    (un->un_state == SD_STATE_PM_CHANGING)) {
+                    (un->un_state == SD_STATE_PM_CHANGING) ||
+                    (un->un_state == SD_STATE_ATTACHING)) {
                         cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
                 }
-
                 mutex_exit(SD_MUTEX(un));
                 if (sd_pm_entry(un) != DDI_SUCCESS) {
                         rval = EIO;
                         SD_ERROR(SD_LOG_OPEN_CLOSE, un,
                             "sdopen: sd_pm_entry failed\n");
                         goto open_failed_with_pm;
                 }
                 mutex_enter(SD_MUTEX(un));
+        } else if (un->un_state == SD_STATE_ATTACH_FAILED) {
+                mutex_exit(SD_MUTEX(un));
+                rval = EIO;
+                SD_ERROR(SD_LOG_OPEN_CLOSE, un,
+                    "sdopen: attach failed, can't open\n");
+                goto open_failed_not_attached;
         }
 
         /* check for previous exclusive open */
         SD_TRACE(SD_LOG_OPEN_CLOSE, un, "sdopen: un=%p\n", (void *)un);
         SD_TRACE(SD_LOG_OPEN_CLOSE, un,

@@ -10258,11 +9903,10 @@
                         rval = un->un_f_has_removable_media ? ENXIO : EIO;
                         SD_ERROR(SD_LOG_OPEN_CLOSE, un, "sdopen: "
                             "device not ready or invalid disk block value\n");
                         goto open_fail;
                 }
-#if defined(__i386) || defined(__amd64)
         } else {
                 uchar_t *cp;
                 /*
                  * x86 requires special nodelay handling, so that p0 is
                  * always defined and accessible.

@@ -10279,12 +9923,10 @@
                         mutex_exit(SD_MUTEX(un));
                         cmlb_invalidate(un->un_cmlbhandle,
                             (void *)SD_PATH_DIRECT);
                         mutex_enter(SD_MUTEX(un));
                 }
-
-#endif
         }
 
         if (otyp == OTYP_LYR) {
                 un->un_ocmap.lyropen[part]++;
         } else {

@@ -10324,10 +9966,16 @@
         }
 
         SD_TRACE(SD_LOG_OPEN_CLOSE, un, "sdopen: "
             "open of part %d type %d\n", part, otyp);
 
+        /*
+         * If we made it here, the disk is alive.
+         * Make sure it is set to normal state.
+         */
+        New_state(un, SD_STATE_NORMAL);
+
         mutex_exit(SD_MUTEX(un));
         if (!nodelay) {
                 sd_pm_exit(un);
         }
 

@@ -10352,10 +10000,11 @@
          */
         if (!nodelay) {
                 sd_pm_exit(un);
         }
 open_failed_with_pm:
+open_failed_not_attached:
         sema_v(&un->un_semoclose);
 
         mutex_enter(&sd_detach_mutex);
         un->un_opens_in_progress--;
         if (otyp == OTYP_LYR) {

@@ -10394,11 +10043,15 @@
         /* Validate the open type */
         if (otyp >= OTYPCNT) {
                 return (ENXIO);
         }
 
+        /* Hold the detach mutex to allow close to complete */
+        mutex_enter(&sd_detach_mutex);
+
         if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL) {
+                mutex_exit(&sd_detach_mutex);
                 return (ENXIO);
         }
 
         part = SDPART(dev);
         nodelay = flag & (FNDELAY | FNONBLOCK);

@@ -10412,12 +10065,13 @@
          */
         sema_p(&un->un_semoclose);
 
         mutex_enter(SD_MUTEX(un));
 
-        /* Don't proceed if power is being changed. */
-        while (un->un_state == SD_STATE_PM_CHANGING) {
+        /* Don't proceed if power is being changed or we're still attaching. */
+        while ((un->un_state == SD_STATE_PM_CHANGING) ||
+            (un->un_state == SD_STATE_ATTACHING)) {
                 cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
         }
 
         if (un->un_exclopen & (1 << part)) {
                 un->un_exclopen &= ~(1 << part);

@@ -10454,12 +10108,11 @@
                         }
                         mutex_exit(SD_MUTEX(un));
                         cmlb_invalidate(un->un_cmlbhandle,
                             (void *)SD_PATH_DIRECT);
                         mutex_enter(SD_MUTEX(un));
-
-                } else {
+                } else if (un->un_state != SD_STATE_ATTACH_FAILED) {
                         /*
                          * Flush any outstanding writes in NVRAM cache.
                          * Note: SYNCHRONIZE CACHE is an optional SCSI-2
                          * cmd, it may not work for non-Pluto devices.
                          * SYNCHRONIZE CACHE is not required for removables,

@@ -10469,22 +10122,15 @@
                          * the only command issued here that requires the
                          * drive be powered up, only do the power up before
                          * sending the Sync Cache command. If additional
                          * commands are added which require a powered up
                          * drive, the following sequence may have to change.
-                         *
-                         * And finally, note that parallel SCSI on SPARC
-                         * only issues a Sync Cache to DVD-RAM, a newly
-                         * supported device.
                          */
-#if defined(__i386) || defined(__amd64)
-                        if ((un->un_f_sync_cache_supported &&
+                        if (!DEVI_IS_GONE(SD_DEVINFO(un)) &&
+                            ((un->un_f_sync_cache_supported &&
                             un->un_f_sync_cache_required) ||
-                            un->un_f_dvdram_writable_device == TRUE) {
-#else
-                        if (un->un_f_dvdram_writable_device == TRUE) {
-#endif
+                            un->un_f_dvdram_writable_device == TRUE)) {
                                 mutex_exit(SD_MUTEX(un));
                                 if (sd_pm_entry(un) == DDI_SUCCESS) {
                                         rval =
                                             sd_send_scsi_SYNCHRONIZE_CACHE(un,
                                             NULL);

@@ -10530,10 +10176,16 @@
                                 }
                                 mutex_enter(SD_MUTEX(un));
                         }
 
                         /*
+                         * Pardon a device that is currently in failfast
+                         * active state, to not bias a future open.
+                         */
+                        un->un_failfast_state = SD_FAILFAST_INACTIVE;
+
+                        /*
                          * If a device has removable media, invalidate all
                          * parameters related to media, such as geometry,
                          * blocksize, and blockcount.
                          */
                         if (un->un_f_has_removable_media) {

@@ -10566,19 +10218,14 @@
         }
 
         mutex_exit(SD_MUTEX(un));
         sema_v(&un->un_semoclose);
 
-        if (otyp == OTYP_LYR) {
-                mutex_enter(&sd_detach_mutex);
-                /*
-                 * The detach routine may run when the layer count
-                 * drops to zero.
-                 */
+        if (otyp == OTYP_LYR)
                 un->un_layer_count--;
+
                 mutex_exit(&sd_detach_mutex);
-        }
 
         return (rval);
 }
 
 

@@ -10671,11 +10318,11 @@
                  * Do a test unit ready to clear any unit attention from non-cd
                  * devices.
                  */
                 mutex_exit(SD_MUTEX(un));
 
-                status = sd_send_scsi_TEST_UNIT_READY(ssc, 0);
+                status = sd_send_scsi_TEST_UNIT_READY(ssc, SD_DONT_RETRY_TUR);
                 if (status != 0) {
                         sd_ssc_assessment(ssc, SD_FMT_IGNORE);
                 }
 
                 mutex_enter(SD_MUTEX(un));

@@ -10877,17 +10524,27 @@
         struct sd_lun   *un = NULL;
         int             secmask;
         int             err = 0;
         sd_ssc_t        *ssc;
 
-        if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL) {
+        if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL ||
+            DEVI_IS_GONE(SD_DEVINFO(un)))
                 return (ENXIO);
-        }
 
         ASSERT(!mutex_owned(SD_MUTEX(un)));
 
+        mutex_enter(SD_MUTEX(un));
+        while (un->un_state == SD_STATE_ATTACHING)
+                cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
 
+        if (un->un_state == SD_STATE_ATTACH_FAILED) {
+                mutex_exit(SD_MUTEX(un));
+                SD_ERROR(SD_LOG_READ_WRITE, un, "sdread: attach failed\n");
+                return (EIO);
+        }
+        mutex_exit(SD_MUTEX(un));
+
         if (!SD_IS_VALID_LABEL(un) && !ISCD(un)) {
                 mutex_enter(SD_MUTEX(un));
                 /*
                  * Because the call to sd_ready_and_valid will issue I/O we
                  * must wait here if either the device is suspended or

@@ -10895,10 +10552,12 @@
                  */
                 while ((un->un_state == SD_STATE_SUSPENDED) ||
                     (un->un_state == SD_STATE_PM_CHANGING)) {
                         cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
                 }
+
+                SD_BAIL_CHECK(un);
                 un->un_ncmds_in_driver++;
                 mutex_exit(SD_MUTEX(un));
 
                 /* Initialize sd_ssc_t for internal uscsi commands */
                 ssc = sd_ssc_init(un);

@@ -10909,10 +10568,12 @@
                 }
                 sd_ssc_fini(ssc);
 
                 mutex_enter(SD_MUTEX(un));
                 un->un_ncmds_in_driver--;
+                if (un->un_f_detach_waiting)
+                        cv_signal(&un->un_detach_cv);
                 ASSERT(un->un_ncmds_in_driver >= 0);
                 mutex_exit(SD_MUTEX(un));
                 if (err != 0)
                         return (err);
         }

@@ -10968,16 +10629,27 @@
         struct sd_lun   *un = NULL;
         int             secmask;
         int             err = 0;
         sd_ssc_t        *ssc;
 
-        if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL) {
+        if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL ||
+            DEVI_IS_GONE(SD_DEVINFO(un)))
                 return (ENXIO);
-        }
 
         ASSERT(!mutex_owned(SD_MUTEX(un)));
 
+        mutex_enter(SD_MUTEX(un));
+        while (un->un_state == SD_STATE_ATTACHING)
+                cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
+
+        if (un->un_state == SD_STATE_ATTACH_FAILED) {
+                mutex_exit(SD_MUTEX(un));
+                SD_ERROR(SD_LOG_READ_WRITE, un, "sdwrite: attach failed\n");
+                return (EIO);
+        }
+        mutex_exit(SD_MUTEX(un));
+
         if (!SD_IS_VALID_LABEL(un) && !ISCD(un)) {
                 mutex_enter(SD_MUTEX(un));
                 /*
                  * Because the call to sd_ready_and_valid will issue I/O we
                  * must wait here if either the device is suspended or

@@ -10985,10 +10657,12 @@
                  */
                 while ((un->un_state == SD_STATE_SUSPENDED) ||
                     (un->un_state == SD_STATE_PM_CHANGING)) {
                         cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
                 }
+
+                SD_BAIL_CHECK(un);
                 un->un_ncmds_in_driver++;
                 mutex_exit(SD_MUTEX(un));
 
                 /* Initialize sd_ssc_t for internal uscsi commands */
                 ssc = sd_ssc_init(un);

@@ -11000,10 +10674,12 @@
                 sd_ssc_fini(ssc);
 
                 mutex_enter(SD_MUTEX(un));
                 un->un_ncmds_in_driver--;
                 ASSERT(un->un_ncmds_in_driver >= 0);
+                if (un->un_f_detach_waiting)
+                        cv_signal(&un->un_detach_cv);
                 mutex_exit(SD_MUTEX(un));
                 if (err != 0)
                         return (err);
         }
 

@@ -11058,16 +10734,27 @@
         struct uio      *uio = aio->aio_uio;
         int             secmask;
         int             err = 0;
         sd_ssc_t        *ssc;
 
-        if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL) {
+        if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL ||
+            DEVI_IS_GONE(SD_DEVINFO(un)))
                 return (ENXIO);
-        }
 
         ASSERT(!mutex_owned(SD_MUTEX(un)));
 
+        mutex_enter(SD_MUTEX(un));
+        while (un->un_state == SD_STATE_ATTACHING)
+                cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
+
+        if (un->un_state == SD_STATE_ATTACH_FAILED) {
+                mutex_exit(SD_MUTEX(un));
+                SD_ERROR(SD_LOG_READ_WRITE, un, "sdaread: attach failed\n");
+                return (EIO);
+        }
+        mutex_exit(SD_MUTEX(un));
+
         if (!SD_IS_VALID_LABEL(un) && !ISCD(un)) {
                 mutex_enter(SD_MUTEX(un));
                 /*
                  * Because the call to sd_ready_and_valid will issue I/O we
                  * must wait here if either the device is suspended or

@@ -11075,10 +10762,12 @@
                  */
                 while ((un->un_state == SD_STATE_SUSPENDED) ||
                     (un->un_state == SD_STATE_PM_CHANGING)) {
                         cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
                 }
+
+                SD_BAIL_CHECK(un);
                 un->un_ncmds_in_driver++;
                 mutex_exit(SD_MUTEX(un));
 
                 /* Initialize sd_ssc_t for internal uscsi commands */
                 ssc = sd_ssc_init(un);

@@ -11090,10 +10779,12 @@
                 sd_ssc_fini(ssc);
 
                 mutex_enter(SD_MUTEX(un));
                 un->un_ncmds_in_driver--;
                 ASSERT(un->un_ncmds_in_driver >= 0);
+                if (un->un_f_detach_waiting)
+                        cv_signal(&un->un_detach_cv);
                 mutex_exit(SD_MUTEX(un));
                 if (err != 0)
                         return (err);
         }
 

@@ -11148,16 +10839,28 @@
         struct uio      *uio = aio->aio_uio;
         int             secmask;
         int             err = 0;
         sd_ssc_t        *ssc;
 
-        if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL) {
+        if ((un = ddi_get_soft_state(sd_state, SDUNIT(dev))) == NULL ||
+            DEVI_IS_GONE(SD_DEVINFO(un)))
                 return (ENXIO);
-        }
 
         ASSERT(!mutex_owned(SD_MUTEX(un)));
 
+        mutex_enter(SD_MUTEX(un));
+        while (un->un_state == SD_STATE_ATTACHING)
+                cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
+
+        if (un->un_state == SD_STATE_ATTACH_FAILED) {
+                mutex_exit(SD_MUTEX(un));
+                SD_ERROR(SD_LOG_READ_WRITE, un,
+                    "sdawrite: attach failed\n");
+                return (EIO);
+        }
+        mutex_exit(SD_MUTEX(un));
+
         if (!SD_IS_VALID_LABEL(un) && !ISCD(un)) {
                 mutex_enter(SD_MUTEX(un));
                 /*
                  * Because the call to sd_ready_and_valid will issue I/O we
                  * must wait here if either the device is suspended or

@@ -11165,10 +10868,12 @@
                  */
                 while ((un->un_state == SD_STATE_SUSPENDED) ||
                     (un->un_state == SD_STATE_PM_CHANGING)) {
                         cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
                 }
+
+                SD_BAIL_CHECK(un);
                 un->un_ncmds_in_driver++;
                 mutex_exit(SD_MUTEX(un));
 
                 /* Initialize sd_ssc_t for internal uscsi commands */
                 ssc = sd_ssc_init(un);

@@ -11180,10 +10885,12 @@
                 sd_ssc_fini(ssc);
 
                 mutex_enter(SD_MUTEX(un));
                 un->un_ncmds_in_driver--;
                 ASSERT(un->un_ncmds_in_driver >= 0);
+                if (un->un_f_detach_waiting)
+                        cv_signal(&un->un_detach_cv);
                 mutex_exit(SD_MUTEX(un));
                 if (err != 0)
                         return (err);
         }
 

@@ -11309,44 +11016,10 @@
  *     layer must acquire & release the SD_MUTEX(un) as needed.
  */
 
 
 /*
- * Create taskq for all targets in the system. This is created at
- * _init(9E) and destroyed at _fini(9E).
- *
- * Note: here we set the minalloc to a reasonably high number to ensure that
- * we will have an adequate supply of task entries available at interrupt time.
- * This is used in conjunction with the TASKQ_PREPOPULATE flag in
- * sd_create_taskq().  Since we do not want to sleep for allocations at
- * interrupt time, set maxalloc equal to minalloc. That way we will just fail
- * the command if we ever try to dispatch more than SD_TASKQ_MAXALLOC taskq
- * requests any one instant in time.
- */
-#define SD_TASKQ_NUMTHREADS     8
-#define SD_TASKQ_MINALLOC       256
-#define SD_TASKQ_MAXALLOC       256
-
-static taskq_t  *sd_tq = NULL;
-_NOTE(SCHEME_PROTECTS_DATA("stable data", sd_tq))
-
-static int      sd_taskq_minalloc = SD_TASKQ_MINALLOC;
-static int      sd_taskq_maxalloc = SD_TASKQ_MAXALLOC;
-
-/*
- * The following task queue is being created for the write part of
- * read-modify-write of non-512 block size devices.
- * Limit the number of threads to 1 for now. This number has been chosen
- * considering the fact that it applies only to dvd ram drives/MO drives
- * currently. Performance for which is not main criteria at this stage.
- * Note: It needs to be explored if we can use a single taskq in future
- */
-#define SD_WMR_TASKQ_NUMTHREADS 1
-static taskq_t  *sd_wmr_tq = NULL;
-_NOTE(SCHEME_PROTECTS_DATA("stable data", sd_wmr_tq))
-
-/*
  *    Function: sd_taskq_create
  *
  * Description: Create taskq thread(s) and preallocate task entries
  *
  * Return Code: Returns a pointer to the allocated taskq_t.

@@ -11420,25 +11093,21 @@
 
 static int
 sdstrategy(struct buf *bp)
 {
         struct sd_lun *un;
+        int error = EIO;
 
-        un = ddi_get_soft_state(sd_state, SD_GET_INSTANCE_FROM_BUF(bp));
-        if (un == NULL) {
-                bioerror(bp, EIO);
-                bp->b_resid = bp->b_bcount;
-                biodone(bp);
-                return (0);
-        }
+        if ((un = ddi_get_soft_state(sd_state,
+            SD_GET_INSTANCE_FROM_BUF(bp))) == NULL)
+                goto fail;
 
-        /* As was done in the past, fail new cmds. if state is dumping. */
-        if (un->un_state == SD_STATE_DUMPING) {
-                bioerror(bp, ENXIO);
-                bp->b_resid = bp->b_bcount;
-                biodone(bp);
-                return (0);
+        /* Fail new cmds if state is dumping or device is gone */
+        if (un->un_state == SD_STATE_DUMPING ||
+            DEVI_IS_GONE(SD_DEVINFO(un))) {
+                error = ENXIO;
+                goto fail;
         }
 
         ASSERT(!mutex_owned(SD_MUTEX(un)));
 
         /*

@@ -11451,14 +11120,26 @@
         /*
          * Must wait here if either the device is suspended or
          * if it's power level is changing.
          */
         while ((un->un_state == SD_STATE_SUSPENDED) ||
-            (un->un_state == SD_STATE_PM_CHANGING)) {
+            (un->un_state == SD_STATE_PM_CHANGING) ||
+            (un->un_state == SD_STATE_ATTACHING)) {
                 cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
         }
 
+        if (un->un_state == SD_STATE_ATTACH_FAILED) {
+                mutex_exit(SD_MUTEX(un));
+                SD_ERROR(SD_LOG_READ_WRITE, un,
+                    "sdstrategy: attach failed\n");
+                goto fail;
+        }
+        if (un->un_detach_count != 0) {
+                mutex_exit(SD_MUTEX(un));
+                goto fail;
+        }
+
         un->un_ncmds_in_driver++;
 
         /*
          * atapi: Since we are running the CD for now in PIO mode we need to
          * call bp_mapin here to avoid bp_mapin called interrupt context under

@@ -11482,13 +11163,18 @@
          * call sd_xbuf_strategy().  We just want to return the
          * result of ddi_xbuf_qstrategy so that we have an opt-
          * imized tail call which saves us a stack frame.
          */
         return (ddi_xbuf_qstrategy(bp, un->un_xbuf_attr));
+
+fail:
+        bioerror(bp, error);
+        bp->b_resid = bp->b_bcount;
+        biodone(bp);
+        return (0);
 }
 
-
 /*
  *    Function: sd_xbuf_strategy
  *
  * Description: Function for initiating IO operations via the
  *              ddi_xbuf_qstrategy() mechanism.

@@ -11667,11 +11353,11 @@
         uchar_t                 cmd;
 
         ASSERT(bp != NULL);
 
         un = ddi_get_soft_state(sd_state, SD_GET_INSTANCE_FROM_BUF(bp));
-        if (un == NULL) {
+        if (un == NULL || DEVI_IS_GONE(SD_DEVINFO(un))) {
                 bioerror(bp, EIO);
                 bp->b_resid = bp->b_bcount;
                 biodone(bp);
                 return (0);
         }

@@ -11704,10 +11390,19 @@
 
         if ((bp->b_flags & B_WRITE) && (bp->b_bcount != 0) &&
             (cmd != SCMD_MODE_SELECT) && (cmd != SCMD_MODE_SELECT_G1))
                 un->un_f_sync_cache_required = TRUE;
 
+        if (sd_failfast_enable & SD_FAILFAST_ENABLE_FAIL_USCSI) {
+                /*
+                 * If there are outstanding commands, treat all
+                 * USCSI commands as if they have B_FAILFAST set.
+                 */
+                if (un->un_ncmds_in_driver != 1)
+                        bp->b_flags |= B_FAILFAST;
+        }
+
         mutex_exit(SD_MUTEX(un));
 
         switch (uip->ui_flags) {
         case SD_PATH_DIRECT:
                 chain_type = SD_CHAIN_DIRECT;

@@ -11778,13 +11473,12 @@
         struct sd_lun   *un;
         sd_ssc_t        *ssc;
         int             rval;
 
         un = ddi_get_soft_state(sd_state, SDUNIT(dev));
-        if (un == NULL) {
+        if (un == NULL || DEVI_IS_GONE(SD_DEVINFO(un)))
                 return (ENXIO);
-        }
 
         /*
          * Using sd_ssc_send to handle uscsi cmd
          */
         ssc = sd_ssc_init(un);

@@ -11947,10 +11641,11 @@
 {
         struct sd_uscsi_info    *uip;
         struct uscsi_cmd        *uscmd;
         struct sd_lun           *un;
         dev_t                   dev;
+        dev_info_t              *dip = SD_DEVINFO(ssc->ssc_un);
 
         int     format = 0;
         int     rval;
 
         ASSERT(ssc != NULL);

@@ -11990,10 +11685,11 @@
          * if USCSI_PMFAILFAST is set and un is in low power, fail the
          * command immediately.
          */
         mutex_enter(SD_MUTEX(un));
         mutex_enter(&un->un_pm_mutex);
+
         if ((uscmd->uscsi_flags & USCSI_PMFAILFAST) &&
             SD_DEVICE_IS_IN_LOW_POWER(un)) {
                 SD_TRACE(SD_LOG_IO, un, "sd_ssc_send:"
                     "un:0x%p is in low power\n", un);
                 mutex_exit(&un->un_pm_mutex);

@@ -12058,10 +11754,14 @@
         uscmd->uscsi_flags &= ~USCSI_NOINTR;
 
         dev = SD_GET_DEV(un);
         rval = scsi_uscsi_handle_cmd(dev, dataspace, uscmd,
             sd_uscsi_strategy, NULL, uip);
+        if (DEVI_IS_GONE(dip)) {
+                cmn_err(CE_WARN, "%s-%d: device is gone!", __func__, __LINE__);
+                return (ENXIO);
+        }
 
         /*
          * mark ssc_flags right after handle_cmd to make sure
          * the uscsi has been sent
          */

@@ -12426,10 +12126,12 @@
                  */
                 un->un_pm_idle_time = gethrtime();
 
                 un->un_ncmds_in_driver--;
                 ASSERT(un->un_ncmds_in_driver >= 0);
+                if (un->un_f_detach_waiting)
+                        cv_signal(&un->un_detach_cv);
                 SD_INFO(SD_LOG_IO, un,
                     "sd_buf_iodone: un_ncmds_in_driver = %ld\n",
                     un->un_ncmds_in_driver);
 
                 mutex_exit(SD_MUTEX(un));

@@ -12476,10 +12178,12 @@
          */
         un->un_pm_idle_time = gethrtime();
 
         un->un_ncmds_in_driver--;
         ASSERT(un->un_ncmds_in_driver >= 0);
+        if (un->un_f_detach_waiting)
+                cv_signal(&un->un_detach_cv);
         SD_INFO(SD_LOG_IO, un, "sd_uscsi_iodone: un_ncmds_in_driver = %ld\n",
             un->un_ncmds_in_driver);
 
         mutex_exit(SD_MUTEX(un));
 

@@ -12873,14 +12577,12 @@
         if ((un->un_tgt_blocksize == DEV_BSIZE && !un->un_f_enable_rmw) ||
             (bp->b_bcount == 0)) {
                 goto done;
         }
 
-#if defined(__i386) || defined(__amd64)
         /* We do not support non-block-aligned transfers for ROD devices */
         ASSERT(!ISROD(un));
-#endif
 
         xp = SD_GET_XBUF(bp);
         ASSERT(xp != NULL);
 
         SD_INFO(SD_LOG_IO_RMMEDIA, un, "sd_mapblocksize_iostart: "

@@ -13575,16 +13277,14 @@
          * Use CDB_GROUP1 commands for most devices except for
          * parallel SCSI fixed drives in which case we get better
          * performance using CDB_GROUP0 commands (where applicable).
          */
         un->un_mincdb = SD_CDB_GROUP1;
-#if !defined(__fibre)
         if (!un->un_f_is_fibre && !un->un_f_cfg_is_atapi && !ISROD(un) &&
             !un->un_f_has_removable_media) {
                 un->un_mincdb = SD_CDB_GROUP0;
         }
-#endif
 
         /*
          * Try to read the max-cdb-length supported by HBA.
          */
         un->un_max_hba_cdb = scsi_ifgetcap(SD_ADDRESS(un), "max-cdb-length", 1);

@@ -13618,11 +13318,11 @@
 #endif
 
         un->un_status_len = (int)((un->un_f_arq_enabled == TRUE)
             ? sizeof (struct scsi_arq_status) : 1);
         if (!ISCD(un))
-                un->un_cmd_timeout = (ushort_t)sd_io_time;
+                un->un_cmd_timeout = (ushort_t)un->un_io_time;
         un->un_uscsi_timeout = ((ISCD(un)) ? 2 : 1) * un->un_cmd_timeout;
 }
 
 
 /*

@@ -13670,11 +13370,10 @@
         SD_TRACE(SD_LOG_IO_CORE, un,
             "sd_initpkt_for_buf: entry: buf:0x%p\n", bp);
 
         mutex_exit(SD_MUTEX(un));
 
-#if defined(__i386) || defined(__amd64) /* DMAFREE for x86 only */
         if (xp->xb_pkt_flags & SD_XB_DMA_FREED) {
                 /*
                  * Already have a scsi_pkt -- just need DMA resources.
                  * We must recompute the CDB in case the mapping returns
                  * a nonzero pkt_resid.

@@ -13686,11 +13385,10 @@
                 ASSERT(xp->xb_pktp != NULL);
                 pktp = xp->xb_pktp;
         } else {
                 pktp = NULL;
         }
-#endif /* __i386 || __amd64 */
 
         startblock = xp->xb_blkno;      /* Absolute block num. */
         blockcount = SD_BYTES2TGTBLOCKS(un, bp->b_bcount);
 
         cmd_flags = un->un_pkt_flags | (xp->xb_pkt_flags & SD_XB_INITPKT_MASK);

@@ -13734,13 +13432,11 @@
                 *pktpp = pktp;
 
                 SD_TRACE(SD_LOG_IO_CORE, un,
                     "sd_initpkt_for_buf: exit: buf:0x%p\n", bp);
 
-#if defined(__i386) || defined(__amd64) /* DMAFREE for x86 only */
                 xp->xb_pkt_flags &= ~SD_XB_DMA_FREED;
-#endif
 
                 mutex_enter(SD_MUTEX(un));
                 return (SD_PKT_ALLOC_SUCCESS);
 
         }

@@ -14400,20 +14096,15 @@
         if (bp->b_flags & (B_PAGEIO | B_PHYS)) {
                 bp_mapin(bp);
         }
 
         bflags &= (B_READ | B_WRITE);
-#if defined(__i386) || defined(__amd64)
         new_bp = getrbuf(KM_SLEEP);
         new_bp->b_un.b_addr = kmem_zalloc(datalen, KM_SLEEP);
         new_bp->b_bcount = datalen;
         new_bp->b_flags = bflags |
             (bp->b_flags & ~(B_PAGEIO | B_PHYS | B_REMAPPED | B_SHADOW));
-#else
-        new_bp = scsi_alloc_consistent_buf(SD_ADDRESS(un), NULL,
-            datalen, bflags, SLEEP_FUNC, NULL);
-#endif
         new_bp->av_forw = NULL;
         new_bp->av_back = NULL;
         new_bp->b_dev   = bp->b_dev;
         new_bp->b_blkno = blkno;
         new_bp->b_iodone = func;

@@ -14498,32 +14189,19 @@
 
         ASSERT(bp != NULL);
         xp = SD_GET_XBUF(bp);
         ASSERT(xp != NULL);
 
-#if defined(__sparc)
         /*
-         * Call bp_mapout() before freeing the buf,  in case a lower
-         * layer or HBA  had done a bp_mapin().  we must do this here
-         * as we are the "originator" of the shadow buf.
-         */
-        bp_mapout(bp);
-#endif
-
-        /*
          * Null out b_iodone before freeing the bp, to ensure that the driver
          * never gets confused by a stale value in this field. (Just a little
          * extra defensiveness here.)
          */
         bp->b_iodone = NULL;
 
-#if defined(__i386) || defined(__amd64)
         kmem_free(bp->b_un.b_addr, bp->b_bcount);
         freerbuf(bp);
-#else
-        scsi_free_consistent_buf(bp);
-#endif
 
         kmem_free(xp, sizeof (struct sd_xbuf));
 }
 
 

@@ -14745,13 +14423,11 @@
 sd_start_cmds(struct sd_lun *un, struct buf *immed_bp)
 {
         struct  sd_xbuf *xp;
         struct  buf     *bp;
         void    (*statp)(kstat_io_t *);
-#if defined(__i386) || defined(__amd64) /* DMAFREE for x86 only */
         void    (*saved_statp)(kstat_io_t *);
-#endif
         int     rval;
         struct sd_fm_internal *sfip = NULL;
 
         ASSERT(un != NULL);
         ASSERT(mutex_owned(SD_MUTEX(un)));

@@ -14758,14 +14434,30 @@
         ASSERT(un->un_ncmds_in_transport >= 0);
         ASSERT(un->un_throttle >= 0);
 
         SD_TRACE(SD_LOG_IO_CORE | SD_LOG_ERROR, un, "sd_start_cmds: entry\n");
 
+        /*
+         * If device is currently retired, we should abort all pending I/O.
+         */
+        if (DEVI(un->un_sd->sd_dev)->devi_flags & DEVI_RETIRED) {
+                if (immed_bp) {
+                        immed_bp->b_resid = immed_bp->b_bcount;
+                        bioerror(immed_bp, ENXIO);
+                        biodone(immed_bp);
+                }
+                /* abort in-flight IO */
+                (void) scsi_abort(SD_ADDRESS(un), NULL);
+                /* abort pending IO */
+                un->un_failfast_state = SD_FAILFAST_ACTIVE;
+                un->un_failfast_bp = NULL;
+                sd_failfast_flushq(un, B_TRUE);
+                return;
+        }
+
         do {
-#if defined(__i386) || defined(__amd64) /* DMAFREE for x86 only */
                 saved_statp = NULL;
-#endif
 
                 /*
                  * If we are syncing or dumping, fail the command to
                  * avoid recursively calling back into scsi_transport().
                  * The dump I/O itself uses a separate code path so this

@@ -14818,13 +14510,11 @@
                                 if ((un->un_retry_statp == kstat_waitq_enter) ||
                                     (un->un_retry_statp ==
                                     kstat_runq_back_to_waitq)) {
                                         statp = kstat_waitq_to_runq;
                                 }
-#if defined(__i386) || defined(__amd64) /* DMAFREE for x86 only */
                                 saved_statp = un->un_retry_statp;
-#endif
                                 un->un_retry_statp = NULL;
 
                                 SD_TRACE(SD_LOG_IO | SD_LOG_ERROR, un,
                                     "sd_start_cmds: un:0x%p: GOT retry_bp:0x%p "
                                     "un_throttle:%d un_ncmds_in_transport:%d\n",

@@ -14897,31 +14587,31 @@
                  * If state is SD_STATE_PM_CHANGING then this command is
                  * part of the device power control and the state must
                  * not be put back to normal. Doing so would would
                  * allow new commands to proceed when they shouldn't,
                  * the device may be going off.
+                 *
+                 * Similarly, if the state is SD_STATE_ATTACHING we should
+                 * not set it to SD_STATE_NORMAL to avoid corruption.
                  */
                 if ((un->un_state != SD_STATE_SUSPENDED) &&
-                    (un->un_state != SD_STATE_PM_CHANGING)) {
+                    (un->un_state != SD_STATE_PM_CHANGING) &&
+                    (un->un_state != SD_STATE_ATTACHING)) {
                         New_state(un, SD_STATE_NORMAL);
                 }
 
                 xp = SD_GET_XBUF(bp);
                 ASSERT(xp != NULL);
 
-#if defined(__i386) || defined(__amd64) /* DMAFREE for x86 only */
                 /*
                  * Allocate the scsi_pkt if we need one, or attach DMA
                  * resources if we have a scsi_pkt that needs them. The
                  * latter should only occur for commands that are being
                  * retried.
                  */
                 if ((xp->xb_pktp == NULL) ||
                     ((xp->xb_pkt_flags & SD_XB_DMA_FREED) != 0)) {
-#else
-                if (xp->xb_pktp == NULL) {
-#endif
                         /*
                          * There is no scsi_pkt allocated for this buf. Call
                          * the initpkt function to allocate & init one.
                          *
                          * The scsi_init_pkt runout callback functionality is

@@ -14985,12 +14675,10 @@
                                  * sent as an immed_bp (which we just fail).
                                  */
                                 SD_TRACE(SD_LOG_IO_CORE | SD_LOG_ERROR, un,
                                     "sd_start_cmds: SD_PKT_ALLOC_FAILURE\n");
 
-#if defined(__i386) || defined(__amd64) /* DMAFREE for x86 only */
-
                                 if (bp == immed_bp) {
                                         /*
                                          * If SD_XB_DMA_FREED is clear, then
                                          * this is a failure to allocate a
                                          * scsi_pkt, and we must fail the

@@ -15058,16 +14746,10 @@
                                                     un, SD_RESTART_TIMEOUT);
                                         }
                                         goto exit;
                                 }
 
-#else
-                                if (bp == immed_bp) {
-                                        break;  /* Just fail the command */
-                                }
-#endif
-
                                 /* Add the buf back to the head of the waitq */
                                 bp->av_forw = un->un_waitq_headp;
                                 un->un_waitq_headp = bp;
                                 if (un->un_waitq_tailp == NULL) {
                                         un->un_waitq_tailp = bp;

@@ -15084,23 +14766,23 @@
                                     "SD_PKT_ALLOC_FAILURE_NO_DMA\n");
                                 break;
 
                         case SD_PKT_ALLOC_FAILURE_PKT_TOO_SMALL:
                                 /*
-                                 * Note:x86: Partial DMA mapping not supported
-                                 * for USCSI commands, and all the needed DMA
-                                 * resources were not allocated.
+                                 * Partial DMA mapping not supported for USCSI
+                                 * commands, and all the needed DMA resources
+                                 * were not allocated.
                                  */
                                 SD_TRACE(SD_LOG_IO_CORE | SD_LOG_ERROR, un,
                                     "sd_start_cmds: "
                                     "SD_PKT_ALLOC_FAILURE_PKT_TOO_SMALL\n");
                                 break;
 
                         case SD_PKT_ALLOC_FAILURE_CDB_TOO_SMALL:
                                 /*
-                                 * Note:x86: Request cannot fit into CDB based
-                                 * on lba and len.
+                                 * Request cannot fit into CDB based on lba
+                                 * and len.
                                  */
                                 SD_TRACE(SD_LOG_IO_CORE | SD_LOG_ERROR, un,
                                     "sd_start_cmds: "
                                     "SD_PKT_ALLOC_FAILURE_CDB_TOO_SMALL\n");
                                 break;

@@ -15135,10 +14817,13 @@
                         xp->xb_pktp->pkt_flags |= FLAG_HEAD;
                 }
 
                 un->un_ncmds_in_transport++;
                 SD_UPDATE_KSTATS(un, statp, bp);
+                /* The start time MAY be overriden by the HBA driver. */
+                xp->xb_pktp->pkt_start = gethrtime();
+                xp->xb_pktp->pkt_stop = 0;
 
                 /*
                  * Call scsi_transport() to send the command to the target.
                  * According to SCSA architecture, we must drop the mutex here
                  * before calling scsi_transport() in order to avoid deadlock.

@@ -15148,10 +14833,18 @@
                  */
                 SD_TRACE(SD_LOG_IO_CORE, un,
                     "sd_start_cmds: calling scsi_transport()\n");
                 DTRACE_PROBE1(scsi__transport__dispatch, struct buf *, bp);
 
+#ifdef SD_FAULT_INJECTION
+                /*
+                 * Packet is ready for submission to the HBA. Perform HBA-based
+                 * fault-injection.
+                 */
+                sd_prefaultinjection(xp->xb_pktp);
+#endif /* SD_FAULT_INJECTION */
+
                 mutex_exit(SD_MUTEX(un));
                 rval = scsi_transport(xp->xb_pktp);
                 mutex_enter(SD_MUTEX(un));
 
                 SD_TRACE(SD_LOG_IO_CORE | SD_LOG_ERROR, un,

@@ -15165,26 +14858,48 @@
 
                 case TRAN_BUSY:
                         un->un_ncmds_in_transport--;
                         ASSERT(un->un_ncmds_in_transport >= 0);
 
+#ifdef SD_FAULT_INJECTION
                         /*
+                         * If the packet was rejected during active fault
+                         * injection session, move to the next fault slot
+                         * and reset packet flag related to rejection.
+                         */
+                        if (sd_fault_injection_on) {
+                                uint_t i = un->sd_fi_fifo_start;
+
+                                if (un->sd_fi_fifo_tran[i] != NULL) {
+                                        kmem_free(un->sd_fi_fifo_tran[i],
+                                            sizeof (struct sd_fi_tran));
+                                        un->sd_fi_fifo_tran[i] = NULL;
+                                }
+                                un->sd_fi_fifo_start++;
+                        }
+
+                        if (xp->xb_pktp->pkt_flags & FLAG_PKT_BUSY) {
+                                xp->xb_pktp->pkt_flags &= ~FLAG_PKT_BUSY;
+                        }
+#endif /* SD_FAULT_INJECTION */
+
+                        /*
                          * Don't retry request sense, the sense data
                          * is lost when another request is sent.
                          * Free up the rqs buf and retry
                          * the original failed cmd.  Update kstat.
                          */
-                        if (bp == un->un_rqs_bp) {
+                        if ((un->un_ncmds_in_transport > 0) &&
+                            (bp == un->un_rqs_bp)) {
                                 SD_UPDATE_KSTATS(un, kstat_runq_exit, bp);
                                 bp = sd_mark_rqs_idle(un, xp);
                                 sd_retry_command(un, bp, SD_RETRIES_STANDARD,
                                     NULL, NULL, EIO, un->un_busy_timeout / 500,
                                     kstat_waitq_enter);
                                 goto exit;
                         }
 
-#if defined(__i386) || defined(__amd64) /* DMAFREE for x86 only */
                         /*
                          * Free the DMA resources for the  scsi_pkt. This will
                          * allow mpxio to select another path the next time
                          * we call scsi_transport() with this scsi_pkt.
                          * See sdintr() for the rationalization behind this.

@@ -15193,11 +14908,10 @@
                             ((xp->xb_pkt_flags & SD_XB_USCSICMD) == 0) &&
                             ((xp->xb_pktp->pkt_flags & FLAG_SENSING) == 0)) {
                                 scsi_dmafree(xp->xb_pktp);
                                 xp->xb_pkt_flags |= SD_XB_DMA_FREED;
                         }
-#endif
 
                         if (SD_IS_DIRECT_PRIORITY(SD_GET_XBUF(bp))) {
                                 /*
                                  * Commands that are SD_PATH_DIRECT_PRIORITY
                                  * are for error recovery situations. These do

@@ -15240,11 +14954,11 @@
                                 sd_reduce_throttle(un, SD_THROTTLE_TRAN_BUSY);
                         }
 
                         /*
                          * Set up the bp to be tried again 10 ms later.
-                         * Note:x86: Is there a timeout value in the sd_lun
+                         * XXX Is there a timeout value in the sd_lun
                          * for this condition?
                          */
                         sd_set_retry_bp(un, bp, un->un_busy_timeout / 500,
                             kstat_runq_back_to_waitq);
                         goto exit;

@@ -15369,11 +15083,11 @@
                          * transfer, try sending it
                          */
                         sd_retry_command(un, bp, SD_RETRIES_NOCHECK,
                             NULL, NULL, 0, (clock_t)0, NULL);
                         sd_start_cmds(un, NULL);
-                        return; /* Note:x86: need a return here? */
+                        return; /* XXX need a return here? */
                 }
         }
 
         /*
          * If this is the failfast bp, clear it from un_failfast_bp. This

@@ -15585,10 +15299,16 @@
  *                 Optionally may be bitwise-OR'ed with SD_RETRIES_ISOLATE
  *                 if the check should be made to see of FLAG_ISOLATE is set
  *                 in the pkt. If FLAG_ISOLATE is set, then the command is
  *                 not retried, it is simply failed.
  *
+ *                 Optionally may be bitwise-OR'ed with SD_RETRIES_FAILFAST
+ *                 to indicate a retry following a command timeout, and check
+ *                 if the target should transition to failfast pending or
+ *                 failfast active. If the buf has B_FAILFAST set, the
+ *                 command should be failed when failfast is active.
+ *
  *              user_funcp - Ptr to function to call before dispatching the
  *                 command. May be NULL if no action needs to be performed.
  *                 (Primarily intended for printing messages.)
  *
  *              user_arg - Optional argument to be passed along to

@@ -15689,10 +15409,27 @@
                 if ((pktp->pkt_flags & FLAG_ISOLATE) != 0) {
                         goto fail_command;
                 }
         }
 
+        if (sd_failfast_enable & (SD_FAILFAST_ENABLE_FAIL_RETRIES |
+            SD_FAILFAST_ENABLE_FAIL_ALL_RETRIES)) {
+                if (sd_failfast_enable & SD_FAILFAST_ENABLE_FAIL_ALL_RETRIES) {
+                        /*
+                         * Fail ALL retries when in active failfast state,
+                         * regardless of reason.
+                         */
+                        if (un->un_failfast_state == SD_FAILFAST_ACTIVE) {
+                                goto fail_command;
+                        }
+                }
+                /*
+                 * Treat bufs being retried as if they have the
+                 * B_FAILFAST flag set.
+                 */
+                bp->b_flags |= B_FAILFAST;
+        }
 
         /*
          * If SD_RETRIES_FAILFAST is set, it indicates that either a
          * command timeout or a selection timeout has occurred. This means
          * that we were unable to establish an kind of communication with

@@ -15740,11 +15477,11 @@
                                  * so enter active failfast state & flush
                                  * queues as appropriate.
                                  */
                                 un->un_failfast_state = SD_FAILFAST_ACTIVE;
                                 un->un_failfast_bp = NULL;
-                                sd_failfast_flushq(un);
+                                sd_failfast_flushq(un, B_FALSE);
 
                                 /*
                                  * Fail this bp now if B_FAILFAST set;
                                  * otherwise continue with retries. (It would
                                  * be pretty ironic if this bp succeeded on a

@@ -15781,11 +15518,17 @@
                  * communication with the target and subsequent commands
                  * and/or retries are likely to get through to the target,
                  * In this case we want to be aggressive about clearing
                  * the failfast state. Note that this does not affect
                  * the "failfast pending" condition.
+                 *
+                 * We limit this to retries that are not a side effect of an
+                 * unrelated event, as it would be unwise to clear failfast
+                 * active state when we see retries due to a reset.
                  */
+                if ((sd_failfast_enable & SD_FAILFAST_ENABLE_FORCE_INACTIVE) &&
+                    (retry_check_flag & SD_RETRIES_MASK) != SD_RETRIES_VICTIM)
                 un->un_failfast_state = SD_FAILFAST_INACTIVE;
         }
 
 
         /*

@@ -16223,13 +15966,12 @@
  * Description: Sends a REQUEST SENSE command to the target
  *
  *     Context: May be called from interrupt context.
  */
 
-static void
-sd_send_request_sense_command(struct sd_lun *un, struct buf *bp,
-    struct scsi_pkt *pktp)
+static void sd_send_request_sense_command(struct sd_lun *un, struct buf *bp,
+    int retry_check_flag, struct scsi_pkt *pktp)
 {
         ASSERT(bp != NULL);
         ASSERT(un != NULL);
         ASSERT(mutex_owned(SD_MUTEX(un)));
 

@@ -16252,18 +15994,15 @@
         /*
          * Retry the failed command and don't issue the request sense if:
          *    1) the sense buf is busy
          *    2) we have 1 or more outstanding commands on the target
          *    (the sense data will be cleared or invalidated any way)
-         *
-         * Note: There could be an issue with not checking a retry limit here,
-         * the problem is determining which retry limit to check.
          */
         if ((un->un_sense_isbusy != 0) || (un->un_ncmds_in_transport > 0)) {
                 /* Don't retry if the command is flagged as non-retryable */
                 if ((pktp->pkt_flags & FLAG_DIAGNOSE) == 0) {
-                        sd_retry_command(un, bp, SD_RETRIES_NOCHECK,
+                        sd_retry_command(un, bp, retry_check_flag,
                             NULL, NULL, 0, un->un_busy_timeout,
                             kstat_waitq_enter);
                         SD_TRACE(SD_LOG_IO_CORE | SD_LOG_ERROR, un,
                             "sd_send_request_sense_command: "
                             "at full throttle, retrying exit\n");

@@ -16431,13 +16170,13 @@
 
         SD_FILL_SCSI1_LUN(un, un->un_rqs_pktp);
 
         /* Set up the other needed members in the ARQ scsi_pkt. */
         un->un_rqs_pktp->pkt_comp   = sdintr;
-        un->un_rqs_pktp->pkt_time   = sd_io_time;
-        un->un_rqs_pktp->pkt_flags |=
-            (FLAG_SENSING | FLAG_HEAD); /* (1222170) */
+        un->un_rqs_pktp->pkt_time = ((ISCD(un)) ? 2 : 1) *
+            (ushort_t)un->un_io_time;
+        un->un_rqs_pktp->pkt_flags |= (FLAG_SENSING | FLAG_HEAD);
 
         /*
          * Allocate  & init the sd_xbuf struct for the RQS command. Do not
          * provide any intpkt, destroypkt routines as we take care of
          * scsi_pkt allocation/freeing here and in sd_free_rqs().

@@ -16468,23 +16207,20 @@
          * is to issue the scsi_ifgetcap() first, then try the scsi_ifsetcap().
          *
          * The 3rd case is the HBA (adp) always return enabled on
          * scsi_ifgetgetcap even when it's not enable, the best approach
          * is issue a scsi_ifsetcap then a scsi_ifgetcap
-         * Note: this case is to circumvent the Adaptec bug. (x86 only)
          */
 
         if (un->un_f_is_fibre == TRUE) {
                 un->un_f_arq_enabled = TRUE;
         } else {
-#if defined(__i386) || defined(__amd64)
                 /*
-                 * Circumvent the Adaptec bug, remove this code when
-                 * the bug is fixed
+                 * XXX Circumvent the Adaptec bug, remove this code when
+                 * the bug is fixed.
                  */
                 (void) scsi_ifsetcap(SD_ADDRESS(un), "auto-rqsense", 1, 1);
-#endif
                 switch (scsi_ifgetcap(SD_ADDRESS(un), "auto-rqsense", 1)) {
                 case 0:
                         SD_INFO(SD_LOG_ATTACH_DETACH, un,
                             "sd_alloc_rqs: HBA supports ARQ\n");
                         /*

@@ -16775,11 +16511,52 @@
          */
         SD_TRACE(SD_LOG_IO_CORE | SD_LOG_ERROR, un, "sdrunout: exit\n");
         return (1);
 }
 
+static void
+sd_slow_io_ereport(struct scsi_pkt *pktp)
+{
+        struct buf *bp;
+        struct sd_lun *un;
+        char *devid;
 
+        ASSERT(pktp != NULL);
+        bp = (struct buf *)pktp->pkt_private;
+        ASSERT(bp != NULL);
+        un = SD_GET_UN(bp);
+        ASSERT(un != NULL);
+
+        SD_ERROR(SD_LOG_IO_CORE | SD_LOG_ERROR, un,
+            "Slow IO detected SD: 0x%p delta in nsec: %llu",
+            (void *)un, pktp->pkt_stop - pktp->pkt_start);
+
+        devid = DEVI(un->un_sd->sd_dev)->devi_devid_str;
+        scsi_fm_ereport_post(un->un_sd, 0, NULL, "cmd.disk.slow-io",
+            fm_ena_generate(0, FM_ENA_FMT1), devid, NULL, DDI_NOSLEEP, NULL,
+            FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
+            "start", DATA_TYPE_UINT64, pktp->pkt_start,
+            "stop", DATA_TYPE_UINT64, pktp->pkt_stop,
+            "delta", DATA_TYPE_UINT64, pktp->pkt_stop - pktp->pkt_start,
+            "threshold", DATA_TYPE_UINT64, un->un_slow_io_threshold,
+            "pkt-reason", DATA_TYPE_UINT32, pktp->pkt_reason,
+            NULL);
+}
+
+/* Clamp the value between 0..max using min as the offset */
+static int
+clamp_lat(int bucket, int min, int max)
+{
+
+        if (max < bucket)
+                bucket = max;
+        if (min > bucket)
+                bucket = min;
+
+        return (bucket - min);
+}
+
 /*
  *    Function: sdintr
  *
  * Description: Completion callback routine for scsi_pkt(9S) structs
  *              sent to the HBA driver via scsi_transport(9F).

@@ -16793,10 +16570,12 @@
         struct buf      *bp;
         struct sd_xbuf  *xp;
         struct sd_lun   *un;
         size_t          actual_len;
         sd_ssc_t        *sscp;
+        hrtime_t        io_delta = 0LL;
+        int             bucket;
 
         ASSERT(pktp != NULL);
         bp = (struct buf *)pktp->pkt_private;
         ASSERT(bp != NULL);
         xp = SD_GET_XBUF(bp);

@@ -16829,11 +16608,34 @@
 
         /* Increment counter to indicate that the callback routine is active */
         un->un_in_callback++;
 
         SD_UPDATE_KSTATS(un, kstat_runq_exit, bp);
+        /* If the HBA driver did not set the stop time, set it now. */
+        if (pktp->pkt_stop == 0)
+                pktp->pkt_stop = gethrtime();
+        /*
+         * If there are HBA drivers or layered drivers which do not participate
+         * in slow-io diagnosis, the start time, set above may be overwritten
+         * with zero. If pkt_start is zero, the delta should also be zero.
+         */
+        if (pktp->pkt_start != 0)
+                io_delta = pktp->pkt_stop - pktp->pkt_start;
+        if (un->un_slow_io_threshold > 0 && io_delta > un->un_slow_io_threshold)
+                sd_slow_io_ereport(pktp);
+        if (un->un_lat_stats) {
+                un->un_lat_stats->l_nrequest++;
+                un->un_lat_stats->l_sum += io_delta;
 
+                /* Track the latency in usec and quantize by power of 2 */
+                bucket = clamp_lat(ddi_fls(io_delta / 1000),
+                    SD_LAT_MIN_USEC_SHIFT, SD_LAT_MAX_USEC_SHIFT - 1);
+                ASSERT3S(bucket, >=, 0);
+                ASSERT3S(bucket, <, ARRAY_SIZE(un->un_lat_stats->l_histogram));
+                un->un_lat_stats->l_histogram[bucket]++;
+        }
+
 #ifdef  SDDEBUG
         if (bp == un->un_retry_bp) {
                 SD_TRACE(SD_LOG_IO | SD_LOG_ERROR, un, "sdintr: "
                     "un:0x%p: GOT retry_bp:0x%p un_ncmds_in_transport:%d\n",
                     un, un->un_retry_bp, un->un_ncmds_in_transport);

@@ -16928,11 +16730,10 @@
                             "sdintr: arq done and FLAG_DIAGNOSE set\n");
                         sd_return_failed_command(un, bp, EIO);
                         goto exit;
                 }
 
-#if (defined(__i386) || defined(__amd64))       /* DMAFREE for x86 only */
                 /*
                  * We want to either retry or fail this command, so free
                  * the DMA resources here.  If we retry the command then
                  * the DMA resources will be reallocated in sd_start_cmds().
                  * Note that when PKT_DMA_PARTIAL is used, this reallocation

@@ -16944,11 +16745,10 @@
                     ((xp->xb_pkt_flags & SD_XB_USCSICMD) == 0) &&
                     ((pktp->pkt_flags & FLAG_SENSING) == 0))  {
                         scsi_dmafree(pktp);
                         xp->xb_pkt_flags |= SD_XB_DMA_FREED;
                 }
-#endif
 
                 SD_TRACE(SD_LOG_IO_CORE | SD_LOG_ERROR, un,
                     "sdintr: arq done, sd_handle_auto_request_sense\n");
 
                 sd_handle_auto_request_sense(un, bp, xp, pktp);

@@ -17020,12 +16820,10 @@
 
                 return;
         }
 
 not_successful:
-
-#if (defined(__i386) || defined(__amd64))       /* DMAFREE for x86 only */
         /*
          * The following is based upon knowledge of the underlying transport
          * and its use of DMA resources.  This code should be removed when
          * PKT_DMA_PARTIAL support is taken out of the disk driver in favor
          * of the new PKT_CMD_BREAKUP protocol. See also sd_initpkt_for_buf()

@@ -17049,11 +16847,10 @@
             ((xp->xb_pkt_flags & SD_XB_USCSICMD) == 0) &&
             ((pktp->pkt_flags & FLAG_SENSING) == 0))  {
                 scsi_dmafree(pktp);
                 xp->xb_pkt_flags |= SD_XB_DMA_FREED;
         }
-#endif
 
         /*
          * The command did not successfully complete as requested so check
          * for FLAG_DIAGNOSE. If set this indicates a uscsi or internal
          * driver command that should not be retried so just return. If

@@ -17067,11 +16864,12 @@
                  * (we handle the auto request sense case above), otherwise
                  * just fail the command.
                  */
                 if ((pktp->pkt_reason == CMD_CMPLT) &&
                     (SD_GET_PKT_STATUS(pktp) == STATUS_CHECK)) {
-                        sd_send_request_sense_command(un, bp, pktp);
+                        sd_send_request_sense_command(un, bp,
+                            SD_RETRIES_STANDARD, pktp);
                 } else {
                         sd_return_failed_command(un, bp, EIO);
                 }
                 goto exit;
         }

@@ -17711,25 +17509,13 @@
 sense_failed:
         /*
          * If the request sense failed (for whatever reason), attempt
          * to retry the original command.
          */
-#if defined(__i386) || defined(__amd64)
-        /*
-         * SD_RETRY_DELAY is conditionally compile (#if fibre) in
-         * sddef.h for Sparc platform, and x86 uses 1 binary
-         * for both SCSI/FC.
-         * The SD_RETRY_DELAY value need to be adjusted here
-         * when SD_RETRY_DELAY change in sddef.h
-         */
         sd_retry_command(un, bp, SD_RETRIES_STANDARD,
             sd_print_sense_failed_msg, msgp, EIO,
             un->un_f_is_fibre?drv_usectohz(100000):(clock_t)0, NULL);
-#else
-        sd_retry_command(un, bp, SD_RETRIES_STANDARD,
-            sd_print_sense_failed_msg, msgp, EIO, SD_RETRY_DELAY, NULL);
-#endif
 
         return (SD_SENSE_DATA_IS_INVALID);
 }
 
 /*

@@ -19171,11 +18957,11 @@
         SD_UPDATE_RESERVATION_STATUS(un, pktp);
 
         funcp = ((pktp->pkt_statistics & STAT_PERR) == 0) ?
             sd_print_retry_msg : NULL;
 
-        sd_retry_command(un, bp, (SD_RETRIES_STANDARD | SD_RETRIES_ISOLATE),
+        sd_retry_command(un, bp, (SD_RETRIES_VICTIM | SD_RETRIES_ISOLATE),
             funcp, NULL, EIO, SD_RESTART_TIMEOUT, NULL);
 }
 
 
 /*

@@ -19274,26 +19060,18 @@
          * retry limit may have been reached for the failed command.
          */
         if (un->un_f_arq_enabled == FALSE) {
                 SD_INFO(SD_LOG_IO_CORE, un, "sd_pkt_status_check_condition: "
                     "no ARQ, sending request sense command\n");
-                sd_send_request_sense_command(un, bp, pktp);
+                sd_send_request_sense_command(un, bp, SD_RETRIES_STANDARD,
+                    pktp);
         } else {
                 SD_INFO(SD_LOG_IO_CORE, un, "sd_pkt_status_check_condition: "
                     "ARQ,retrying request sense command\n");
-#if defined(__i386) || defined(__amd64)
-                /*
-                 * The SD_RETRY_DELAY value need to be adjusted here
-                 * when SD_RETRY_DELAY change in sddef.h
-                 */
                 sd_retry_command(un, bp, SD_RETRIES_STANDARD, NULL, NULL, EIO,
-                    un->un_f_is_fibre?drv_usectohz(100000):(clock_t)0,
+                    un->un_f_is_fibre ? drv_usectohz(100000) : (clock_t)0,
                     NULL);
-#else
-                sd_retry_command(un, bp, SD_RETRIES_STANDARD, NULL, NULL,
-                    EIO, SD_RETRY_DELAY, NULL);
-#endif
         }
 
         SD_TRACE(SD_LOG_IO_CORE, un, "sd_pkt_status_check_condition: exit\n");
 }
 

@@ -20045,11 +19823,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)capacity_buf;
         ucmd_buf.uscsi_buflen   = SD_CAPACITY_SIZE;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (sense_buf);
         ucmd_buf.uscsi_flags    = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, path_flag);
 
         switch (status) {

@@ -20261,11 +20039,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)capacity16_buf;
         ucmd_buf.uscsi_buflen   = SD_CAPACITY_16_SIZE;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (sense_buf);
         ucmd_buf.uscsi_flags    = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         /*
          * Read Capacity (16) is a Service Action In command.  One
          * command byte (0x9E) is overloaded for multiple operations,
          * with the second CDB byte specifying the desired operation

@@ -20305,15 +20083,25 @@
                  *
                  *  bytes 8-11: Block length in bytes
                  *              (MSB in byte:8 & LSB in byte:11)
                  *
                  *  byte 13: LOGICAL BLOCKS PER PHYSICAL BLOCK EXPONENT
+                 *
+                 *  byte 14:
+                 *      bit 7: Thin-Provisioning Enabled
+                 *      bit 6: Thin-Provisioning Read Zeros
                  */
                 capacity = BE_64(capacity16_buf[0]);
                 lbasize = BE_32(*(uint32_t *)&capacity16_buf[1]);
                 lbpb_exp = (BE_64(capacity16_buf[1]) >> 16) & 0x0f;
 
+                un->un_thin_flags = 0;
+                if (((uint8_t *)capacity16_buf)[14] & (1 << 7))
+                        un->un_thin_flags |= SD_THIN_PROV_ENABLED;
+                if (((uint8_t *)capacity16_buf)[14] & (1 << 6))
+                        un->un_thin_flags |= SD_THIN_PROV_READ_ZEROS;
+
                 pbsize = lbasize << lbpb_exp;
 
                 /*
                  * Done with capacity16_buf
                  */

@@ -20483,11 +20271,11 @@
         ucmd_buf.uscsi_bufaddr  = NULL;
         ucmd_buf.uscsi_buflen   = 0;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (struct scsi_extended_sense);
         ucmd_buf.uscsi_flags    = USCSI_RQENABLE | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 200;
+        ucmd_buf.uscsi_timeout  = 3 * un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, path_flag);
 
         switch (status) {

@@ -20702,11 +20490,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)bufaddr;
         ucmd_buf.uscsi_buflen   = buflen;
         ucmd_buf.uscsi_rqbuf    = NULL;
         ucmd_buf.uscsi_rqlen    = 0;
         ucmd_buf.uscsi_flags    = USCSI_READ | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 200;  /* Excessive legacy value */
+        ucmd_buf.uscsi_timeout  = 2 * un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, SD_PATH_DIRECT);
 
         /*

@@ -20805,11 +20593,11 @@
 
         /* Use flag USCSI_DIAGNOSE to prevent retries if it fails. */
         if ((flag & SD_DONT_RETRY_TUR) != 0) {
                 ucmd_buf.uscsi_flags |= USCSI_DIAGNOSE;
         }
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, ((flag & SD_BYPASS_PM) ? SD_PATH_DIRECT :
             SD_PATH_STANDARD));
 

@@ -20902,11 +20690,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)data_bufp;
         ucmd_buf.uscsi_buflen   = data_len;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (struct scsi_extended_sense);
         ucmd_buf.uscsi_flags    = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, SD_PATH_STANDARD);
 
         switch (status) {

@@ -21008,11 +20796,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)prp;
         ucmd_buf.uscsi_buflen   = data_len;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (struct scsi_extended_sense);
         ucmd_buf.uscsi_flags    = USCSI_RQENABLE | USCSI_WRITE | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         switch (usr_cmd) {
         case SD_SCSI3_REGISTER: {
                 mhioc_register_t *ptr = (mhioc_register_t *)usr_bufp;
 

@@ -21203,11 +20991,11 @@
         uscmd->uscsi_buflen = 0;
         uscmd->uscsi_rqbuf = kmem_zalloc(SENSE_LENGTH, KM_SLEEP);
         uscmd->uscsi_rqlen = SENSE_LENGTH;
         uscmd->uscsi_rqresid = SENSE_LENGTH;
         uscmd->uscsi_flags = USCSI_RQENABLE | USCSI_SILENT;
-        uscmd->uscsi_timeout = sd_io_time;
+        uscmd->uscsi_timeout = un->un_cmd_timeout;
 
         /*
          * Allocate an sd_uscsi_info struct and fill it with the info
          * needed by sd_initpkt_for_uscsi().  Then put the pointer into
          * b_private in the buf for sd_initpkt_for_uscsi().  Note that

@@ -21350,13 +21138,12 @@
                 }
                 break;
         }
 
 done:
-        if (uip->ui_dkc.dkc_callback != NULL) {
+        if (uip->ui_dkc.dkc_callback != NULL)
                 (*uip->ui_dkc.dkc_callback)(uip->ui_dkc.dkc_cookie, status);
-        }
 
         ASSERT((bp->b_flags & B_REMAPPED) == 0);
         freerbuf(bp);
         kmem_free(uip, sizeof (struct sd_uscsi_info));
         kmem_free(uscmd->uscsi_rqbuf, SENSE_LENGTH);

@@ -21364,12 +21151,230 @@
         kmem_free(uscmd, sizeof (struct uscsi_cmd));
 
         return (status);
 }
 
+/*
+ * Issues a single SCSI UNMAP command with a prepared UNMAP parameter list.
+ * Returns zero on success, or the non-zero command error code on failure.
+ */
+static int
+sd_send_scsi_UNMAP_issue_one(sd_ssc_t *ssc, unmap_param_hdr_t *uph,
+    uint64_t num_descr, uint64_t bytes)
+{
+        struct sd_lun           *un = ssc->ssc_un;
+        struct scsi_extended_sense      sense_buf;
+        union scsi_cdb          cdb;
+        struct uscsi_cmd        ucmd_buf;
+        int                     status;
+        const uint64_t          param_size = sizeof (unmap_param_hdr_t) +
+            num_descr * sizeof (unmap_blk_descr_t);
 
+        uph->uph_data_len = BE_16(param_size - 2);
+        uph->uph_descr_data_len = BE_16(param_size - 8);
+
+        bzero(&cdb, sizeof (cdb));
+        bzero(&ucmd_buf, sizeof (ucmd_buf));
+        bzero(&sense_buf, sizeof (struct scsi_extended_sense));
+
+        cdb.scc_cmd = SCMD_UNMAP;
+        FORMG1COUNT(&cdb, param_size);
+
+        ucmd_buf.uscsi_cdb      = (char *)&cdb;
+        ucmd_buf.uscsi_cdblen   = (uchar_t)CDB_GROUP1;
+        ucmd_buf.uscsi_bufaddr  = (caddr_t)uph;
+        ucmd_buf.uscsi_buflen   = param_size;
+        ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
+        ucmd_buf.uscsi_rqlen    = sizeof (struct scsi_extended_sense);
+        ucmd_buf.uscsi_flags    = USCSI_WRITE | USCSI_RQENABLE | USCSI_SILENT;
+        ucmd_buf.uscsi_timeout  = un->un_cmd_timeout;
+
+        status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL, UIO_SYSSPACE,
+            SD_PATH_STANDARD);
+
+        switch (status) {
+        case 0:
+                sd_ssc_assessment(ssc, SD_FMT_STANDARD);
+
+                if (un->un_unmapstats) {
+                        atomic_inc_64(&un->un_unmapstats->us_cmds.value.ui64);
+                        atomic_add_64(&un->un_unmapstats->us_extents.value.ui64,
+                            num_descr);
+                        atomic_add_64(&un->un_unmapstats->us_bytes.value.ui64,
+                            bytes);
+                }
+                break;  /* Success! */
+        case EIO:
+                if (un->un_unmapstats)
+                        atomic_inc_64(&un->un_unmapstats->us_errs.value.ui64);
+                switch (ucmd_buf.uscsi_status) {
+                case STATUS_RESERVATION_CONFLICT:
+                        status = EACCES;
+                        break;
+                default:
+                        break;
+                }
+                break;
+        default:
+                if (un->un_unmapstats)
+                        atomic_inc_64(&un->un_unmapstats->us_errs.value.ui64);
+                break;
+        }
+
+        return (status);
+}
+
 /*
+ * Returns a pointer to the i'th block descriptor inside an UNMAP param list.
+ */
+static inline unmap_blk_descr_t *
+UNMAP_blk_descr_i(void *buf, uint64_t i)
+{
+        return ((unmap_blk_descr_t *)((uint8_t *)buf +
+            sizeof (unmap_param_hdr_t) + (i * sizeof (unmap_blk_descr_t))));
+}
+
+/*
+ * Takes the list of extents from sd_send_scsi_UNMAP, chops it up, prepares
+ * UNMAP block descriptors and issues individual SCSI UNMAP commands. While
+ * doing so we consult the block limits to determine at most how many
+ * extents and LBAs we can UNMAP in one command.
+ * If a command fails for whatever, reason, extent list processing is aborted
+ * and the failed command's status is returned. Otherwise returns 0 on
+ * success.
+ */
+static int
+sd_send_scsi_UNMAP_issue(dev_t dev, sd_ssc_t *ssc, const dkioc_free_list_t *dfl)
+{
+        struct sd_lun           *un = ssc->ssc_un;
+        unmap_param_hdr_t       *uph;
+        sd_blk_limits_t         *lim = &un->un_blk_lim;
+        int                     rval = 0;
+        int                     partition;
+        /* partition offset & length in system blocks */
+        diskaddr_t              part_off_sysblks = 0, part_len_sysblks = 0;
+        uint64_t                part_off, part_len;
+        uint64_t                descr_cnt_lim, byte_cnt_lim;
+        uint64_t                descr_issued = 0, bytes_issued = 0;
+
+        uph = kmem_zalloc(SD_UNMAP_PARAM_LIST_MAXSZ, KM_SLEEP);
+
+        partition = SDPART(dev);
+        (void) cmlb_partinfo(un->un_cmlbhandle, partition, &part_len_sysblks,
+            &part_off_sysblks, NULL, NULL, (void *)SD_PATH_DIRECT);
+        part_off = SD_SYSBLOCKS2BYTES(part_off_sysblks);
+        part_len = SD_SYSBLOCKS2BYTES(part_len_sysblks);
+
+        ASSERT(un->un_blk_lim.lim_max_unmap_lba_cnt != 0);
+        ASSERT(un->un_blk_lim.lim_max_unmap_descr_cnt != 0);
+        /* Spec says 0xffffffff are special values, so compute maximums. */
+        byte_cnt_lim = lim->lim_max_unmap_lba_cnt < UINT32_MAX ?
+            (uint64_t)lim->lim_max_unmap_lba_cnt * un->un_tgt_blocksize :
+            UINT64_MAX;
+        descr_cnt_lim = MIN(lim->lim_max_unmap_descr_cnt, SD_UNMAP_MAX_DESCR);
+
+        for (size_t i = 0; i < dfl->dfl_num_exts; i++) {
+                const dkioc_free_list_ext_t *ext = &dfl->dfl_exts[i];
+                uint64_t ext_start = ext->dfle_start;
+                uint64_t ext_length = ext->dfle_length;
+
+                while (ext_length > 0) {
+                        unmap_blk_descr_t *ubd;
+                        /* Respect device limit on LBA count per command */
+                        uint64_t len = MIN(MIN(ext_length, byte_cnt_lim -
+                            bytes_issued), SD_TGTBLOCKS2BYTES(un, UINT32_MAX));
+
+                        /* check partition limits */
+                        if (ext_start + len > part_len) {
+                                rval = SET_ERROR(EINVAL);
+                                goto out;
+                        }
+#ifdef  DEBUG
+                        if (dfl->dfl_ck_func)
+                                dfl->dfl_ck_func(dfl->dfl_offset + ext_start,
+                                    len, dfl->dfl_ck_arg);
+#endif
+                        ASSERT3U(descr_issued, <, descr_cnt_lim);
+                        ASSERT3U(bytes_issued, <, byte_cnt_lim);
+                        ubd = UNMAP_blk_descr_i(uph, descr_issued);
+
+                        /* adjust in-partition addresses to be device-global */
+                        ubd->ubd_lba = BE_64(SD_BYTES2TGTBLOCKS(un,
+                            dfl->dfl_offset + ext_start + part_off));
+                        ubd->ubd_lba_cnt = BE_32(SD_BYTES2TGTBLOCKS(un, len));
+
+                        descr_issued++;
+                        bytes_issued += len;
+
+                        /* Issue command when device limits reached */
+                        if (descr_issued == descr_cnt_lim ||
+                            bytes_issued == byte_cnt_lim) {
+                                rval = sd_send_scsi_UNMAP_issue_one(ssc, uph,
+                                    descr_issued, bytes_issued);
+                                if (rval != 0)
+                                        goto out;
+                                descr_issued = 0;
+                                bytes_issued = 0;
+                        }
+
+                        ext_start += len;
+                        ext_length -= len;
+                }
+        }
+
+        if (descr_issued > 0) {
+                /* issue last command */
+                rval = sd_send_scsi_UNMAP_issue_one(ssc, uph, descr_issued,
+                    bytes_issued);
+        }
+
+out:
+        kmem_free(uph, SD_UNMAP_PARAM_LIST_MAXSZ);
+        return (rval);
+}
+
+/*
+ * Issues one or several UNMAP commands based on a list of extents to be
+ * unmapped. The internal multi-command processing is hidden, as the exact
+ * number of commands and extents per command is limited by both SCSI
+ * command syntax and device limits (as expressed in the SCSI Block Limits
+ * VPD page and un_blk_lim in struct sd_lun).
+ * Returns zero on success, or the error code of the first failed SCSI UNMAP
+ * command.
+ */
+static int
+sd_send_scsi_UNMAP(dev_t dev, sd_ssc_t *ssc, dkioc_free_list_t *dfl, int flag)
+{
+        struct sd_lun           *un = ssc->ssc_un;
+        int                     rval = 0;
+
+        ASSERT(!mutex_owned(SD_MUTEX(un)));
+        ASSERT(dfl != NULL);
+
+        /* Per spec, any of these conditions signals lack of UNMAP support. */
+        if (!(un->un_thin_flags & SD_THIN_PROV_ENABLED) ||
+            un->un_blk_lim.lim_max_unmap_descr_cnt == 0 ||
+            un->un_blk_lim.lim_max_unmap_lba_cnt == 0) {
+                return (SET_ERROR(ENOTSUP));
+        }
+
+        /* For userspace calls we must copy in. */
+        if (!(flag & FKIOCTL) && (dfl = dfl_copyin(dfl, flag, KM_SLEEP)) ==
+            NULL)
+                return (SET_ERROR(EFAULT));
+
+        rval = sd_send_scsi_UNMAP_issue(dev, ssc, dfl);
+
+        if (!(flag & FKIOCTL)) {
+                dfl_free(dfl);
+                dfl = NULL;
+        }
+
+        return (rval);
+}
+
+/*
  *    Function: sd_send_scsi_GET_CONFIGURATION
  *
  * Description: Issues the get configuration command to the device.
  *              Called from sd_check_for_writable_cd & sd_get_media_info
  *              caller needs to ensure that buflen = SD_PROFILE_HEADER_LEN

@@ -21421,11 +21426,11 @@
         cdb[8] = SD_PROFILE_HEADER_LEN;
         ucmdbuf->uscsi_cdb = cdb;
         ucmdbuf->uscsi_cdblen = CDB_GROUP1;
         ucmdbuf->uscsi_bufaddr = (caddr_t)bufaddr;
         ucmdbuf->uscsi_buflen = buflen;
-        ucmdbuf->uscsi_timeout = sd_io_time;
+        ucmdbuf->uscsi_timeout = un->un_uscsi_timeout;
         ucmdbuf->uscsi_rqbuf = (caddr_t)rqbuf;
         ucmdbuf->uscsi_rqlen = rqbuflen;
         ucmdbuf->uscsi_flags = USCSI_RQENABLE|USCSI_SILENT|USCSI_READ;
 
         status = sd_ssc_send(ssc, ucmdbuf, FKIOCTL,

@@ -21514,11 +21519,11 @@
         cdb[8] = buflen;
         ucmdbuf->uscsi_cdb = cdb;
         ucmdbuf->uscsi_cdblen = CDB_GROUP1;
         ucmdbuf->uscsi_bufaddr = (caddr_t)bufaddr;
         ucmdbuf->uscsi_buflen = buflen;
-        ucmdbuf->uscsi_timeout = sd_io_time;
+        ucmdbuf->uscsi_timeout = un->un_uscsi_timeout;
         ucmdbuf->uscsi_rqbuf = (caddr_t)rqbuf;
         ucmdbuf->uscsi_rqlen = rqbuflen;
         ucmdbuf->uscsi_flags = USCSI_RQENABLE|USCSI_SILENT|USCSI_READ;
 
         status = sd_ssc_send(ssc, ucmdbuf, FKIOCTL,

@@ -21627,11 +21632,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)bufaddr;
         ucmd_buf.uscsi_buflen   = buflen;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (struct scsi_extended_sense);
         ucmd_buf.uscsi_flags    = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, path_flag);
 
         switch (status) {

@@ -21746,11 +21751,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)bufaddr;
         ucmd_buf.uscsi_buflen   = buflen;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (struct scsi_extended_sense);
         ucmd_buf.uscsi_flags    = USCSI_RQENABLE | USCSI_WRITE | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, path_flag);
 
         switch (status) {

@@ -21884,11 +21889,11 @@
         ucmd_buf.uscsi_bufaddr  = bufaddr;
         ucmd_buf.uscsi_buflen   = buflen;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (struct scsi_extended_sense);
         ucmd_buf.uscsi_flags    = flag | USCSI_RQENABLE | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_cmd_timeout;
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, path_flag);
 
         switch (status) {
         case 0:

@@ -21964,11 +21969,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)bufaddr;
         ucmd_buf.uscsi_buflen   = buflen;
         ucmd_buf.uscsi_rqbuf    = (caddr_t)&sense_buf;
         ucmd_buf.uscsi_rqlen    = sizeof (struct scsi_extended_sense);
         ucmd_buf.uscsi_flags    = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, path_flag);
 
         switch (status) {

@@ -22096,11 +22101,11 @@
         ucmd_buf.uscsi_bufaddr  = (caddr_t)bufaddr;
         ucmd_buf.uscsi_buflen   = buflen;
         ucmd_buf.uscsi_rqbuf    = NULL;
         ucmd_buf.uscsi_rqlen    = 0;
         ucmd_buf.uscsi_flags    = USCSI_READ | USCSI_SILENT;
-        ucmd_buf.uscsi_timeout  = 60;
+        ucmd_buf.uscsi_timeout  = un->un_uscsi_timeout;
 
         status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL,
             UIO_SYSSPACE, SD_PATH_DIRECT);
 
         /*

@@ -22178,36 +22183,48 @@
                 return (ENXIO);
         }
 
         ASSERT(!mutex_owned(SD_MUTEX(un)));
 
-        /* Initialize sd_ssc_t for internal uscsi commands */
-        ssc = sd_ssc_init(un);
-
-        is_valid = SD_IS_VALID_LABEL(un);
-
         /*
          * Moved this wait from sd_uscsi_strategy to here for
          * reasons of deadlock prevention. Internal driver commands,
          * specifically those to change a devices power level, result
          * in a call to sd_uscsi_strategy.
          */
         mutex_enter(SD_MUTEX(un));
         while ((un->un_state == SD_STATE_SUSPENDED) ||
-            (un->un_state == SD_STATE_PM_CHANGING)) {
+            (un->un_state == SD_STATE_PM_CHANGING) ||
+            (un->un_state == SD_STATE_ATTACHING)) {
                 cv_wait(&un->un_suspend_cv, SD_MUTEX(un));
         }
+
+        if (un->un_state == SD_STATE_ATTACH_FAILED) {
+                mutex_exit(SD_MUTEX(un));
+                SD_ERROR(SD_LOG_READ_WRITE, un,
+                    "sdioctl: attach failed\n");
+                return (EIO);
+        }
+
         /*
          * Twiddling the counter here protects commands from now
          * through to the top of sd_uscsi_strategy. Without the
          * counter inc. a power down, for example, could get in
          * after the above check for state is made and before
          * execution gets to the top of sd_uscsi_strategy.
          * That would cause problems.
          */
         un->un_ncmds_in_driver++;
+        mutex_exit(SD_MUTEX(un));
 
+        /* Initialize sd_ssc_t for internal uscsi commands */
+        ssc = sd_ssc_init(un);
+
+        is_valid = SD_IS_VALID_LABEL(un);
+
+        mutex_enter(SD_MUTEX(un));
+
         if (!is_valid &&
             (flag & (FNDELAY | FNONBLOCK))) {
                 switch (cmd) {
                 case DKIOCGGEOM:        /* SD_PATH_DIRECT */
                 case DKIOCGVTOC:

@@ -22224,16 +22241,13 @@
                 case DKIOCSETEFI:
                 case DKIOCGMBOOT:
                 case DKIOCSMBOOT:
                 case DKIOCG_PHYGEOM:
                 case DKIOCG_VIRTGEOM:
-#if defined(__i386) || defined(__amd64)
                 case DKIOCSETEXTPART:
-#endif
                         /* let cmlb handle it */
                         goto skip_ready_valid;
-
                 case CDROMPAUSE:
                 case CDROMRESUME:
                 case CDROMPLAYMSF:
                 case CDROMPLAYTRKIND:
                 case CDROMREADTOCHDR:

@@ -22253,10 +22267,12 @@
                 case CDROMCDXA:
                 case CDROMSUBCODE:
                         if (!ISCD(un)) {
                                 un->un_ncmds_in_driver--;
                                 ASSERT(un->un_ncmds_in_driver >= 0);
+                                if (un->un_f_detach_waiting)
+                                        cv_signal(&un->un_detach_cv);
                                 mutex_exit(SD_MUTEX(un));
                                 err = ENOTTY;
                                 goto done_without_assess;
                         }
                         break;

@@ -22264,10 +22280,12 @@
                 case DKIOCEJECT:
                 case CDROMEJECT:
                         if (!un->un_f_eject_media_supported) {
                                 un->un_ncmds_in_driver--;
                                 ASSERT(un->un_ncmds_in_driver >= 0);
+                                if (un->un_f_detach_waiting)
+                                        cv_signal(&un->un_detach_cv);
                                 mutex_exit(SD_MUTEX(un));
                                 err = ENOTTY;
                                 goto done_without_assess;
                         }
                         break;

@@ -22276,10 +22294,12 @@
                         err = sd_send_scsi_TEST_UNIT_READY(ssc, 0);
                         if (err != 0) {
                                 mutex_enter(SD_MUTEX(un));
                                 un->un_ncmds_in_driver--;
                                 ASSERT(un->un_ncmds_in_driver >= 0);
+                                if (un->un_f_detach_waiting)
+                                        cv_signal(&un->un_detach_cv);
                                 mutex_exit(SD_MUTEX(un));
                                 err = EIO;
                                 goto done_quick_assess;
                         }
                         mutex_enter(SD_MUTEX(un));

@@ -22334,10 +22354,12 @@
                                                 err = EIO;
                                         }
                                 }
                                 un->un_ncmds_in_driver--;
                                 ASSERT(un->un_ncmds_in_driver >= 0);
+                                if (un->un_f_detach_waiting)
+                                        cv_signal(&un->un_detach_cv);
                                 mutex_exit(SD_MUTEX(un));
 
                                 goto done_without_assess;
                         }
                 }

@@ -22377,13 +22399,11 @@
         case DKIOCSETEFI:
         case DKIOCGMBOOT:
         case DKIOCSMBOOT:
         case DKIOCG_PHYGEOM:
         case DKIOCG_VIRTGEOM:
-#if defined(__i386) || defined(__amd64)
         case DKIOCSETEXTPART:
-#endif
                 SD_TRACE(SD_LOG_IOCTL, un, "DKIOC %d\n", cmd);
 
                 /* TUR should spin up */
 
                 if (un->un_f_has_removable_media)

@@ -22758,18 +22778,14 @@
                 }
                 break;
 
         case CDROMPLAYTRKIND:
                 SD_TRACE(SD_LOG_IOCTL, un, "CDROMPLAYTRKIND\n");
-#if defined(__i386) || defined(__amd64)
                 /*
                  * not supported on ATAPI CD drives, use CDROMPLAYMSF instead
                  */
                 if (!ISCD(un) || (un->un_f_cfg_is_atapi == TRUE)) {
-#else
-                if (!ISCD(un)) {
-#endif
                         err = ENOTTY;
                 } else {
                         err = sr_play_trkind(dev, (caddr_t)arg, flag);
                 }
                 break;

@@ -23056,15 +23072,15 @@
         case SDIOCINSERTUN:
         case SDIOCINSERTARQ:
         case SDIOCPUSH:
         case SDIOCRETRIEVE:
         case SDIOCRUN:
+        case SDIOCINSERTTRAN:
                 SD_INFO(SD_LOG_SDTEST, un, "sdioctl:"
                     "SDIOC detected cmd:0x%X:\n", cmd);
                 /* call error generator */
-                sd_faultinjection_ioctl(cmd, arg, un);
-                err = 0;
+                err = sd_faultinjection_ioctl(cmd, arg, un);
                 break;
 
 #endif /* SD_FAULT_INJECTION */
 
         case DKIOCFLUSHWRITECACHE:

@@ -23101,10 +23117,24 @@
                                 err = sd_send_scsi_SYNCHRONIZE_CACHE(un, NULL);
                         }
                 }
                 break;
 
+        case DKIOCFREE:
+                {
+                        dkioc_free_list_t *dfl = (dkioc_free_list_t *)arg;
+
+                        /* bad userspace ioctls shouldn't panic */
+                        if (dfl == NULL && !(flag & FKIOCTL)) {
+                                err = SET_ERROR(EINVAL);
+                                break;
+                        }
+                        /* synchronous UNMAP request */
+                        err = sd_send_scsi_UNMAP(dev, ssc, dfl, flag);
+                }
+                break;
+
         case DKIOCGETWCE: {
 
                 int wce;
 
                 if ((err = sd_get_write_cache_enabled(ssc, &wce)) != 0) {

@@ -23246,10 +23276,12 @@
                 break;
         }
         mutex_enter(SD_MUTEX(un));
         un->un_ncmds_in_driver--;
         ASSERT(un->un_ncmds_in_driver >= 0);
+        if (un->un_f_detach_waiting)
+                cv_signal(&un->un_detach_cv);
         mutex_exit(SD_MUTEX(un));
 
 
 done_without_assess:
         sd_ssc_fini(ssc);

@@ -23259,10 +23291,12 @@
 
 done_with_assess:
         mutex_enter(SD_MUTEX(un));
         un->un_ncmds_in_driver--;
         ASSERT(un->un_ncmds_in_driver >= 0);
+        if (un->un_f_detach_waiting)
+                cv_signal(&un->un_detach_cv);
         mutex_exit(SD_MUTEX(un));
 
 done_quick_assess:
         if (err != 0)
                 sd_ssc_assessment(ssc, SD_FMT_IGNORE);

@@ -23363,11 +23397,11 @@
  *              dki_lbsize      - logical block size
  *              dki_capacity    - capacity in blocks
  *              dki_pbsize      - physical block size (if requested)
  *
  * Return Code: 0
- *              EACCESS
+ *              EACCES
  *              EFAULT
  *              ENXIO
  *              EIO
  */
 static int

@@ -23481,14 +23515,13 @@
          */
         if (dki_pbsize && un->un_f_descr_format_supported) {
                 rval = sd_send_scsi_READ_CAPACITY_16(ssc, &capacity, &lbasize,
                     &pbsize, SD_PATH_DIRECT);
 
-                /*
-                 * Override the physical blocksize if the instance already
-                 * has a larger value.
-                 */
+                if (un->un_f_sdconf_phy_blocksize) /* keep sd.conf's pbs */
+                        pbsize = un->un_phy_blocksize;
+                else /* override the pbs if the instance has a larger value */
                 pbsize = MAX(pbsize, un->un_phy_blocksize);
         }
 
         if (dki_pbsize == NULL || rval != 0 ||
             !un->un_f_descr_format_supported) {

@@ -23745,10 +23778,12 @@
                  * If the count isn't decremented the device can't
                  * be powered down.
                  */
                 un->un_ncmds_in_driver--;
                 ASSERT(un->un_ncmds_in_driver >= 0);
+                if (un->un_f_detach_waiting)
+                        cv_signal(&un->un_detach_cv);
 
                 /*
                  * if a prior request had been made, this will be the same
                  * token, as scsi_watch was designed that way.
                  */

@@ -24238,11 +24273,11 @@
  *
  * Return Code: -1 - on error (log sense is optional and may not be supported).
  *              0  - log page not found.
  *              1  - log page found.
  */
-
+#ifdef notyet
 static int
 sd_log_page_supported(sd_ssc_t *ssc, int log_page)
 {
         uchar_t *log_page_data;
         int     i;

@@ -24303,12 +24338,12 @@
                 }
         }
         kmem_free(log_page_data, 0xFF);
         return (match);
 }
+#endif
 
-
 /*
  *    Function: sd_mhdioc_failfast
  *
  * Description: This routine is the driver entry point for handling ioctl
  *              requests to enable/disable the multihost failfast option.

@@ -25740,16 +25775,16 @@
         diskaddr_t      nblks = 0;
         diskaddr_t      start_block;
 
         instance = SDUNIT(dev);
         if (((un = ddi_get_soft_state(sd_state, instance)) == NULL) ||
+            (un->un_state == SD_STATE_ATTACHING) ||
+            (un->un_state == SD_STATE_ATTACH_FAILED) ||
             !SD_IS_VALID_LABEL(un) || ISCD(un)) {
                 return (ENXIO);
         }
 
-        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*un))
-
         SD_TRACE(SD_LOG_DUMP, un, "sddump: entry\n");
 
         partition = SDPART(dev);
         SD_INFO(SD_LOG_DUMP, un, "sddump: partition = %d\n", partition);
 

@@ -27989,11 +28024,11 @@
         com = kmem_zalloc(sizeof (*com), KM_SLEEP);
         com->uscsi_cdb     = cdb;
         com->uscsi_cdblen  = CDB_GROUP1;
         com->uscsi_bufaddr = buffer;
         com->uscsi_buflen  = 0x04;
-        com->uscsi_timeout = 300;
+        com->uscsi_timeout = 3 * un->un_cmd_timeout;
         com->uscsi_flags   = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_READ;
 
         rval = sd_send_scsi_cmd(dev, com, FKIOCTL, UIO_SYSSPACE,
             SD_PATH_STANDARD);
         if (un->un_f_cfg_read_toc_trk_bcd == TRUE) {

@@ -29338,12 +29373,10 @@
                         if (wmp == NULL)
                                 wmp = kmem_cache_alloc(un->un_wm_cache,
                                     KM_NOSLEEP);
                         if (wmp == NULL) {
                                 mutex_exit(SD_MUTEX(un));
-                                _NOTE(DATA_READABLE_WITHOUT_LOCK
-                                    (sd_lun::un_wm_cache))
                                 wmp = kmem_cache_alloc(un->un_wm_cache,
                                     KM_SLEEP);
                                 mutex_enter(SD_MUTEX(un));
                                 /*
                                  * we released the mutex so recheck and go to

@@ -29738,11 +29771,11 @@
  *
  *     Context: may execute in interrupt context.
  */
 
 static void
-sd_failfast_flushq(struct sd_lun *un)
+sd_failfast_flushq(struct sd_lun *un, boolean_t flush_all)
 {
         struct buf *bp;
         struct buf *next_waitq_bp;
         struct buf *prev_waitq_bp = NULL;
 

@@ -29756,11 +29789,12 @@
 
         /*
          * Check if we should flush all bufs when entering failfast state, or
          * just those with B_FAILFAST set.
          */
-        if (sd_failfast_flushctl & SD_FAILFAST_FLUSH_ALL_BUFS) {
+        if ((sd_failfast_flushctl & SD_FAILFAST_FLUSH_ALL_BUFS) ||
+            flush_all) {
                 /*
                  * Move *all* bp's on the wait queue to the failfast flush
                  * queue, including those that do NOT have B_FAILFAST set.
                  */
                 if (un->un_failfast_headp == NULL) {

@@ -30016,11 +30050,10 @@
  * causing faults in multiple layers of the driver.
  *
  */
 
 #ifdef SD_FAULT_INJECTION
-static uint_t   sd_fault_injection_on = 0;
 
 /*
  *    Function: sd_faultinjection_ioctl()
  *
  * Description: This routine is the driver entry point for handling

@@ -30029,15 +30062,16 @@
  *
  *   Arguments: cmd     - the ioctl cmd received
  *              arg     - the arguments from user and returns
  */
 
-static void
+static int
 sd_faultinjection_ioctl(int cmd, intptr_t arg,  struct sd_lun *un)
 {
         uint_t i = 0;
         uint_t rval;
+        int ret = 0;
 
         SD_TRACE(SD_LOG_IOERR, un, "sd_faultinjection_ioctl: entry\n");
 
         mutex_enter(SD_MUTEX(un));
 

@@ -30063,10 +30097,11 @@
                 for (i = 0; i < SD_FI_MAX_ERROR; i++) {
                         un->sd_fi_fifo_pkt[i] = NULL;
                         un->sd_fi_fifo_xb[i] = NULL;
                         un->sd_fi_fifo_un[i] = NULL;
                         un->sd_fi_fifo_arq[i] = NULL;
+                        un->sd_fi_fifo_tran[i] = NULL;
                 }
                 un->sd_fi_fifo_start = 0;
                 un->sd_fi_fifo_end = 0;
 
                 mutex_enter(&(un->un_fi_mutex));

@@ -30101,14 +30136,19 @@
                         }
                         if (un->sd_fi_fifo_arq[i] != NULL) {
                                 kmem_free(un->sd_fi_fifo_arq[i],
                                     sizeof (struct sd_fi_arq));
                         }
+                        if (un->sd_fi_fifo_tran[i] != NULL) {
+                                kmem_free(un->sd_fi_fifo_tran[i],
+                                    sizeof (struct sd_fi_tran));
+                        }
                         un->sd_fi_fifo_pkt[i] = NULL;
                         un->sd_fi_fifo_un[i] = NULL;
                         un->sd_fi_fifo_xb[i] = NULL;
                         un->sd_fi_fifo_arq[i] = NULL;
+                        un->sd_fi_fifo_tran[i] = NULL;
                 }
                 un->sd_fi_fifo_start = 0;
                 un->sd_fi_fifo_end = 0;
 
                 SD_INFO(SD_LOG_IOERR, un,

@@ -30120,10 +30160,15 @@
                 SD_INFO(SD_LOG_SDTEST, un,
                     "sd_faultinjection_ioctl: Injecting Fault Insert Pkt\n");
 
                 i = un->sd_fi_fifo_end % SD_FI_MAX_ERROR;
 
+                if (un->sd_fi_fifo_tran[i] != NULL) {
+                        ret = EBUSY;
+                        break;
+                }
+
                 sd_fault_injection_on = 0;
 
                 /* No more that SD_FI_MAX_ERROR allowed in Queue */
                 if (un->sd_fi_fifo_pkt[i] != NULL) {
                         kmem_free(un->sd_fi_fifo_pkt[i],

@@ -30132,32 +30177,101 @@
                 if (arg != NULL) {
                         un->sd_fi_fifo_pkt[i] =
                             kmem_alloc(sizeof (struct sd_fi_pkt), KM_NOSLEEP);
                         if (un->sd_fi_fifo_pkt[i] == NULL) {
                                 /* Alloc failed don't store anything */
+                                ret = ENOMEM;
                                 break;
                         }
                         rval = ddi_copyin((void *)arg, un->sd_fi_fifo_pkt[i],
                             sizeof (struct sd_fi_pkt), 0);
                         if (rval == -1) {
                                 kmem_free(un->sd_fi_fifo_pkt[i],
                                     sizeof (struct sd_fi_pkt));
                                 un->sd_fi_fifo_pkt[i] = NULL;
+                                ret = EFAULT;
+                                break;
                         }
                 } else {
                         SD_INFO(SD_LOG_IOERR, un,
                             "sd_faultinjection_ioctl: pkt null\n");
                 }
                 break;
 
+        case SDIOCINSERTTRAN:
+                /* Store a tran packet struct to be pushed onto fifo. */
+                SD_INFO(SD_LOG_SDTEST, un,
+                    "sd_faultinjection_ioctl: Injecting Fault Insert TRAN\n");
+                i = un->sd_fi_fifo_end % SD_FI_MAX_ERROR;
+
+                /*
+                 * HBA-related fault injections can't be mixed with target-level
+                 * fault injections.
+                 */
+                if (un->sd_fi_fifo_pkt[i] != NULL ||
+                    un->sd_fi_fifo_xb[i] != NULL ||
+                    un->sd_fi_fifo_un[i] != NULL ||
+                    un->sd_fi_fifo_arq[i] != NULL) {
+                        ret = EBUSY;
+                        break;
+                }
+
+                sd_fault_injection_on = 0;
+
+                if (un->sd_fi_fifo_tran[i] != NULL) {
+                        kmem_free(un->sd_fi_fifo_tran[i],
+                            sizeof (struct sd_fi_tran));
+                        un->sd_fi_fifo_tran[i] = NULL;
+                }
+                if (arg != NULL) {
+                        un->sd_fi_fifo_tran[i] =
+                            kmem_alloc(sizeof (struct sd_fi_tran), KM_NOSLEEP);
+                        if (un->sd_fi_fifo_tran[i] == NULL) {
+                                /* Alloc failed don't store anything */
+                                ret = ENOMEM;
+                                break;
+                        }
+                        rval = ddi_copyin((void *)arg, un->sd_fi_fifo_tran[i],
+                            sizeof (struct sd_fi_tran), 0);
+
+                        if (rval == 0) {
+                                switch (un->sd_fi_fifo_tran[i]->tran_cmd) {
+                                        case SD_FLTINJ_CMD_BUSY:
+                                        case SD_FLTINJ_CMD_TIMEOUT:
+                                                break;
+                                        default:
+                                                ret = EINVAL;
+                                                break;
+                                }
+                        } else {
+                                ret = EFAULT;
+                        }
+
+                        if (ret != 0) {
+                                kmem_free(un->sd_fi_fifo_tran[i],
+                                    sizeof (struct sd_fi_tran));
+                                un->sd_fi_fifo_tran[i] = NULL;
+                                break;
+                        }
+                } else {
+                        SD_INFO(SD_LOG_IOERR, un,
+                            "sd_faultinjection_ioctl: tran null\n");
+                }
+                break;
+
         case SDIOCINSERTXB:
                 /* Store a xb struct to be pushed onto fifo */
                 SD_INFO(SD_LOG_SDTEST, un,
                     "sd_faultinjection_ioctl: Injecting Fault Insert XB\n");
 
                 i = un->sd_fi_fifo_end % SD_FI_MAX_ERROR;
 
+                if (un->sd_fi_fifo_tran[i] != NULL) {
+                        ret = EBUSY;
+                        break;
+                }
+
                 sd_fault_injection_on = 0;
 
                 if (un->sd_fi_fifo_xb[i] != NULL) {
                         kmem_free(un->sd_fi_fifo_xb[i],
                             sizeof (struct sd_fi_xb));

@@ -30166,19 +30280,22 @@
                 if (arg != NULL) {
                         un->sd_fi_fifo_xb[i] =
                             kmem_alloc(sizeof (struct sd_fi_xb), KM_NOSLEEP);
                         if (un->sd_fi_fifo_xb[i] == NULL) {
                                 /* Alloc failed don't store anything */
+                                ret = ENOMEM;
                                 break;
                         }
                         rval = ddi_copyin((void *)arg, un->sd_fi_fifo_xb[i],
                             sizeof (struct sd_fi_xb), 0);
 
                         if (rval == -1) {
                                 kmem_free(un->sd_fi_fifo_xb[i],
                                     sizeof (struct sd_fi_xb));
                                 un->sd_fi_fifo_xb[i] = NULL;
+                                ret = EFAULT;
+                                break;
                         }
                 } else {
                         SD_INFO(SD_LOG_IOERR, un,
                             "sd_faultinjection_ioctl: xb null\n");
                 }

@@ -30188,10 +30305,14 @@
                 /* Store a un struct to be pushed onto fifo */
                 SD_INFO(SD_LOG_SDTEST, un,
                     "sd_faultinjection_ioctl: Injecting Fault Insert UN\n");
 
                 i = un->sd_fi_fifo_end % SD_FI_MAX_ERROR;
+                if (un->sd_fi_fifo_tran[i] != NULL) {
+                        ret = EBUSY;
+                        break;
+                }
 
                 sd_fault_injection_on = 0;
 
                 if (un->sd_fi_fifo_un[i] != NULL) {
                         kmem_free(un->sd_fi_fifo_un[i],

@@ -30201,18 +30322,21 @@
                 if (arg != NULL) {
                         un->sd_fi_fifo_un[i] =
                             kmem_alloc(sizeof (struct sd_fi_un), KM_NOSLEEP);
                         if (un->sd_fi_fifo_un[i] == NULL) {
                                 /* Alloc failed don't store anything */
+                                ret = ENOMEM;
                                 break;
                         }
                         rval = ddi_copyin((void *)arg, un->sd_fi_fifo_un[i],
                             sizeof (struct sd_fi_un), 0);
                         if (rval == -1) {
                                 kmem_free(un->sd_fi_fifo_un[i],
                                     sizeof (struct sd_fi_un));
                                 un->sd_fi_fifo_un[i] = NULL;
+                                ret = EFAULT;
+                                break;
                         }
 
                 } else {
                         SD_INFO(SD_LOG_IOERR, un,
                             "sd_faultinjection_ioctl: un null\n");

@@ -30223,10 +30347,14 @@
         case SDIOCINSERTARQ:
                 /* Store a arq struct to be pushed onto fifo */
                 SD_INFO(SD_LOG_SDTEST, un,
                     "sd_faultinjection_ioctl: Injecting Fault Insert ARQ\n");
                 i = un->sd_fi_fifo_end % SD_FI_MAX_ERROR;
+                if (un->sd_fi_fifo_tran[i] != NULL) {
+                        ret = EBUSY;
+                        break;
+                }
 
                 sd_fault_injection_on = 0;
 
                 if (un->sd_fi_fifo_arq[i] != NULL) {
                         kmem_free(un->sd_fi_fifo_arq[i],

@@ -30236,18 +30364,21 @@
                 if (arg != NULL) {
                         un->sd_fi_fifo_arq[i] =
                             kmem_alloc(sizeof (struct sd_fi_arq), KM_NOSLEEP);
                         if (un->sd_fi_fifo_arq[i] == NULL) {
                                 /* Alloc failed don't store anything */
+                                ret = ENOMEM;
                                 break;
                         }
                         rval = ddi_copyin((void *)arg, un->sd_fi_fifo_arq[i],
                             sizeof (struct sd_fi_arq), 0);
                         if (rval == -1) {
                                 kmem_free(un->sd_fi_fifo_arq[i],
                                     sizeof (struct sd_fi_arq));
                                 un->sd_fi_fifo_arq[i] = NULL;
+                                ret = EFAULT;
+                                break;
                         }
 
                 } else {
                         SD_INFO(SD_LOG_IOERR, un,
                             "sd_faultinjection_ioctl: arq null\n");

@@ -30254,11 +30385,11 @@
                 }
 
                 break;
 
         case SDIOCPUSH:
-                /* Push stored xb, pkt, un, and arq onto fifo */
+                /* Push stored xb, pkt, un, arq and tran onto fifo */
                 sd_fault_injection_on = 0;
 
                 if (arg != NULL) {
                         rval = ddi_copyin((void *)arg, &i, sizeof (uint_t), 0);
                         if (rval != -1 &&

@@ -30299,10 +30430,11 @@
                 break;
         }
 
         mutex_exit(SD_MUTEX(un));
         SD_TRACE(SD_LOG_IOERR, un, "sd_faultinjection_ioctl: exit\n");
+        return (ret);
 }
 
 
 /*
  *    Function: sd_injection_log()

@@ -30338,11 +30470,60 @@
         }
 
         mutex_exit(&(un->un_fi_mutex));
 }
 
+/*
+ * This function is called just before sending the packet to the HBA.
+ * Caller must hold per-LUN mutex. Mutex is held locked upon return.
+ */
+static void
+sd_prefaultinjection(struct scsi_pkt *pktp)
+{
+        uint_t i;
+        struct buf *bp;
+        struct sd_lun *un;
+        struct sd_fi_tran *fi_tran;
 
+        ASSERT(pktp != NULL);
+
+        /* pull bp and un from pktp */
+        bp = (struct buf *)pktp->pkt_private;
+        un = SD_GET_UN(bp);
+
+        /* if injection is off return */
+        if (sd_fault_injection_on == 0 ||
+            un->sd_fi_fifo_start == un->sd_fi_fifo_end) {
+                return;
+        }
+
+        ASSERT(un != NULL);
+        ASSERT(mutex_owned(SD_MUTEX(un)));
+
+        /* take next set off fifo */
+        i = un->sd_fi_fifo_start % SD_FI_MAX_ERROR;
+
+        fi_tran = un->sd_fi_fifo_tran[i];
+        if (fi_tran != NULL) {
+                switch (fi_tran->tran_cmd) {
+                        case SD_FLTINJ_CMD_BUSY:
+                                pktp->pkt_flags |= FLAG_PKT_BUSY;
+                                break;
+                        case SD_FLTINJ_CMD_TIMEOUT:
+                                pktp->pkt_flags |= FLAG_PKT_TIMEOUT;
+                                break;
+                        default:
+                                return;
+                }
+        }
+        /*
+         * We don't deallocate any data here - it will be deallocated after
+         * the packet has been processed by the HBA.
+         */
+}
+
+
 /*
  *    Function: sd_faultinjection()
  *
  * Description: This routine takes the pkt and changes its
  *              content based on error injection scenerio.

@@ -30563,19 +30744,16 @@
  * 4. Building default VTOC label
  *
  *     As section 3 says, sd checks if some kinds of devices have VTOC label.
  *     If those devices have no valid VTOC label, sd(7d) will attempt to
  *     create default VTOC for them. Currently sd creates default VTOC label
- *     for all devices on x86 platform (VTOC_16), but only for removable
- *     media devices on SPARC (VTOC_8).
+ *     for all devices on x86 platform (VTOC_16).
  *
  *     -----------------------------------------------------------
  *       removable media hotpluggable platform   |   Default Label
  *     -----------------------------------------------------------
- *             false          false    sparc     |     No
  *             false          true      x86      |     Yes
- *             false          true     sparc     |     Yes
  *             true             x        x       |     Yes
  *     ----------------------------------------------------------
  *
  *
  * 5. Supported blocksizes of target devices

@@ -30604,39 +30782,27 @@
  *     of devices and apply automounting policies to each.
  *
  *
  * 7. fdisk partition management
  *
- *     Fdisk is traditional partition method on x86 platform. Sd(7d) driver
- *     just supports fdisk partitions on x86 platform. On sparc platform, sd
- *     doesn't support fdisk partitions at all. Note: pcfs(7fs) can recognize
- *     fdisk partitions on both x86 and SPARC platform.
+ *     Fdisk is traditional partition method on x86 platform. sd(7D) driver
+ *     just supports fdisk partitions on x86 platform.
  *
  *     -----------------------------------------------------------
  *       platform   removable media  USB/1394  |  fdisk supported
  *     -----------------------------------------------------------
  *        x86         X               X        |       true
- *     ------------------------------------------------------------
- *        sparc       X               X        |       false
- *     ------------------------------------------------------------
+ *     -----------------------------------------------------------
  *
  *
  * 8. MBOOT/MBR
  *
- *     Although sd(7d) doesn't support fdisk on SPARC platform, it does support
- *     read/write mboot for removable media devices on sparc platform.
- *
  *     -----------------------------------------------------------
  *       platform   removable media  USB/1394  |  mboot supported
  *     -----------------------------------------------------------
  *        x86         X               X        |       true
- *     ------------------------------------------------------------
- *        sparc      false           false     |       false
- *        sparc      false           true      |       true
- *        sparc      true            false     |       true
- *        sparc      true            true      |       true
- *     ------------------------------------------------------------
+ *     -----------------------------------------------------------
  *
  *
  * 9.  error handling during opening device
  *
  *     If failed to open a disk device, an errno is returned. For some kinds

@@ -31170,17 +31336,13 @@
  *
  * Description: Will be called when SD driver need to post an ereport.
  *
  *    Context: Kernel thread or interrupt context.
  */
-
-#define DEVID_IF_KNOWN(d) "devid", DATA_TYPE_STRING, (d) ? (d) : "unknown"
-
 static void
 sd_ssc_ereport_post(sd_ssc_t *ssc, enum sd_driver_assessment drv_assess)
 {
-        int uscsi_path_instance = 0;
         uchar_t uscsi_pkt_reason;
         uint32_t uscsi_pkt_state;
         uint32_t uscsi_pkt_statistics;
         uint64_t uscsi_ena;
         uchar_t op_code;

@@ -31219,11 +31381,10 @@
         if (ddi_in_panic() || (un->un_state == SD_STATE_SUSPENDED) ||
             (un->un_state == SD_STATE_DUMPING))
                 return;
 
         uscsi_pkt_reason = ssc->ssc_uscsi_info->ui_pkt_reason;
-        uscsi_path_instance = ssc->ssc_uscsi_cmd->uscsi_path_instance;
         uscsi_pkt_state = ssc->ssc_uscsi_info->ui_pkt_state;
         uscsi_pkt_statistics = ssc->ssc_uscsi_info->ui_pkt_statistics;
         uscsi_ena = ssc->ssc_uscsi_info->ui_ena;
 
         sensep = (uint8_t *)ssc->ssc_uscsi_cmd->uscsi_rqbuf;

@@ -31268,11 +31429,11 @@
          * If drv_assess == SD_FM_DRV_RECOVERY, this should be a recovered
          * command, we will post ereport.io.scsi.cmd.disk.recovered.
          * driver-assessment will always be "recovered" here.
          */
         if (drv_assess == SD_FM_DRV_RECOVERY) {
-                scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, NULL,
+                scsi_fm_ereport_post(un->un_sd, 0, NULL,
                     "cmd.disk.recovered", uscsi_ena, devid, NULL,
                     DDI_NOSLEEP, NULL,
                     FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
                     DEVID_IF_KNOWN(devid),
                     "driver-assessment", DATA_TYPE_STRING, assessment,

@@ -31295,11 +31456,11 @@
          * SSC_FLAGS_INVALID_STATUS - invalid stat-code encountered.
          * SSC_FLAGS_INVALID_DATA - invalid data sent back.
          */
         if (ssc->ssc_flags & ssc_invalid_flags) {
                 if (ssc->ssc_flags & SSC_FLAGS_INVALID_SENSE) {
-                        scsi_fm_ereport_post(un->un_sd, uscsi_path_instance,
+                        scsi_fm_ereport_post(un->un_sd, 0,
                             NULL, "cmd.disk.dev.uderr", uscsi_ena, devid,
                             NULL, DDI_NOSLEEP, NULL,
                             FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
                             DEVID_IF_KNOWN(devid),
                             "driver-assessment", DATA_TYPE_STRING,

@@ -31324,11 +31485,11 @@
                          * For other type of invalid data, the
                          * un-decode-value field would be empty because the
                          * un-decodable content could be seen from upper
                          * level payload or inside un-decode-info.
                          */
-                        scsi_fm_ereport_post(un->un_sd, uscsi_path_instance,
+                        scsi_fm_ereport_post(un->un_sd, 0,
                             NULL,
                             "cmd.disk.dev.uderr", uscsi_ena, devid,
                             NULL, DDI_NOSLEEP, NULL,
                             FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
                             DEVID_IF_KNOWN(devid),

@@ -31366,11 +31527,11 @@
                  * error.
                  */
                 if (ssc->ssc_flags & SSC_FLAGS_TRAN_ABORT)
                         ssc->ssc_flags &= ~SSC_FLAGS_TRAN_ABORT;
 
-                scsi_fm_ereport_post(un->un_sd, uscsi_path_instance, NULL,
+                scsi_fm_ereport_post(un->un_sd, 0, NULL,
                     "cmd.disk.tran", uscsi_ena, NULL, NULL, DDI_NOSLEEP, NULL,
                     FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
                     DEVID_IF_KNOWN(devid),
                     "driver-assessment", DATA_TYPE_STRING,
                     drv_assess == SD_FM_DRV_FATAL ? "fail" : assessment,

@@ -31410,11 +31571,11 @@
                                 /*
                                  * driver-assessment should be "fatal" if
                                  * drv_assess is SD_FM_DRV_FATAL.
                                  */
                                 scsi_fm_ereport_post(un->un_sd,
-                                    uscsi_path_instance, NULL,
+                                    0, NULL,
                                     "cmd.disk.dev.rqs.merr",
                                     uscsi_ena, devid, NULL, DDI_NOSLEEP, NULL,
                                     FM_VERSION, DATA_TYPE_UINT8,
                                     FM_EREPORT_VERS0,
                                     DEVID_IF_KNOWN(devid),

@@ -31459,11 +31620,11 @@
                                  * error), driver-assessment should
                                  * be "fatal" if drv_assess is
                                  * SD_FM_DRV_FATAL.
                                  */
                                 scsi_fm_ereport_post(un->un_sd,
-                                    uscsi_path_instance, NULL,
+                                    0, NULL,
                                     "cmd.disk.dev.rqs.derr",
                                     uscsi_ena, devid,
                                     NULL, DDI_NOSLEEP, NULL,
                                     FM_VERSION,
                                     DATA_TYPE_UINT8, FM_EREPORT_VERS0,

@@ -31514,11 +31675,11 @@
                          * Post ereport.io.scsi.cmd.disk.dev.serr if we got the
                          * stat-code but with sense data unavailable.
                          * driver-assessment will be set based on parameter
                          * drv_assess.
                          */
-                        scsi_fm_ereport_post(un->un_sd, uscsi_path_instance,
+                        scsi_fm_ereport_post(un->un_sd, 0,
                             NULL,
                             "cmd.disk.dev.serr", uscsi_ena,
                             devid, NULL, DDI_NOSLEEP, NULL,
                             FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
                             DEVID_IF_KNOWN(devid),

@@ -31768,14 +31929,17 @@
                 if (rval != 0) {
                         un->un_phy_blocksize = DEV_BSIZE;
                 } else {
                         if (!ISP2(pbsize % DEV_BSIZE) || pbsize == 0) {
                                 un->un_phy_blocksize = DEV_BSIZE;
-                        } else if (pbsize > un->un_phy_blocksize) {
+                        } else if (pbsize > un->un_phy_blocksize &&
+                            !un->un_f_sdconf_phy_blocksize) {
                                 /*
-                                 * Don't reset the physical blocksize
-                                 * unless we've detected a larger value.
+                                 * Reset the physical block size
+                                 * if we've detected a larger value and
+                                 * we didn't already set the physical
+                                 * block size in sd.conf
                                  */
                                 un->un_phy_blocksize = pbsize;
                         }
                 }
         }