--- old/usr/src/uts/common/io/scsi/targets/sd.c	Mon Aug  5 17:31:49 2019
+++ new/usr/src/uts/common/io/scsi/targets/sd.c	Mon Aug  5 17:31:49 2019
@@ -22,210 +22,56 @@
 /*
  * 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 */
@@ -232,7 +78,6 @@
 
 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;
@@ -242,13 +87,14 @@
 
 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
@@ -294,9 +140,6 @@
  */
 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
  */
@@ -325,12 +168,6 @@
 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.
@@ -351,16 +188,42 @@
 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 = {
@@ -392,8 +255,6 @@
  * Vendor specific data name property declarations
  */
 
-#if defined(__fibre) || defined(__i386) ||defined(__amd64)
-
 static sd_tunables seagate_properties = {
 	SEAGATE_THROTTLE_VALUE,
 	0,
@@ -406,7 +267,6 @@
 	0
 };
 
-
 static sd_tunables fujitsu_properties = {
 	FUJITSU_THROTTLE_VALUE,
 	0,
@@ -479,12 +339,6 @@
 	PIRUS_LUN_RESET_ENABLED_FLAG
 };
 
-#endif
-
-#if (defined(__sparc) && !defined(__fibre)) || \
-	(defined(__i386) || defined(__amd64))
-
-
 static sd_tunables elite_properties = {
 	ELITE_THROTTLE_VALUE,
 	0,
@@ -509,8 +363,6 @@
 	0
 };
 
-#endif /* Fibre or not */
-
 static sd_tunables lsi_properties_scsi = {
 	LSI_THROTTLE_VALUE,
 	0,
@@ -560,10 +412,7 @@
 	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
@@ -570,7 +419,6 @@
 #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,
@@ -615,7 +463,6 @@
  *	 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 },
@@ -665,137 +512,136 @@
 	{ "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 },
-	{ "Fujitsu SX300",	SD_CONF_BSET_THROTTLE,  &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 |
-			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 |
-		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 |
-			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 |
-		SD_CONF_BSET_LUN_RESET_ENABLED,
-		&maserati_properties },
-	{ "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 |
-		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 |
-		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 |
-		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 |
-		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 |
-		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     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|
+				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|
+				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|
+				SD_CONF_BSET_LUN_RESET_ENABLED,
+				&maserati_properties },
+	{ "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|
+				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|
+				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|
+				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|
+				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|
+				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     STK6580_6780", SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
-	{ "SUN     SUN_6180", SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
+	{ "SUN     SUN_6180",	SD_CONF_BSET_NRR_COUNT, &lsi_oem_properties },
 	{ "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 },
-	{ "*SUN0207*", SD_CONF_BSET_FAB_DEVID, NULL },
-	{ "*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_properties },
-	{ "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 */
-
+	{ "SYMBIOS",		SD_CONF_BSET_NRR_COUNT, &symbios_properties },
+	{ "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 },
+	{ "*SUN0207*",		SD_CONF_BSET_FAB_DEVID, NULL },
+	{ "*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_properties },
+	{ "LSI",		SD_CONF_BSET_THROTTLE|
+				SD_CONF_BSET_NRR_COUNT,
+				&lsi_properties_scsi },
+	{ " 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
 };
 
@@ -860,298 +706,25 @@
 /*
  * 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);
@@ -1201,7 +774,7 @@
  * 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);
@@ -1253,8 +826,10 @@
 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);
@@ -1263,7 +838,7 @@
 
 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);
@@ -1276,18 +851,9 @@
 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
@@ -1410,7 +976,7 @@
 	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,
@@ -1533,6 +1099,8 @@
 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);
@@ -1627,7 +1195,9 @@
 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.
@@ -1650,7 +1220,7 @@
 /*
  * 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);
 
 /*
@@ -1690,6 +1260,38 @@
 #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:
  *
@@ -1705,18 +1307,22 @@
 #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
 
@@ -2372,10 +1978,6 @@
 /*
  * 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))
@@ -2568,7 +2170,6 @@
 		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);
@@ -2618,7 +2219,6 @@
 		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);
@@ -2668,7 +2268,6 @@
 		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);
@@ -2701,10 +2300,6 @@
 	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);
 	}
@@ -2731,36 +2326,16 @@
 			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;
-			}
+			rval = DDI_PROBE_SUCCESS;
 			break;
-
 		case DTYPE_NOTPRESENT:
 		default:
 			rval = DDI_PROBE_FAILURE;
@@ -2860,12 +2435,25 @@
 	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));
 }
 
 /*
@@ -3213,9 +2801,13 @@
 	status = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP,
 	    SD_TARGET_START, SD_PATH_DIRECT);
 
-	if (status != 0) {
-		if (status == EACCES)
-			has_conflict = TRUE;
+	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);
 	}
 
@@ -3834,7 +3426,7 @@
 
 	/* 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);
 	}
@@ -4198,6 +3790,7 @@
 		    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;
 		}
@@ -4205,6 +3798,30 @@
 		    "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;
@@ -4966,7 +4583,6 @@
 	 * 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))
@@ -5308,8 +4924,90 @@
 	}
 }
 
+/*
+ * 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
@@ -5440,20 +5138,14 @@
 	 * 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) {
-		/*
-		 * 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)) {
+	if (un->un_f_opt_fab_devid == TRUE &&
+	    reservation_flag != SD_TARGET_IS_RESERVED) {
+		if (sd_get_devid(ssc) == EINVAL)
 			/*
 			 * 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) {
@@ -5475,7 +5167,7 @@
 		/* 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
@@ -5837,6 +5529,9 @@
 			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;
@@ -5865,7 +5560,7 @@
  *
  *     Context: Kernel Thread
  */
-
+#ifdef notyet
 static void
 sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi)
 {
@@ -5920,6 +5615,7 @@
 				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);
@@ -6121,8 +5817,8 @@
 	mutex_exit(&un->un_pm_mutex);
 	mutex_exit(SD_MUTEX(un));
 }
+#endif
 
-
 /*
  *    Function: sd_ddi_suspend
  *
@@ -6295,25 +5991,6 @@
 		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");
@@ -6395,17 +6072,6 @@
 		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.
 	 *
@@ -7092,115 +6758,25 @@
 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.
@@ -7295,7 +6871,7 @@
 	 * 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*/
 	}
@@ -7341,92 +6917,51 @@
 		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;
 	}
 
@@ -7444,6 +6979,9 @@
 		}
 	}
 
+	(void) ddi_prop_update_int(DDI_DEV_T_NONE, devi,
+	    "allow-unconstrained-retire", 1);
+
 	/*
 	 * Initialize the Request Sense command for the target
 	 */
@@ -7451,22 +6989,8 @@
 		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
@@ -7493,28 +7017,33 @@
 	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
@@ -7530,12 +7059,12 @@
 		    "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);
 		}
 	}
@@ -7554,7 +7083,7 @@
 	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) {
@@ -7561,17 +7090,23 @@
 		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;
 
-	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 */
@@ -7585,6 +7120,10 @@
 		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;
@@ -7627,6 +7166,7 @@
 	 * 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
@@ -7638,7 +7178,7 @@
 	 */
 	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
@@ -7724,14 +7264,47 @@
 		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
@@ -7755,7 +7328,7 @@
 			    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) {
@@ -7776,7 +7349,7 @@
 			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);
 		}
 
 		/*
@@ -7785,22 +7358,14 @@
 		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)) {
@@ -7807,7 +7372,7 @@
 			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);
 			}
 
@@ -7818,9 +7383,9 @@
 			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 {
@@ -7827,7 +7392,7 @@
 			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);
 			}
 		}
@@ -7834,7 +7399,7 @@
 	} 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);
 	}
 
 	/*
@@ -7842,25 +7407,22 @@
 	 */
 	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;
@@ -7870,7 +7432,7 @@
 		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);
 		}
 	}
@@ -7884,12 +7446,10 @@
 		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);
@@ -7924,15 +7484,283 @@
 			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.
@@ -7996,7 +7824,6 @@
 					    "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
@@ -8004,10 +7831,9 @@
 					 * we have to allow it to continue to
 					 * work.
 					 */
-					if (capacity -1 > DK_MAX_BLOCKS)
+					if (capacity - 1 > DK_MAX_BLOCKS)
+						goto spinup_failed;
 #endif
-					goto spinup_failed;
-#endif
 				}
 
 				/*
@@ -8070,12 +7896,14 @@
 				 * 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;
@@ -8125,7 +7953,9 @@
 	 */
 	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
@@ -8160,7 +7990,7 @@
 	    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);
 	}
 
@@ -8196,26 +8026,11 @@
 	 */
 	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
@@ -8228,30 +8043,13 @@
 	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
@@ -8363,6 +8161,7 @@
 	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
@@ -8380,16 +8179,20 @@
 	/* 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)
 	 */
@@ -8410,123 +8213,23 @@
 	}
 
 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;
-		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));
-	}
-
+	New_state(un, SD_STATE_ATTACH_FAILED);
+	cv_broadcast(&un->un_suspend_cv);
 	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);
+	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));
 
-	/* 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);
 }
 
 
@@ -8556,21 +8259,28 @@
 
 	/*
 	 * 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
-	 *  - Another thread is already detaching this instance
-	 *  - Another thread is currently performing an open
+	 * - Unable to get the sd_lun struct for 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
@@ -8584,10 +8294,6 @@
 
 	dev = sd_make_device(SD_DEVINFO(un));
 
-#ifndef lint
-	_NOTE(COMPETING_THREADS_NOW);
-#endif
-
 	mutex_enter(SD_MUTEX(un));
 
 	/*
@@ -8601,24 +8307,19 @@
 	}
 
 	/*
-	 * 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));
 		/*
@@ -8627,7 +8328,7 @@
 		 */
 		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));
@@ -8684,7 +8385,32 @@
 	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;
@@ -8697,11 +8423,10 @@
 	/* 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
@@ -8708,7 +8433,7 @@
 			 * 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;
@@ -8716,11 +8441,10 @@
 
 	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
@@ -8727,29 +8451,21 @@
 			 * 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;
@@ -8791,9 +8507,9 @@
 		    (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().
@@ -8856,7 +8572,7 @@
 			 * 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;
@@ -8871,7 +8587,7 @@
 			 * 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;
@@ -8880,6 +8596,25 @@
 	/* 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);
 
@@ -8936,6 +8671,16 @@
 		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;
@@ -8966,6 +8711,9 @@
 	/* 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);
 
@@ -9001,20 +8749,20 @@
 		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);
 }
 
@@ -9221,9 +8969,8 @@
 
 	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;
@@ -9248,108 +8995,7 @@
 	}
 }
 
-
-#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) ? \
@@ -9752,9 +9398,8 @@
 
 		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));
@@ -10096,18 +9741,12 @@
 	 * 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);
 	}
@@ -10158,10 +9797,10 @@
 
 	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;
@@ -10170,6 +9809,12 @@
 			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 */
@@ -10260,7 +9905,6 @@
 			    "device not ready or invalid disk block value\n");
 			goto open_fail;
 		}
-#if defined(__i386) || defined(__amd64)
 	} else {
 		uchar_t *cp;
 		/*
@@ -10281,8 +9925,6 @@
 			    (void *)SD_PATH_DIRECT);
 			mutex_enter(SD_MUTEX(un));
 		}
-
-#endif
 	}
 
 	if (otyp == OTYP_LYR) {
@@ -10326,6 +9968,12 @@
 	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);
@@ -10354,6 +10002,7 @@
 		sd_pm_exit(un);
 	}
 open_failed_with_pm:
+open_failed_not_attached:
 	sema_v(&un->un_semoclose);
 
 	mutex_enter(&sd_detach_mutex);
@@ -10396,7 +10045,11 @@
 		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);
 	}
 
@@ -10414,8 +10067,9 @@
 
 	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));
 	}
 
@@ -10456,8 +10110,7 @@
 			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
@@ -10471,18 +10124,11 @@
 			 * 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 =
@@ -10532,6 +10178,12 @@
 			}
 
 			/*
+			 * 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.
@@ -10568,16 +10220,11 @@
 	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);
-	}
 
+	mutex_exit(&sd_detach_mutex);
+
 	return (rval);
 }
 
@@ -10673,7 +10320,7 @@
 		 */
 		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);
 		}
@@ -10879,13 +10526,23 @@
 	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));
 		/*
@@ -10897,6 +10554,8 @@
 		    (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));
 
@@ -10911,6 +10570,8 @@
 
 		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)
@@ -10970,12 +10631,23 @@
 	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));
 		/*
@@ -10987,6 +10659,8 @@
 		    (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));
 
@@ -11002,6 +10676,8 @@
 		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);
@@ -11060,12 +10736,23 @@
 	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));
 		/*
@@ -11077,6 +10764,8 @@
 		    (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));
 
@@ -11092,6 +10781,8 @@
 		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);
@@ -11150,12 +10841,24 @@
 	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));
 		/*
@@ -11167,6 +10870,8 @@
 		    (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));
 
@@ -11182,6 +10887,8 @@
 		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);
@@ -11311,40 +11018,6 @@
 
 
 /*
- * 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
@@ -11422,21 +11095,17 @@
 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)));
@@ -11453,10 +11122,22 @@
 	 * 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++;
 
 	/*
@@ -11484,9 +11165,14 @@
 	 * 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
  *
@@ -11669,7 +11355,7 @@
 	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);
@@ -11706,6 +11392,15 @@
 	    (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) {
@@ -11780,9 +11475,8 @@
 	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
@@ -11949,6 +11643,7 @@
 	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;
@@ -11992,6 +11687,7 @@
 	 */
 	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:"
@@ -12060,6 +11756,10 @@
 	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
@@ -12428,6 +12128,8 @@
 
 		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);
@@ -12478,6 +12180,8 @@
 
 	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);
 
@@ -12875,10 +12579,8 @@
 		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);
@@ -13577,12 +13279,10 @@
 	 * 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.
@@ -13620,7 +13320,7 @@
 	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;
 }
 
@@ -13672,7 +13372,6 @@
 
 	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.
@@ -13688,7 +13387,6 @@
 	} else {
 		pktp = NULL;
 	}
-#endif /* __i386 || __amd64 */
 
 	startblock = xp->xb_blkno;	/* Absolute block num. */
 	blockcount = SD_BYTES2TGTBLOCKS(un, bp->b_bcount);
@@ -13736,9 +13434,7 @@
 		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);
@@ -14402,16 +14098,11 @@
 	}
 
 	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;
@@ -14500,16 +14191,7 @@
 	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.)
@@ -14516,12 +14198,8 @@
 	 */
 	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));
 }
@@ -14747,9 +14425,7 @@
 	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;
 
@@ -14760,10 +14436,26 @@
 
 	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
@@ -14820,9 +14512,7 @@
 				    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,
@@ -14899,9 +14589,13 @@
 		 * 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);
 		}
 
@@ -14908,7 +14602,6 @@
 		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
@@ -14917,9 +14610,6 @@
 		 */
 		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.
@@ -14987,8 +14677,6 @@
 				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
@@ -15060,12 +14748,6 @@
 					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;
@@ -15086,9 +14768,9 @@
 
 			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: "
@@ -15097,8 +14779,8 @@
 
 			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: "
@@ -15137,6 +14819,9 @@
 
 		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.
@@ -15150,6 +14835,14 @@
 		    "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));
@@ -15167,13 +14860,36 @@
 			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,
@@ -15182,7 +14898,6 @@
 				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
@@ -15195,7 +14910,6 @@
 				scsi_dmafree(xp->xb_pktp);
 				xp->xb_pkt_flags |= SD_XB_DMA_FREED;
 			}
-#endif
 
 			if (SD_IS_DIRECT_PRIORITY(SD_GET_XBUF(bp))) {
 				/*
@@ -15242,7 +14956,7 @@
 
 			/*
 			 * 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,
@@ -15371,7 +15085,7 @@
 			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? */
 		}
 	}
 
@@ -15587,6 +15301,12 @@
  *		   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.)
@@ -15691,6 +15411,23 @@
 		}
 	}
 
+	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
@@ -15742,7 +15479,7 @@
 				 */
 				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;
@@ -15783,8 +15520,14 @@
 		 * 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.
 		 */
-		un->un_failfast_state = SD_FAILFAST_INACTIVE;
+		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;
 	}
 
 
@@ -16225,9 +15968,8 @@
  *     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);
@@ -16254,14 +15996,11 @@
 	 *    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,
@@ -16432,10 +16171,10 @@
 	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_comp = sdintr;
+	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
@@ -16470,19 +16209,16 @@
 	 * 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,
@@ -16777,7 +16513,48 @@
 	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
  *
@@ -16795,6 +16572,8 @@
 	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;
@@ -16831,7 +16610,30 @@
 	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: "
@@ -16930,7 +16732,6 @@
 			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
@@ -16946,7 +16747,6 @@
 			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");
@@ -17022,8 +16822,6 @@
 	}
 
 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
@@ -17051,7 +16849,6 @@
 		scsi_dmafree(pktp);
 		xp->xb_pkt_flags |= SD_XB_DMA_FREED;
 	}
-#endif
 
 	/*
 	 * The command did not successfully complete as requested so check
@@ -17069,7 +16866,8 @@
 		 */
 		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);
 		}
@@ -17713,21 +17511,9 @@
 	 * 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);
 }
@@ -19173,7 +18959,7 @@
 	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);
 }
 
@@ -19276,22 +19062,14 @@
 	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");
@@ -20047,7 +19825,7 @@
 	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);
@@ -20263,7 +20041,7 @@
 	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
@@ -20307,11 +20085,21 @@
 		 *		(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;
 
 		/*
@@ -20485,7 +20273,7 @@
 	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);
@@ -20704,7 +20492,7 @@
 	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);
@@ -20807,7 +20595,7 @@
 	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 :
@@ -20904,7 +20692,7 @@
 	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);
@@ -21010,7 +20798,7 @@
 	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: {
@@ -21205,7 +20993,7 @@
 	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
@@ -21352,9 +21140,8 @@
 	}
 
 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);
@@ -21366,8 +21153,226 @@
 	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.
@@ -21423,7 +21428,7 @@
 	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;
@@ -21516,7 +21521,7 @@
 	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;
@@ -21629,7 +21634,7 @@
 	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);
@@ -21748,7 +21753,7 @@
 	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);
@@ -21886,7 +21891,7 @@
 	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);
 
@@ -21966,7 +21971,7 @@
 	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);
@@ -22098,7 +22103,7 @@
 	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);
@@ -22180,11 +22185,6 @@
 
 	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,
@@ -22193,9 +22193,18 @@
 	 */
 	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
@@ -22205,7 +22214,15 @@
 	 * 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) {
@@ -22226,12 +22243,9 @@
 		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:
@@ -22255,6 +22269,8 @@
 			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;
@@ -22266,6 +22282,8 @@
 			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;
@@ -22278,6 +22296,8 @@
 				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;
@@ -22336,6 +22356,8 @@
 				}
 				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;
@@ -22379,9 +22401,7 @@
 	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 */
@@ -22760,14 +22780,10 @@
 
 	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);
@@ -23058,11 +23074,11 @@
 	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 */
@@ -23103,6 +23119,20 @@
 		}
 		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;
@@ -23248,6 +23278,8 @@
 	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));
 
 
@@ -23261,6 +23293,8 @@
 	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:
@@ -23365,7 +23399,7 @@
  *		dki_pbsize	- physical block size (if requested)
  *
  * Return Code: 0
- *		EACCESS
+ *		EACCES
  *		EFAULT
  *		ENXIO
  *		EIO
@@ -23483,11 +23517,10 @@
 		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.
-		 */
-		pbsize = MAX(pbsize, un->un_phy_blocksize);
+		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 ||
@@ -23747,6 +23780,8 @@
 		 */
 		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
@@ -24240,7 +24275,7 @@
  *		0  - log page not found.
  *  		1  - log page found.
  */
-
+#ifdef notyet
 static int
 sd_log_page_supported(sd_ssc_t *ssc, int log_page)
 {
@@ -24305,8 +24340,8 @@
 	kmem_free(log_page_data, 0xFF);
 	return (match);
 }
+#endif
 
-
 /*
  *    Function: sd_mhdioc_failfast
  *
@@ -25742,12 +25777,12 @@
 
 	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);
@@ -27991,7 +28026,7 @@
 	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,
@@ -29340,8 +29375,6 @@
 				    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));
@@ -29740,7 +29773,7 @@
  */
 
 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;
@@ -29758,7 +29791,8 @@
 	 * 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.
@@ -30018,7 +30052,6 @@
  */
 
 #ifdef SD_FAULT_INJECTION
-static uint_t   sd_fault_injection_on = 0;
 
 /*
  *    Function: sd_faultinjection_ioctl()
@@ -30031,11 +30064,12 @@
  *		arg	- the arguments from user and returns
  */
 
-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)
 {
 	uint_t i = 0;
 	uint_t rval;
+	int ret = 0;
 
 	SD_TRACE(SD_LOG_IOERR, un, "sd_faultinjection_ioctl: entry\n");
 
@@ -30065,6 +30099,7 @@
 			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;
@@ -30103,10 +30138,15 @@
 				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;
@@ -30122,6 +30162,11 @@
 
 		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 */
@@ -30134,6 +30179,7 @@
 			    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],
@@ -30142,6 +30188,8 @@
 				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,
@@ -30149,6 +30197,67 @@
 		}
 		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,
@@ -30156,6 +30265,11 @@
 
 		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) {
@@ -30168,6 +30282,7 @@
 			    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],
@@ -30177,6 +30292,8 @@
 				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,
@@ -30190,6 +30307,10 @@
 		    "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;
 
@@ -30203,6 +30324,7 @@
 			    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],
@@ -30211,6 +30333,8 @@
 				kmem_free(un->sd_fi_fifo_un[i],
 				    sizeof (struct sd_fi_un));
 				un->sd_fi_fifo_un[i] = NULL;
+				ret = EFAULT;
+				break;
 			}
 
 		} else {
@@ -30225,6 +30349,10 @@
 		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;
 
@@ -30238,6 +30366,7 @@
 			    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],
@@ -30246,6 +30375,8 @@
 				kmem_free(un->sd_fi_fifo_arq[i],
 				    sizeof (struct sd_fi_arq));
 				un->sd_fi_fifo_arq[i] = NULL;
+				ret = EFAULT;
+				break;
 			}
 
 		} else {
@@ -30256,7 +30387,7 @@
 		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) {
@@ -30301,6 +30432,7 @@
 
 	mutex_exit(SD_MUTEX(un));
 	SD_TRACE(SD_LOG_IOERR, un, "sd_faultinjection_ioctl: exit\n");
+	return (ret);
 }
 
 
@@ -30340,7 +30472,56 @@
 	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()
  *
@@ -30565,15 +30746,12 @@
  *     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
  *     ----------------------------------------------------------
  *
@@ -30606,35 +30784,23 @@
  *
  * 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
@@ -31172,13 +31338,9 @@
  *
  *    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;
@@ -31221,7 +31383,6 @@
 		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;
@@ -31270,7 +31431,7 @@
 	 * 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,
@@ -31297,7 +31458,7 @@
 	 */
 	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,
@@ -31326,7 +31487,7 @@
 			 * 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,
@@ -31368,7 +31529,7 @@
 		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),
@@ -31412,7 +31573,7 @@
 				 * 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,
@@ -31461,7 +31622,7 @@
 				 * 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,
@@ -31516,7 +31677,7 @@
 			 * 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,
@@ -31770,10 +31931,13 @@
 		} 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;
 			}
