6502757 sata: deadlock possible when device reset occurs during cfgadm operations
authorpawelw
Fri, 30 Mar 2007 11:43:17 -0700
changeset 3935 0767d871beb3
parent 3934 273f6bb7e684
child 3936 9bfdf69914f6
6502757 sata: deadlock possible when device reset occurs during cfgadm operations 6502181 sata module should better inform target driver about device removal 6508809 sata: better handling of unexpected device removal and re-insertion is needed 6493063 sata framework incorrectly tests for partial information when doing IDENTIFY DEVICE 6503211 thumper:panic in sata module due to inadequate handling of memory shortage 6484766 SATA hotplug panics system (possibly with ZFS as root) 6520862 DKIOCSTATE to a SATA device panics system
usr/src/uts/common/io/sata/impl/sata.c
usr/src/uts/common/sys/sata/impl/sata.h
--- a/usr/src/uts/common/io/sata/impl/sata.c	Fri Mar 30 08:46:35 2007 -0700
+++ b/usr/src/uts/common/io/sata/impl/sata.c	Fri Mar 30 11:43:17 2007 -0700
@@ -155,6 +155,8 @@
 static	void sata_process_device_attached(sata_hba_inst_t *, sata_address_t *);
 static	void sata_process_port_pwr_change(sata_hba_inst_t *, sata_address_t *);
 static	void sata_process_cntrl_pwr_level_change(sata_hba_inst_t *);
+static	void sata_process_target_node_cleanup(sata_hba_inst_t *,
+    sata_address_t *);
 
 /* Local functions for ioctl */
 static	int32_t sata_get_port_num(sata_hba_inst_t *,  struct devctl_iocdata *);
@@ -185,6 +187,7 @@
 static 	void sata_txlt_rw_completion(sata_pkt_t *);
 static 	void sata_txlt_atapi_completion(sata_pkt_t *);
 static 	void sata_txlt_nodata_cmd_completion(sata_pkt_t *);
+static 	int sata_emul_rw_completion(sata_pkt_txlate_t *);
 
 static 	struct scsi_extended_sense *sata_immediate_error_response(
     sata_pkt_txlate_t *, int);
@@ -211,14 +214,14 @@
 static	void sata_save_drive_settings(sata_drive_info_t *);
 static	void sata_show_drive_info(sata_hba_inst_t *, sata_drive_info_t *);
 static	void sata_log(sata_hba_inst_t *, uint_t, char *fmt, ...);
-static int sata_fetch_smart_return_status(sata_hba_inst_t *,
+static	int sata_fetch_smart_return_status(sata_hba_inst_t *,
     sata_drive_info_t *);
-static int sata_fetch_smart_data(sata_hba_inst_t *, sata_drive_info_t *,
+static	int sata_fetch_smart_data(sata_hba_inst_t *, sata_drive_info_t *,
     struct smart_data *);
-static int sata_smart_selftest_log(sata_hba_inst_t *,
+static	int sata_smart_selftest_log(sata_hba_inst_t *,
     sata_drive_info_t *,
     struct smart_selftest_log *);
-static int sata_ext_smart_selftest_read_log(sata_hba_inst_t *,
+static	int sata_ext_smart_selftest_read_log(sata_hba_inst_t *,
     sata_drive_info_t *, struct smart_ext_selftest_log *, uint16_t);
 static	int sata_smart_read_log(sata_hba_inst_t *, sata_drive_info_t *,
     uint8_t *, uint8_t, uint8_t);
@@ -226,6 +229,10 @@
     struct read_log_ext_directory *);
 static	void sata_gen_sysevent(sata_hba_inst_t *, sata_address_t *, int);
 static	void sata_xlate_errors(sata_pkt_txlate_t *);
+static	void sata_set_device_removed(dev_info_t *);
+static	boolean_t sata_check_device_removed(dev_info_t *);
+static	void sata_set_target_node_cleanup(sata_hba_inst_t *, int cport);
+
 
 /*
  * SATA Framework will ignore SATA HBA driver cb_ops structure and
@@ -259,7 +266,7 @@
 
 static struct modlmisc modlmisc = {
 	&mod_miscops,			/* Type of module */
-	"Generic SATA Driver v%I%"	/* module name */
+	"SATA Module v%I%"		/* module name */
 };
 
 
@@ -1169,24 +1176,42 @@
 				mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
 				    cport)->cport_mutex);
 				tdip = sata_get_target_dip(dip, comp_port);
-				if (tdip != NULL) {
-					/* target node exist */
-					if (ndi_devi_offline(tdip,
-					    NDI_DEVI_REMOVE) != NDI_SUCCESS) {
-						/*
-						 * Problem
-						 * A target node remained
-						 * attached. This happens when
-						 * the file was open or a node
-						 * was waiting for resources.
-						 * Cannot do anything about it.
-						 */
-						SATA_LOG_D((sata_hba_inst,
-						    CE_WARN,
-						    "sata_hba_ioctl: "
-						    "disconnect: cannot "
-						    "remove target node!!!"));
-					}
+				if (tdip != NULL && ndi_devi_offline(tdip,
+				    NDI_DEVI_REMOVE) != NDI_SUCCESS) {
+					/*
+					 * Problem
+					 * A target node remained
+					 * attached. This happens when
+					 * the file was open or a node
+					 * was waiting for resources.
+					 * Cannot do anything about it.
+					 */
+					SATA_LOG_D((sata_hba_inst, CE_WARN,
+					    "sata_hba_ioctl: "
+					    "disconnect: could not "
+					    "unconfigure device before "
+					    "disconnecting the SATA "
+					    "port %d", cport));
+
+					/*
+					 * Set DEVICE REMOVED state
+					 * in the target node. It
+					 * will prevent access to
+					 * the device even when a
+					 * new device is attached,
+					 * until the old target node
+					 * is released, removed and
+					 * recreated for a new
+					 * device.
+					 */
+					sata_set_device_removed(tdip);
+					/*
+					 * Instruct event daemon to
+					 * try the target node cleanup
+					 * later.
+					 */
+					sata_set_target_node_cleanup(
+					    sata_hba_inst, cport);
 				}
 				mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
 				    cport)->cport_mutex);
@@ -1264,7 +1289,7 @@
 				SATA_LOG_D((sata_hba_inst, CE_WARN,
 				    "sata_hba_ioctl: unconfigure: "
 				    "failed to unconfigure "
-				    "device at cport %d", cport));
+				    "device at SATA port %d", cport));
 				rv = EIO;
 			}
 			/*
@@ -1283,7 +1308,7 @@
 			SATA_LOG_D((sata_hba_inst, CE_WARN,
 			    "sata_hba_ioctl: unconfigure: "
 			    "attempt to unconfigure non-existing device "
-			    "at cport %d", cport));
+			    "at SATA port %d", cport));
 			rv = ENXIO;
 		}
 
@@ -1336,7 +1361,7 @@
 				    cport)->cport_mutex);
 				SATA_LOG_D((sata_hba_inst, CE_WARN,
 				    "sata_hba_ioctl: connect: "
-				    "failed to activate SATA cport %d",
+				    "failed to activate SATA port %d",
 				    cport));
 				rv = EIO;
 				break;
@@ -1443,7 +1468,7 @@
 					    sata_hba_inst, cport)->cport_mutex);
 					SATA_LOG_D((sata_hba_inst, CE_WARN,
 					    "sata_hba_ioctl: configure: "
-					    "failed to activate SATA cport %d",
+					    "failed to activate SATA port %d",
 					    cport));
 					rv = EIO;
 					break;
@@ -1516,26 +1541,55 @@
 		mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
 
 		if ((tdip = sata_get_target_dip(dip, comp_port)) != NULL) {
-			/* target node still exists */
-			if (ndi_devi_online(tdip, 0) != NDI_SUCCESS) {
-				SATA_LOG_D((sata_hba_inst, CE_WARN,
-				    "sata_hba_ioctl: configure: "
-				    "onlining device at cport %d failed",
-				    cport));
-				rv = EIO;
+			/*
+			 * Target node exists. Verify, that it belongs
+			 * to existing, attached device and not to
+			 * a removed device.
+			 */
+			if (sata_check_device_removed(tdip) == B_FALSE) {
+				if (ndi_devi_online(tdip, 0) != NDI_SUCCESS) {
+					SATA_LOG_D((sata_hba_inst, CE_WARN,
+					    "sata_hba_ioctl: configure: "
+					    "onlining device at SATA port %d "
+					    "failed", cport));
+					rv = EIO;
+					break;
+				} else {
+					mutex_enter(&SATA_CPORT_INFO(
+					    sata_hba_inst, cport)->cport_mutex);
+					SATA_CPORT_INFO(sata_hba_inst, cport)->
+					    cport_tgtnode_clean = B_TRUE;
+					mutex_exit(&SATA_CPORT_INFO(
+					    sata_hba_inst, cport)->cport_mutex);
+				}
+			} else {
+				sata_log(sata_hba_inst, CE_WARN,
+				    "SATA device at port %d cannot be "
+				    "configured. "
+				    "Application(s) accessing previously "
+				    "attached device "
+				    "have to release it before newly inserted "
+				    "device can be made accessible.",
+				    cport);
 				break;
 			}
 		} else {
 			/*
 			 * No target node - need to create a new target node.
 			 */
+			mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->
+			    cport_mutex);
+			SATA_CPORT_INFO(sata_hba_inst, cport)->
+			    cport_tgtnode_clean = B_TRUE;
+			mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->
+			    cport_mutex);
 			tdip = sata_create_target_node(dip, sata_hba_inst,
 			    &sata_device.satadev_addr);
 			if (tdip == NULL) {
 				/* configure failed */
 				SATA_LOG_D((sata_hba_inst, CE_WARN,
 				    "sata_hba_ioctl: configure: "
-				    "configuring device at cport %d "
+				    "configuring SATA device at port %d "
 				    "failed", cport));
 				rv = EIO;
 				break;
@@ -1853,7 +1907,7 @@
 
 			/*
 			 * This operation returns EFAULT if either reset
-			 * controller failed or a re-probbing of any ports
+			 * controller failed or a re-probing of any ports
 			 * failed.
 			 * We return here, because common return is for
 			 * a single cport operation.
@@ -1909,7 +1963,7 @@
 						    NULL);
 
 						if (ndi_devi_offline(tdip,
-						    NDI_UNCONFIG) !=
+						    NDI_DEVI_REMOVE) !=
 						    NDI_SUCCESS) {
 							SATA_LOG_D((
 							    sata_hba_inst,
@@ -1918,29 +1972,32 @@
 							    "port deactivate: "
 							    "failed to "
 							    "unconfigure "
-							    "device at cport "
-							    "%d", cport));
+							    "device at port "
+							    "%d before "
+							    "deactivating "
+							    "the port", cport));
 						}
-						if (ndi_devi_offline(tdip,
-						    NDI_DEVI_REMOVE) !=
-						    NDI_SUCCESS) {
-							/*
-							 * Problem;
-							 * target node remained
-							 * attached.
-							 * Too bad...
-							 */
-							SATA_LOG_D((
-							    sata_hba_inst,
-							    CE_WARN,
-							    "sata_hba_ioctl: "
-							    "port deactivate: "
-							    "failed to "
-							    "unconfigure "
-							    "device at "
-							    "cport %d",
-							    cport));
-						}
+
+
+						/*
+						 * Set DEVICE REMOVED state
+						 * in the target node. It
+						 * will prevent access to
+						 * the device even when a
+						 * new device is attached,
+						 * until the old target node
+						 * is released, removed and
+						 * recreated for a new
+						 * device.
+						 */
+						sata_set_device_removed(tdip);
+						/*
+						 * Instruct event daemon to
+						 * try the target node cleanup
+						 * later.
+						 */
+						sata_set_target_node_cleanup(
+						    sata_hba_inst, cport);
 					}
 					mutex_enter(&SATA_CPORT_INFO(
 					    sata_hba_inst, cport)->cport_mutex);
@@ -1997,7 +2054,7 @@
 				}
 				SATA_LOG_D((sata_hba_inst, CE_WARN,
 				    "sata_hba_ioctl: port deactivate: "
-				    "cannot deactivate SATA cport %d",
+				    "cannot deactivate SATA port %d",
 				    cport));
 				rv = EIO;
 			} else {
@@ -2044,7 +2101,7 @@
 				    cport)->cport_mutex);
 				SATA_LOG_D((sata_hba_inst, CE_WARN,
 				    "sata_hba_ioctl: port activate: "
-				    "cannot activate SATA cport %d",
+				    "cannot activate SATA port %d",
 				    cport));
 				rv = EIO;
 				break;
@@ -2524,7 +2581,7 @@
 		if ((ddi_prop_update_int(DDI_DEV_T_NONE, sd->sd_dev,
 		    "pm-capable", 1)) != DDI_PROP_SUCCESS) {
 			sata_log(sata_hba_inst, CE_WARN,
-			"device at port %d: will not be power-managed ",
+			"SATA device at port %d: will not be power-managed ",
 			SCSI_TO_SATA_CPORT(sd->sd_address.a_target));
 			SATA_LOG_D((sata_hba_inst, CE_WARN,
 			"failure updating pm-capable property"));
@@ -2699,7 +2756,12 @@
 	mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
 	    sata_device.satadev_addr.cport)));
 	/*
-	 * Allocate necessary DMA resources for the packet's buffer
+	 * Allocate necessary DMA resources for the packet's data buffer
+	 * NOTE:
+	 * In case of read/write commands, DMA resource allocation here is
+	 * based on the premise that the transfer length specified in
+	 * the read/write scsi cdb will match exactly DMA resources -
+	 * returning correct packet residue is crucial.
 	 */
 	if ((rval = sata_dma_buf_setup(spx, flags, callback, arg,
 	    &cur_dma_attr)) != DDI_SUCCESS) {
@@ -2794,19 +2856,33 @@
 	ASSERT(spx != NULL &&
 	    spx->txlt_scsi_pkt == pkt && spx->txlt_sata_pkt != NULL);
 
-	/*
-	 * Mutex-protected section below is just to identify device type
-	 * and switch to ATAPI processing, if necessary
-	 */
 	cport = SCSI_TO_SATA_CPORT(ap->a_target);
 
 	mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
-
 	sdinfo = sata_get_device_info(sata_hba_inst,
 	    &spx->txlt_sata_pkt->satapkt_device);
-	if (sdinfo == NULL) {
+	if (sdinfo == NULL ||
+	    SATA_CPORT_INFO(sata_hba_inst, cport)->cport_tgtnode_clean ==
+	    B_FALSE) {
 		mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
-		spx->txlt_scsi_pkt->pkt_reason = CMD_DEV_GONE;
+		pkt->pkt_reason = CMD_DEV_GONE;
+		/*
+		 * The sd target driver is checking CMD_DEV_GONE pkt_reason
+		 * only in callback function (for normal requests) and
+		 * in the dump code path.
+		 * So, if the callback is available, we need to do
+		 * the callback rather than returning TRAN_FATAL_ERROR here.
+		 */
+		if (pkt->pkt_comp != NULL) {
+			/* scsi callback required */
+			if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
+			    (task_func_t *)pkt->pkt_comp,
+			    (void *)pkt, TQ_SLEEP) == NULL)
+				/* Scheduling the callback failed */
+				return (TRAN_BUSY);
+			return (TRAN_ACCEPT);
+		}
+		/* No callback available */
 		return (TRAN_FATAL_ERROR);
 	}
 
@@ -3297,8 +3373,12 @@
 		 * Free DMA resources - cookies and handles
 		 */
 		ASSERT(spx->txlt_dma_cookie_list != NULL);
-		(void) kmem_free(spx->txlt_dma_cookie_list,
-		    spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t));
+		if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) {
+			(void) kmem_free(spx->txlt_dma_cookie_list,
+			    spx->txlt_dma_cookie_list_len *
+			    sizeof (ddi_dma_cookie_t));
+			spx->txlt_dma_cookie_list = NULL;
+		}
 		(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
 		(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
 	}
@@ -3329,8 +3409,12 @@
 		 * Free DMA resources - cookies and handles
 		 */
 		ASSERT(spx->txlt_dma_cookie_list != NULL);
-		(void) kmem_free(spx->txlt_dma_cookie_list,
-		    spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t));
+		if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) {
+			(void) kmem_free(spx->txlt_dma_cookie_list,
+			    spx->txlt_dma_cookie_list_len *
+			    sizeof (ddi_dma_cookie_t));
+			spx->txlt_dma_cookie_list = NULL;
+		}
 		(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
 		(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
 	}
@@ -3423,6 +3507,24 @@
 	case 1:
 		/* valid address but no device - it has disappeared ? */
 		spx->txlt_scsi_pkt->pkt_reason = CMD_DEV_GONE;
+		/*
+		 * The sd target driver is checking CMD_DEV_GONE pkt_reason
+		 * only in callback function (for normal requests) and
+		 * in the dump code path.
+		 * So, if the callback is available, we need to do
+		 * the callback rather than returning TRAN_FATAL_ERROR here.
+		 */
+		if (spx->txlt_scsi_pkt->pkt_comp != NULL) {
+			/* scsi callback required */
+			if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
+			    (task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
+			    (void *)spx->txlt_scsi_pkt,
+			    TQ_SLEEP) == NULL)
+				/* Scheduling the callback failed */
+				return (TRAN_BUSY);
+
+			return (TRAN_ACCEPT);
+		}
 		return (TRAN_FATAL_ERROR);
 	default:
 		/* all OK */
@@ -3433,16 +3535,27 @@
 
 	/*
 	 * If device is in reset condition, reject the packet with
-	 * TRAN_BUSY
+	 * TRAN_BUSY, unless:
+	 * 1. system is panicking (dumping)
+	 * In such case only one thread is running and there is no way to
+	 * process reset.
+	 * 2. cfgadm operation is is progress (internal APCTL lock is set)
+	 * Some cfgadm operations involve drive commands, so reset condition
+	 * needs to be ignored for IOCTL operations.
 	 */
 	if ((sdinfo->satadrv_event_flags &
-	    (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) &&
-	    !ddi_in_panic()) {
-		spx->txlt_scsi_pkt->pkt_reason = CMD_INCOMPLETE;
-		SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
-		    "sata_scsi_start: rejecting command because "
-		    "of device reset state\n", NULL);
-		return (TRAN_BUSY);
+	    (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) != 0) {
+
+		if (!ddi_in_panic() &&
+		    ((SATA_CPORT_EVENT_FLAGS(spx->txlt_sata_hba_inst,
+		    sata_device.satadev_addr.cport) &
+		    SATA_APCTL_LOCK_PORT_BUSY) == 0)) {
+			spx->txlt_scsi_pkt->pkt_reason = CMD_INCOMPLETE;
+			SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
+			    "sata_scsi_start: rejecting command because "
+			    "of device reset state\n", NULL);
+			return (TRAN_BUSY);
+		}
 	}
 
 	/*
@@ -3455,6 +3568,12 @@
 	spx->txlt_sata_pkt->satapkt_device.satadev_type = sdinfo->satadrv_type;
 
 	spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags = sata_initial_cmd_flags;
+	if ((SATA_CPORT_INFO(spx->txlt_sata_hba_inst,
+	    sata_device.satadev_addr.cport)->cport_event_flags &
+	    SATA_APCTL_LOCK_PORT_BUSY) != 0) {
+		spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags.
+		    sata_ignore_dev_reset = B_TRUE;
+	}
 
 	spx->txlt_scsi_pkt->pkt_reason = CMD_CMPLT;
 
@@ -3588,7 +3707,7 @@
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
 		    (void *)spx->txlt_scsi_pkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 	return (TRAN_ACCEPT);
@@ -3628,7 +3747,7 @@
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
 		    (void *)spx->txlt_scsi_pkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 	return (TRAN_ACCEPT);
@@ -3788,7 +3907,7 @@
 				 * identifiers are common for SATA devices
 				 * But not now.
 				 */
-				/*FALLTHRU*/
+				/*FALLTHROUGH*/
 
 			default:
 				/* Request for unsupported VPD page */
@@ -3816,7 +3935,7 @@
 		/* scsi callback required */
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 	}
@@ -3875,7 +3994,7 @@
 		/* scsi callback required */
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 	return (TRAN_ACCEPT);
@@ -3933,7 +4052,7 @@
 		/* scsi callback required */
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 
@@ -3991,7 +4110,7 @@
 			/* scsi callback required */
 			if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 			    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-			    TQ_SLEEP) == 0)
+			    TQ_SLEEP) == NULL)
 				/* Scheduling the callback failed */
 				return (TRAN_BUSY);
 
@@ -4119,7 +4238,7 @@
 		/* scsi callback required */
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 
@@ -4373,7 +4492,7 @@
 		/* scsi callback required */
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 
@@ -4632,7 +4751,7 @@
 		/* scsi callback required */
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 
@@ -4845,7 +4964,7 @@
 		/* scsi callback required */
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 
@@ -4913,9 +5032,7 @@
 
 	scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
 	/*
-	 * Build cmd block depending on the device capability and
-	 * requested operation mode.
-	 * Do not bother with non-dma mode.
+	 * Extract LBA and sector count from scsi CDB.
 	 */
 	switch ((uint_t)scsipkt->pkt_cdbp[0]) {
 	case SCMD_READ:
@@ -4977,6 +5094,22 @@
 		return (sata_txlt_lba_out_of_range(spx));
 	}
 
+	/*
+	 * For zero-length transfer, emulate good completion of the command
+	 * (reasons for rejecting the command were already checked).
+	 * No DMA resources were allocated.
+	 */
+	if (spx->txlt_dma_cookie_list == NULL) {
+		mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx)));
+		return (sata_emul_rw_completion(spx));
+	}
+
+	/*
+	 * Build cmd block depending on the device capability and
+	 * requested operation mode.
+	 * Do not bother with non-dma mode - we are working only with
+	 * devices supporting DMA.
+	 */
 	scmd->satacmd_addr_type = ATA_ADDR_LBA;
 	scmd->satacmd_device_reg = SATA_ADH_LBA;
 	scmd->satacmd_cmd_reg = SATAC_READ_DMA;
@@ -5129,9 +5262,7 @@
 
 	scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE;
 	/*
-	 * Build cmd block depending on the device capability and
-	 * requested operation mode.
-	 * Do not bother with non-dma mode.
+	 * Extract LBA and sector count from scsi CDB
 	 */
 	switch ((uint_t)scsipkt->pkt_cdbp[0]) {
 	case SCMD_WRITE:
@@ -5193,6 +5324,22 @@
 		return (sata_txlt_lba_out_of_range(spx));
 	}
 
+	/*
+	 * For zero-length transfer, emulate good completion of the command
+	 * (reasons for rejecting the command were already checked).
+	 * No DMA resources were allocated.
+	 */
+	if (spx->txlt_dma_cookie_list == NULL) {
+		mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx)));
+		return (sata_emul_rw_completion(spx));
+	}
+
+	/*
+	 * Build cmd block depending on the device capability and
+	 * requested operation mode.
+	 * Do not bother with non-dma mode- we are working only with
+	 * devices supporting DMA.
+	 */
 	scmd->satacmd_addr_type = ATA_ADDR_LBA;
 	scmd->satacmd_device_reg = SATA_ADH_LBA;
 	scmd->satacmd_cmd_reg = SATAC_WRITE_DMA;
@@ -5575,11 +5722,11 @@
 		    spx->txlt_sata_pkt->satapkt_device.satadev_addr.qual ==
 		    SATA_ADDR_DCPORT)
 			sata_log(sata_hba_inst, CE_CONT,
-			    "port %d error",
+			    "SATA port %d error",
 			    sata_device->satadev_addr.cport);
 		else
 			sata_log(sata_hba_inst, CE_CONT,
-			    "port %d pmport %d error\n",
+			    "SATA port %d pmport %d error\n",
 			    sata_device->satadev_addr.cport,
 			    sata_device->satadev_addr.pmport);
 
@@ -5702,7 +5849,7 @@
 		/* scsi callback required */
 		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
 		    (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt,
-		    TQ_SLEEP) == 0)
+		    TQ_SLEEP) == NULL)
 			/* Scheduling the callback failed */
 			return (TRAN_BUSY);
 	return (TRAN_ACCEPT);
@@ -5824,6 +5971,33 @@
 
 
 /*
+ * Emulated SATA Read/Write command completion for zero-length requests.
+ * This request always succedes, so in synchronous mode it always returns
+ * TRAN_ACCEPT, and in non-synchronous mode it may return TRAN_BUSY if the
+ * callback cannot be scheduled.
+ */
+static int
+sata_emul_rw_completion(sata_pkt_txlate_t *spx)
+{
+	struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
+
+	scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+	    STATE_SENT_CMD | STATE_GOT_STATUS;
+	scsipkt->pkt_reason = CMD_CMPLT;
+	*scsipkt->pkt_scbp = STATUS_GOOD;
+	if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) {
+		/* scsi callback required - have to schedule it */
+		if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
+		    (task_func_t *)scsipkt->pkt_comp,
+		    (void *)scsipkt, TQ_SLEEP) == NULL)
+			/* Scheduling the callback failed */
+			return (TRAN_BUSY);
+	}
+	return (TRAN_ACCEPT);
+}
+
+
+/*
  * Translate completion status of SATA read/write commands into scsi response.
  * pkt completion_reason is checked to determine the completion status.
  * Do scsi callback if necessary.
@@ -5983,7 +6157,6 @@
 	    scsipkt->pkt_comp != NULL)
 		/* scsi callback required */
 		(*scsipkt->pkt_comp)(scsipkt);
-
 }
 
 /*
@@ -7211,7 +7384,7 @@
 		    minor_number, DDI_NT_SATA_ATTACHMENT_POINT, 0) !=
 		    DDI_SUCCESS) {
 			sata_log(sata_hba_inst, CE_WARN, "sata_hba_attach: "
-			    "cannot create sata attachment point for port %d",
+			    "cannot create SATA attachment point for port %d",
 			    ncport);
 		}
 
@@ -7323,7 +7496,7 @@
 				    0) != DDI_SUCCESS) {
 					sata_log(sata_hba_inst, CE_WARN,
 					    "sata_hba_attach: "
-					    "cannot create sata attachment "
+					    "cannot create SATA attachment "
 					    "point for port %d pmult port %d",
 					    ncport, npmport);
 				}
@@ -7491,6 +7664,7 @@
 		mutex_enter(&cportinfo->cport_mutex);
 		sata_show_drive_info(sata_hba_inst,
 		    SATA_CPORTINFO_DRV_INFO(cportinfo));
+		cportinfo->cport_tgtnode_clean = B_TRUE;
 		mutex_exit(&cportinfo->cport_mutex);
 		cdip = sata_create_target_node(pdip, sata_hba_inst,
 		    &sata_device.satadev_addr);
@@ -7557,6 +7731,7 @@
 		mutex_enter(&cportinfo->cport_mutex);
 		sata_show_drive_info(sata_hba_inst,
 		    pmportinfo->pmport_sata_drive);
+		pmportinfo->pmport_tgtnode_clean = B_TRUE;
 		mutex_exit(&cportinfo->cport_mutex);
 		cdip = sata_create_target_node(pdip, sata_hba_inst,
 		    &sata_device.satadev_addr);
@@ -7590,6 +7765,8 @@
  *
  * A dev_info_t pointer is returned if operation is successful, NULL is
  * returned otherwise.
+ *
+ * No port multiplier support.
  */
 
 static dev_info_t *
@@ -7728,7 +7905,7 @@
 		    "node removal failed %d", rval));
 	}
 	sata_log(sata_hba_inst, CE_WARN, "sata_create_target_node: "
-	    "cannot create target node for device at port %d",
+	    "cannot create target node for SATA device at port %d",
 	    sata_addr->cport);
 	return (NULL);
 }
@@ -7795,8 +7972,9 @@
 	if (rval != SATA_SUCCESS) {
 		cportinfo->cport_state = SATA_PSTATE_FAILED;
 		mutex_exit(&cportinfo->cport_mutex);
-		SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: "
-		    "connect: port probbing failed"));
+		SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_port: "
+		    "SATA port %d probing failed",
+		    cportinfo->cport_addr.cport));
 		return (SATA_FAILURE);
 	}
 
@@ -8691,11 +8869,12 @@
 	ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
 	spx->txlt_buf_dma_handle = 0;
 
-	kmem_free(spx->txlt_dma_cookie_list,
-	    spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t));
-	spx->txlt_dma_cookie_list = NULL;
-	spx->txlt_dma_cookie_list_len = 0;
-
+	if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) {
+		kmem_free(spx->txlt_dma_cookie_list,
+		    spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t));
+		spx->txlt_dma_cookie_list = NULL;
+		spx->txlt_dma_cookie_list_len = 0;
+	}
 	/* Free buffer */
 	scsi_free_consistent_buf(spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp);
 }
@@ -8819,9 +8998,13 @@
  * This function handles initial DMA resource allocation as well as
  * DMA window shift and may be called repeatedly for the same DMA window
  * until all DMA cookies in the DMA window are processed.
- *
- * Returns DDI_SUCCESS upon successful operation,
- * returns failure code returned by failing commands or DDI_FAILURE when
+ * To guarantee that there is always a coherent set of cookies to process
+ * by SATA HBA driver (observing alignment, device granularity, etc.),
+ * the number of slots for DMA cookies is equal to lesser of  a number of
+ * cookies in a DMA window and a max number of scatter/gather entries.
+ *
+ * Returns DDI_SUCCESS upon successful operation.
+ * Return failure code of a failing command or DDI_FAILURE when
  * internal cleanup failed.
  */
 static int
@@ -8829,15 +9012,13 @@
     int (*callback)(caddr_t), caddr_t arg,
     ddi_dma_attr_t *cur_dma_attr)
 {
-	int			rval;
-	ddi_dma_cookie_t	cookie;
-	off_t			offset;
-	size_t			size;
-	int			max_sg_len, req_sg_len, i;
-	uint_t			dma_flags;
-	struct buf		*bp;
-	uint64_t		max_txfer_len;
-	uint64_t		cur_txfer_len;
+	int	rval;
+	off_t	offset;
+	size_t	size;
+	int	max_sg_len, req_len, i;
+	uint_t	dma_flags;
+	struct buf	*bp;
+	uint64_t	cur_txfer_len;
 
 
 	ASSERT(spx->txlt_sata_pkt != NULL);
@@ -8884,7 +9065,7 @@
 			rval = ddi_dma_buf_bind_handle(
 					spx->txlt_buf_dma_handle,
 					bp, dma_flags, callback, arg,
-					&cookie,
+					&spx->txlt_dma_cookie,
 					&spx->txlt_curwin_num_dma_cookies);
 		} else { /* Buffer is not aligned */
 
@@ -8967,7 +9148,8 @@
 				NULL,
 				spx->txlt_tmp_buf,
 				bufsz, dma_flags, ddicallback, 0,
-				&cookie, &spx->txlt_curwin_num_dma_cookies);
+				&spx->txlt_dma_cookie,
+				&spx->txlt_curwin_num_dma_cookies);
 		}
 
 		switch (rval) {
@@ -8994,6 +9176,11 @@
 				    "sata_dma_buf_setup: numwin failed\n"));
 				return (DDI_FAILURE);
 			}
+			SATADBG2(SATA_DBG_DMA_SETUP,
+			    spx->txlt_sata_hba_inst,
+			    "sata_dma_buf_setup: windows: %d, cookies: %d\n",
+			    spx->txlt_num_dma_win,
+			    spx->txlt_curwin_num_dma_cookies);
 			spx->txlt_cur_dma_win = 0;
 			break;
 
@@ -9001,6 +9188,10 @@
 			/* DMA fully mapped */
 			spx->txlt_num_dma_win = 1;
 			spx->txlt_cur_dma_win = 0;
+			SATADBG1(SATA_DBG_DMA_SETUP,
+			    spx->txlt_sata_hba_inst,
+			    "sata_dma_buf_setup: windows: 1 "
+			    "cookies: %d\n", spx->txlt_curwin_num_dma_cookies);
 			break;
 
 		default:
@@ -9038,10 +9229,9 @@
 			if (spx->txlt_cur_dma_win < spx->txlt_num_dma_win) {
 				(void) ddi_dma_getwin(spx->txlt_buf_dma_handle,
 				    spx->txlt_cur_dma_win, &offset, &size,
-				    &cookie,
+				    &spx->txlt_dma_cookie,
 				    &spx->txlt_curwin_num_dma_cookies);
 				spx->txlt_curwin_processed_dma_cookies = 0;
-
 			} else {
 				/* No more windows! End of request! */
 				/* What to do? - panic for now */
@@ -9056,10 +9246,16 @@
 			}
 		}
 	}
-	/* There better be at least one DMA cookie */
+	/* There better be at least one DMA cookie outstanding */
 	ASSERT((spx->txlt_curwin_num_dma_cookies -
 	    spx->txlt_curwin_processed_dma_cookies) > 0);
 
+	if (spx->txlt_dma_cookie_list == &spx->txlt_dma_cookie) {
+		/* The default cookie slot was used in previous run */
+		ASSERT(spx->txlt_curwin_processed_dma_cookies == 0);
+		spx->txlt_dma_cookie_list = NULL;
+		spx->txlt_dma_cookie_list_len = 0;
+	}
 	if (spx->txlt_curwin_processed_dma_cookies == 0) {
 		/*
 		 * Processing a new DMA window - set-up dma cookies list.
@@ -9081,23 +9277,65 @@
 			spx->txlt_dma_cookie_list_len = 0;
 		}
 		if (spx->txlt_dma_cookie_list == NULL) {
-			/* Allocate new dma cookie array */
-			spx->txlt_dma_cookie_list = kmem_zalloc(
-			    sizeof (ddi_dma_cookie_t) *
-			    spx->txlt_curwin_num_dma_cookies,
-			    callback == NULL_FUNC ? KM_NOSLEEP : KM_SLEEP);
-			spx->txlt_dma_cookie_list_len =
-			    spx->txlt_curwin_num_dma_cookies;
-		}
-		/*
-		 * Copy all DMA cookies into local list, so we will know their
-		 * dma_size in advance of setting the sata_pkt.
-		 * One cookie was already fetched, so copy it.
-		 */
-		*(&spx->txlt_dma_cookie_list[0]) = cookie;
-		for (i = 1; i < spx->txlt_curwin_num_dma_cookies; i++) {
-			ddi_dma_nextcookie(spx->txlt_buf_dma_handle, &cookie);
-			*(&spx->txlt_dma_cookie_list[i]) = cookie;
+			/*
+			 * Calculate lesser of number of cookies in this
+			 * DMA window and number of s/g entries.
+			 */
+			max_sg_len = cur_dma_attr->dma_attr_sgllen;
+			req_len = MIN(max_sg_len,
+			    spx->txlt_curwin_num_dma_cookies);
+
+			/* Allocate new dma cookie array if necessary */
+			if (req_len == 1) {
+				/* Only one cookie - no need for a list */
+				spx->txlt_dma_cookie_list =
+				    &spx->txlt_dma_cookie;
+				spx->txlt_dma_cookie_list_len = 1;
+			} else {
+				/*
+				 * More than one cookie - try to allocate space.
+				 */
+				spx->txlt_dma_cookie_list = kmem_zalloc(
+				    sizeof (ddi_dma_cookie_t) * req_len,
+				    callback == NULL_FUNC ? KM_NOSLEEP :
+				    KM_SLEEP);
+				if (spx->txlt_dma_cookie_list == NULL) {
+					SATADBG1(SATA_DBG_DMA_SETUP,
+					    spx->txlt_sata_hba_inst,
+					    "sata_dma_buf_setup: cookie list "
+					    "allocation failed\n", NULL);
+					/*
+					 * We could not allocate space for
+					 * neccessary number of dma cookies in
+					 * this window, so we fail this request.
+					 * Next invocation would try again to
+					 * allocate space for cookie list.
+					 * Note:Packet residue was not modified.
+					 */
+					return (DDI_DMA_NORESOURCES);
+				} else {
+					spx->txlt_dma_cookie_list_len = req_len;
+				}
+			}
+		}
+		/*
+		 * Fetch DMA cookies into cookie list in sata_pkt_txlate.
+		 * First cookie was already fetched.
+		 */
+		*(&spx->txlt_dma_cookie_list[0]) = spx->txlt_dma_cookie;
+		cur_txfer_len =
+		    (uint64_t)spx->txlt_dma_cookie_list[0].dmac_size;
+		spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies = 1;
+		spx->txlt_curwin_processed_dma_cookies++;
+		for (i = 1; (i < spx->txlt_dma_cookie_list_len) &&
+		    (i < spx->txlt_curwin_num_dma_cookies); i++) {
+			ddi_dma_nextcookie(spx->txlt_buf_dma_handle,
+			&spx->txlt_dma_cookie_list[i]);
+			cur_txfer_len +=
+			    (uint64_t)spx->txlt_dma_cookie_list[i].dmac_size;
+			spx->txlt_curwin_processed_dma_cookies++;
+			spx->txlt_sata_pkt->
+			    satapkt_cmd.satacmd_num_dma_cookies += 1;
 		}
 	} else {
 		SATADBG2(SATA_DBG_DMA_SETUP, spx->txlt_sata_hba_inst,
@@ -9105,88 +9343,52 @@
 		    "cur cookie %d, total cookies %d\n",
 		    spx->txlt_curwin_processed_dma_cookies,
 		    spx->txlt_curwin_num_dma_cookies);
-	}
-
-	/*
-	 * Set-up sata_pkt cookie list.
-	 * No single cookie transfer size would exceed max transfer size of
-	 * an ATA command used for addressed device (tha adjustment of the dma
-	 * attributes took care of this). But there may be more
-	 * then one cookie, so the cmd cookie list has to be
-	 * constrained by both a maximum scatter gather list length and
-	 * a maximum transfer size restriction of an ATA command.
-	 */
-
-	max_sg_len = cur_dma_attr->dma_attr_sgllen;
-	req_sg_len = MIN(max_sg_len,
-	    (spx->txlt_curwin_num_dma_cookies -
-	    spx->txlt_curwin_processed_dma_cookies));
-
-	ASSERT(req_sg_len > 0);
-
-	max_txfer_len = MAX((cur_dma_attr->dma_attr_granular * 0x100),
-	    cur_dma_attr->dma_attr_maxxfer);
-
-	/* One cookie should be always available */
+
+		/*
+		 * Not all cookies from the current dma window were used because
+		 * of s/g limitation.
+		 * There is no need to re-size the list - it was set at
+		 * optimal size, or only default entry is used (s/g = 1).
+		 */
+		if (spx->txlt_dma_cookie_list == NULL) {
+			spx->txlt_dma_cookie_list = &spx->txlt_dma_cookie;
+			spx->txlt_dma_cookie_list_len = 1;
+		}
+		/*
+		 * Since we are processing remaining cookies in a DMA window,
+		 * there may be less of them than the number of entries in the
+		 * current dma cookie list.
+		 */
+		req_len = MIN(spx->txlt_dma_cookie_list_len,
+		    (spx->txlt_curwin_num_dma_cookies -
+		    spx->txlt_curwin_processed_dma_cookies));
+
+		/* Fetch the next batch of cookies */
+		for (i = 0, cur_txfer_len = 0; i < req_len; i++) {
+			ddi_dma_nextcookie(spx->txlt_buf_dma_handle,
+			&spx->txlt_dma_cookie_list[i]);
+			cur_txfer_len +=
+			    (uint64_t)spx->txlt_dma_cookie_list[i].dmac_size;
+			spx->txlt_sata_pkt->
+			    satapkt_cmd.satacmd_num_dma_cookies++;
+			spx->txlt_curwin_processed_dma_cookies++;
+		}
+	}
+
+	ASSERT(spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies > 0);
+
+	/* Point sata_cmd to the cookie list */
 	spx->txlt_sata_pkt->satapkt_cmd.satacmd_dma_cookie_list =
-	    &spx->txlt_dma_cookie_list[spx->txlt_curwin_processed_dma_cookies];
-
-	spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies = 1;
-
-	cur_txfer_len =
-	    (uint64_t)spx->txlt_dma_cookie_list[
-	    spx->txlt_curwin_processed_dma_cookies].dmac_size;
-
-	spx->txlt_curwin_processed_dma_cookies++;
-
-	ASSERT(cur_txfer_len <= max_txfer_len);
-
-	/* Add more cookies to the scatter-gather list */
-	for (i = 1; i < req_sg_len; i++) {
-		if (cur_txfer_len < max_txfer_len) {
-			/*
-			 * Check if the next cookie could be used by
-			 * this sata_pkt.
-			 */
-			if ((cur_txfer_len +
-			    spx->txlt_dma_cookie_list[
-			    spx->txlt_curwin_processed_dma_cookies].
-			    dmac_size) <= max_txfer_len) {
-				/* Yes, transfer lenght is within bounds */
-				spx->txlt_sata_pkt->
-				    satapkt_cmd.satacmd_num_dma_cookies++;
-				cur_txfer_len +=
-				    spx->txlt_dma_cookie_list[
-				    spx->txlt_curwin_processed_dma_cookies].
-				    dmac_size;
-				spx->txlt_curwin_processed_dma_cookies++;
-			} else {
-				/* No, transfer would exceed max lenght. */
-				SATADBG3(SATA_DBG_DMA_SETUP,
-				    spx->txlt_sata_hba_inst,
-				    "ncookies %d, size 0x%lx, "
-				    "max_size 0x%lx\n",
-				    spx->txlt_sata_pkt->
-				    satapkt_cmd.satacmd_num_dma_cookies,
-				    cur_txfer_len, max_txfer_len);
-				break;
-			}
-		} else {
-			/* Cmd max transfer length reached */
-			SATADBG3(SATA_DBG_DMA_SETUP, spx->txlt_sata_hba_inst,
-			    "Max transfer length? "
-			    "ncookies %d, size 0x%lx, max_size 0x%lx\n",
-			    spx->txlt_sata_pkt->
-			    satapkt_cmd.satacmd_num_dma_cookies,
-			    cur_txfer_len, max_txfer_len);
-			break;
-		}
-	}
+	    &spx->txlt_dma_cookie_list[0];
+
+	/* Remember number of DMA cookies passed in sata packet */
+	spx->txlt_num_dma_cookies =
+	    spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies;
 
 	ASSERT(cur_txfer_len != 0);
 	if (cur_txfer_len <= bp->b_bcount)
 		spx->txlt_total_residue -= cur_txfer_len;
-	else
+	else {
 		/*
 		 * Temporary DMA buffer has been padded by
 		 * ddi_dma_mem_alloc()!
@@ -9197,6 +9399,7 @@
 		 * the requested number of bytes.
 		 */
 		spx->txlt_total_residue -= bp->b_bcount;
+	}
 
 	return (DDI_SUCCESS);
 }
@@ -9284,11 +9487,12 @@
 	rval = (*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst), spkt);
 	if (rval == SATA_TRAN_ACCEPTED &&
 	    spkt->satapkt_reason == SATA_PKT_COMPLETED) {
-		if ((sdinfo->satadrv_id.ai_config & 4) == 1) {
-			sata_log(sata_hba_inst, CE_WARN,
+		if ((sdinfo->satadrv_id.ai_config & SATA_INCOMPLETE_DATA) ==
+		    SATA_INCOMPLETE_DATA) {
+			SATA_LOG_D((sata_hba_inst, CE_WARN,
 			    "SATA disk device at port %d - "
 			    "partial Identify Data",
-			    sdinfo->satadrv_addr.cport);
+			    sdinfo->satadrv_addr.cport));
 			rval = 1; /* may retry later */
 			goto fail;
 		}
@@ -9712,7 +9916,7 @@
  * SCSI_TO_SATA_PMPORT extracts pmport number and
  * SCSI_TO_SATA_ADDR_QUAL extracts port mulitplier qualifier flag.
  *
- * For now, support is for cports only - no pmultiplier ports.
+ * For now, support is for cports only - no port multiplier device ports.
  */
 
 static void
@@ -9745,22 +9949,15 @@
 	switch (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport)) {
 	case SATA_DTYPE_NONE:
 	{
+		ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
+		ap_state->ap_condition = AP_COND_OK;
 		/* No device attached */
 		ap_state->ap_rstate = AP_RSTATE_EMPTY;
-		ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
-		ap_state->ap_condition = AP_COND_OK;
 		break;
 	}
 	case SATA_DTYPE_UNKNOWN:
 	case SATA_DTYPE_ATAPINONCD:
 	case SATA_DTYPE_PMULT:	/* Until PMult is supported */
-	{
-		/* Unknown device attached */
-		ap_state->ap_rstate = AP_RSTATE_CONNECTED;
-		ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
-		ap_state->ap_condition = AP_COND_UNKNOWN;
-		break;
-	}
 	case SATA_DTYPE_ATADISK:
 	case SATA_DTYPE_ATAPICD:
 	{
@@ -9774,13 +9971,28 @@
 		if (tdip != NULL) {
 			ndi_devi_enter(dip, &circ);
 			mutex_enter(&(DEVI(tdip)->devi_lock));
+			if (DEVI_IS_DEVICE_REMOVED(tdip)) {
+				/*
+				 * There could be the case where previously
+				 * configured and opened device was removed
+				 * and unknown device was plugged.
+				 * In such case we want to show a device, and
+				 * its configured or unconfigured state but
+				 * indicate unusable condition untill the
+				 * old target node is released and removed.
+				 */
+				ap_state->ap_condition = AP_COND_UNUSABLE;
+			} else {
+				ap_state->ap_condition = AP_COND_OK;
+			}
 			if ((DEVI_IS_DEVICE_OFFLINE(tdip)) ||
 			    (DEVI_IS_DEVICE_DOWN(tdip))) {
-				ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
+				ap_state->ap_ostate =
+				    AP_OSTATE_UNCONFIGURED;
 			} else {
-				ap_state->ap_ostate = AP_OSTATE_CONFIGURED;
-			}
-			ap_state->ap_condition = AP_COND_OK;
+				ap_state->ap_ostate =
+				    AP_OSTATE_CONFIGURED;
+			}
 			mutex_exit(&(DEVI(tdip)->devi_lock));
 			ndi_devi_exit(dip, circ);
 		} else {
@@ -9795,7 +10007,7 @@
 		ap_state->ap_condition = AP_COND_UNKNOWN;
 		/*
 		 * This is actually internal error condition (non fatal),
-		 * beacuse we already checked all defined device types.
+		 * because we have already checked all defined device types.
 		 */
 		SATA_LOG_D((sata_hba_inst, CE_WARN,
 		    "sata_cfgadm_state: Internal error: "
@@ -10424,6 +10636,10 @@
 				sata_process_port_pwr_change(sata_hba_inst,
 				    saddr);
 			}
+			if (event_flags & SATA_EVNT_TARGET_NODE_CLEANUP) {
+				sata_process_target_node_cleanup(
+				    sata_hba_inst, saddr);
+			}
 		}
 		if (SATA_CPORT_DEV_TYPE(sata_hba_inst, ncport) !=
 		    SATA_DTYPE_NONE) {
@@ -10499,7 +10715,7 @@
 	/* Fail the port */
 	cportinfo->cport_state = SATA_PSTATE_FAILED;
 	mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
-	sata_log(sata_hba_inst, CE_WARN, "port %d failed", saddr->cport);
+	sata_log(sata_hba_inst, CE_WARN, "SATA port %d failed", saddr->cport);
 }
 
 /*
@@ -10545,25 +10761,24 @@
 		return;
 	}
 
-	if ((sdinfo->satadrv_event_flags & SATA_EVNT_DEVICE_RESET) == 0) {
+	if ((sdinfo->satadrv_event_flags &
+	    (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) == 0) {
 		/* Nothing to do */
 		mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
 		    cport_mutex);
 		return;
 	}
-
-	SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
-	    "Processing port %d device reset", saddr->cport);
-
-	if (sdinfo->satadrv_event_flags & SATA_EVNT_INPROC_DEVICE_RESET) {
+#ifdef SATA_DEBUG
+	if ((sdinfo->satadrv_event_flags &
+	    (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) ==
+	    (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) {
 		/* Something is weird - new device reset event */
 		SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
 		    "Overlapping device reset events!", NULL);
-		/* Just leave */
-		mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
-		    cport_mutex);
-		return;
-	}
+	}
+#endif
+	SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
+	    "Processing port %d device reset", saddr->cport);
 
 	/* Clear event flag */
 	sdinfo->satadrv_event_flags &= ~SATA_EVNT_DEVICE_RESET;
@@ -10585,7 +10800,8 @@
 		cportinfo->cport_state = SATA_PSTATE_FAILED;
 		mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
 		    cport_mutex);
-		SATA_LOG_D((sata_hba_inst, CE_WARN, "Port %d probing failed",
+		SATA_LOG_D((sata_hba_inst, CE_WARN,
+		    "SATA port %d probing failed",
 		    saddr->cport));
 		return;
 	}
@@ -10633,17 +10849,13 @@
 		    SATA_PORT_DEVLINK_UP_MASK) == SATA_PORT_DEVLINK_UP &&
 		    (sata_device.satadev_type & SATA_DTYPE_ATADISK) != 0) {
 			/*
-			 * We may retry this a bit later - reinstate reset
-			 * condition
+			 * We may retry this a bit later - in-process reset
+			 * condition is already set.
 			 */
 			if ((cportinfo->cport_dev_type &
 			    SATA_VALID_DEV_TYPE) != 0 &&
 			    SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) {
 				sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
-				sdinfo->satadrv_event_flags |=
-				    SATA_EVNT_DEVICE_RESET;
-				sdinfo->satadrv_event_flags &=
-				    ~SATA_EVNT_INPROC_DEVICE_RESET;
 				mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
 				    saddr->cport)->cport_mutex);
 				mutex_enter(&sata_hba_inst->satahba_mutex);
@@ -10760,7 +10972,8 @@
 		cportinfo->cport_state = SATA_PSTATE_FAILED;
 		mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
 		    cport_mutex);
-		SATA_LOG_D((sata_hba_inst, CE_WARN, "Port %d probing failed",
+		SATA_LOG_D((sata_hba_inst, CE_WARN,
+		    "SATA port %d probing failed",
 		    saddr->cport));
 		/*
 		 * We may want to release device info structure, but
@@ -10963,7 +11176,8 @@
 		cportinfo->cport_state = SATA_PSTATE_FAILED;
 		mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
 		    cport_mutex);
-		SATA_LOG_D((sata_hba_inst, CE_WARN, "Port %d probing failed",
+		SATA_LOG_D((sata_hba_inst, CE_WARN,
+		    "SATA port %d probing failed",
 		    saddr->cport));
 		/*
 		 * We may want to release device info structure, but
@@ -11016,19 +11230,9 @@
 	tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport);
 	if (tdip != NULL) {
 		/*
-		 * target node exist - unconfigure device first, then remove
-		 * the node
-		 */
-		if (ndi_devi_offline(tdip, NDI_UNCONFIG) != NDI_SUCCESS) {
-			/*
-			 * PROBLEM - no device, but target node remained
-			 * This happens when the file was open or node was
-			 * waiting for resources.
-			 */
-			SATA_LOG_D((sata_hba_inst, CE_WARN,
-			    "sata_process_device_detached: "
-			    "Failed to unconfigure removed device."));
-		}
+		 * Target node exists.  Unconfigure device then remove
+		 * the target node (one ndi operation).
+		 */
 		if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) != NDI_SUCCESS) {
 			/*
 			 * PROBLEM - no device, but target node remained
@@ -11038,7 +11242,22 @@
 			SATA_LOG_D((sata_hba_inst, CE_WARN,
 			    "sata_process_device_detached: "
 			    "Failed to remove target node for "
-			    "removed device."));
+			    "detached SATA device."));
+			/*
+			 * Set target node state to DEVI_DEVICE_REMOVED.
+			 * But re-check first that the node still exists.
+			 */
+			tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst),
+			    saddr->cport);
+			if (tdip != NULL) {
+				sata_set_device_removed(tdip);
+				/*
+				 * Instruct event daemon to retry the
+				 * cleanup later.
+				 */
+				sata_set_target_node_cleanup(sata_hba_inst,
+				    saddr->cport);
+			}
 		}
 	}
 	/*
@@ -11081,8 +11300,9 @@
 	cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
 	mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
 
-	/* Clear event flag first */
+	/* Clear attach event flag first */
 	cportinfo->cport_event_flags &= ~SATA_EVNT_DEVICE_ATTACHED;
+
 	/* If the port is in SHUTDOWN or FAILED state, ignore event. */
 	if ((cportinfo->cport_state &
 	    (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
@@ -11094,9 +11314,9 @@
 
 	/*
 	 * If the sata_drive_info structure is found attached to the port info,
-	 * something went wrong in the event reporting and processing sequence.
-	 * To recover, arbitrarily release device info structure and issue
-	 * a warning.
+	 * despite the fact the device was removed and now it is re-attached,
+	 * the old drive info structure was not removed.
+	 * Arbitrarily release device info structure.
 	 */
 	if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) {
 		sdevinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
@@ -11127,7 +11347,8 @@
 		cportinfo->cport_dev_attach_time = 0;
 		mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
 		    cport_mutex);
-		SATA_LOG_D((sata_hba_inst, CE_WARN, "Port %d probing failed",
+		SATA_LOG_D((sata_hba_inst, CE_WARN,
+		    "SATA port %d probing failed",
 		    saddr->cport));
 		return;
 	} else {
@@ -11161,58 +11382,15 @@
 	sata_gen_sysevent(sata_hba_inst, saddr, SE_HINT_INSERT);
 
 	/*
-	 * Make sure that there is no target node for that device.
-	 * If so, release it. It should not happen, unless we had problem
-	 * removing the node when device was detached.
-	 */
-	tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport);
-	if (tdip != NULL) {
-
-		SATA_LOG_D((sata_hba_inst, CE_WARN,
-		    "sata_process_device_attached: "
-		    "old device target node exists!!!"));
-		/*
-		 * target node exist - unconfigure device first, then remove
-		 * the node
-		 */
-		if (ndi_devi_offline(tdip, NDI_UNCONFIG) != NDI_SUCCESS) {
-			/*
-			 * PROBLEM - no device, but target node remained
-			 * This happens when the file was open or node was
-			 * waiting for resources.
-			 */
-			SATA_LOG_D((sata_hba_inst, CE_WARN,
-			    "sata_process_device_attached: "
-			    "Failed to unconfigure old target node!"));
-		}
-		/* Following call will retry node offlining and removing it */
-		if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) != NDI_SUCCESS) {
-			/* PROBLEM - no device, but target node remained */
-			SATA_LOG_D((sata_hba_inst, CE_WARN,
-			    "sata_process_device_attached: "
-			    "Failed to remove old target node!"));
-			/*
-			 * It is not clear, what should be done here.
-			 * For now, we will not attach a new device
-			 */
-			mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
-			    saddr->cport)->cport_mutex);
-			cportinfo->cport_dev_attach_time = 0;
-			mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
-			    saddr->cport)->cport_mutex);
-			return;
-		}
-	}
-
-	/*
-	 * Port reprobing will take care of the creation of the device info
-	 * structure and determination of the device type.
+	 * Port reprobing will take care of the creation of the device
+	 * info structure and determination of the device type.
 	 */
 	sata_device.satadev_addr = *saddr;
-	rval = sata_reprobe_port(sata_hba_inst, &sata_device,
+	(void) sata_reprobe_port(sata_hba_inst, &sata_device,
 	    SATA_DEV_IDENTIFY_NORETRY);
 
-	mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
+	mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
+	    cport_mutex);
 	if ((cportinfo->cport_state & SATA_STATE_READY) &&
 	    (cportinfo->cport_dev_type != SATA_DTYPE_NONE)) {
 		/* Some device is attached to the port */
@@ -11237,11 +11415,18 @@
 				} else {
 					/* Timeout - cannot identify device */
 					cportinfo->cport_dev_attach_time = 0;
+					sata_log(sata_hba_inst,
+					    CE_WARN,
+					    "Cannot identify SATA device "
+					    "at port %d - device will not be "
+					    "attached.",
+					    saddr->cport);
 				}
 			} else {
 				/*
-				 * Start tracking time for dev identification.
-				 * save current time (lbolt value).
+				 * Start tracking time for device
+				 * identification.
+				 * Save current time (lbolt value).
 				 */
 				cportinfo->cport_dev_attach_time =
 				    ddi_get_lbolt();
@@ -11253,24 +11438,92 @@
 			/*
 			 * If device was successfully attached, an explicit
 			 * 'configure' command will be needed to configure it.
+			 * Log the message indicating that a device
+			 * was attached.
 			 */
 			cportinfo->cport_dev_attach_time = 0;
 			sata_log(sata_hba_inst, CE_WARN,
-			    "SATA device attached at port %d", saddr->cport);
+			    "SATA device detected at port %d", saddr->cport);
 
 			if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) {
 				sata_drive_info_t new_sdinfo;
 
 				/* Log device info data */
-				new_sdinfo =
-				    *(SATA_CPORTINFO_DRV_INFO(cportinfo));
+				new_sdinfo = *(SATA_CPORTINFO_DRV_INFO(
+				    cportinfo));
 				sata_show_drive_info(sata_hba_inst,
 				    &new_sdinfo);
 			}
+
+			mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
+			    saddr->cport)->cport_mutex);
+
+			/*
+			 * Make sure that there is no target node for that
+			 * device. If so, release it. It should not happen,
+			 * unless we had problem removing the node when
+			 * device was detached.
+			 */
+			tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst),
+			    saddr->cport);
+			mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
+			    saddr->cport)->cport_mutex);
+			if (tdip != NULL) {
+
+#ifdef SATA_DEBUG
+				if ((cportinfo->cport_event_flags &
+				    SATA_EVNT_TARGET_NODE_CLEANUP) == 0)
+					sata_log(sata_hba_inst, CE_WARN,
+					    "sata_process_device_attached: "
+					    "old device target node exists!");
+#endif
+				/*
+				 * target node exists - try to unconfigure
+				 * device and remove the node.
+				 */
+				mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
+				    saddr->cport)->cport_mutex);
+				rval = ndi_devi_offline(tdip,
+				    NDI_DEVI_REMOVE);
+				mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
+				    saddr->cport)->cport_mutex);
+
+				if (rval == NDI_SUCCESS) {
+					cportinfo->cport_event_flags &=
+					    ~SATA_EVNT_TARGET_NODE_CLEANUP;
+					cportinfo->cport_tgtnode_clean = B_TRUE;
+				} else {
+					/*
+					 * PROBLEM - the target node remained
+					 * and it belongs to a previously
+					 * attached device.
+					 * This happens when the file was open
+					 * or the node was waiting for
+					 * resources at the time the
+					 * associated device was removed.
+					 * Instruct event daemon to retry the
+					 * cleanup later.
+					 */
+					sata_log(sata_hba_inst,
+					    CE_WARN,
+					    "Application(s) accessing "
+					    "previously attached SATA "
+					    "device have to release "
+					    "it before newly inserted "
+					    "device can be made accessible.",
+					    saddr->cport);
+					cportinfo->cport_event_flags |=
+					    SATA_EVNT_TARGET_NODE_CLEANUP;
+					cportinfo->cport_tgtnode_clean =
+					    B_FALSE;
+				}
+			}
+
 		}
 	} else {
 		cportinfo->cport_dev_attach_time = 0;
 	}
+
 	event_flags = cportinfo->cport_event_flags;
 	mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
 	if (event_flags != 0) {
@@ -11285,6 +11538,78 @@
 
 
 /*
+ * Device Target Node Cleanup Event processing.
+ * If the target node associated with a sata port device is in
+ * DEVI_DEVICE_REMOVED state, an attempt is made to remove it.
+ * If the target node cannot be removed, the event flag is left intact,
+ * so that event daemon may re-run this function later.
+ *
+ * This function cannot be called in interrupt context (it may sleep).
+ *
+ * NOTE: Processes cport events only, not port multiplier ports.
+ */
+static void
+sata_process_target_node_cleanup(sata_hba_inst_t *sata_hba_inst,
+    sata_address_t *saddr)
+{
+	sata_cport_info_t *cportinfo;
+	dev_info_t *tdip;
+
+	SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
+	    "Processing port %d device target node cleanup", saddr->cport);
+
+	cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
+
+	/*
+	 * Check if there is target node for that device and it is in the
+	 * DEVI_DEVICE_REMOVED state. If so, release it.
+	 */
+	tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport);
+	if (tdip != NULL) {
+		/*
+		 * target node exists - check if it is target node of
+		 * a removed device.
+		 */
+		if (sata_check_device_removed(tdip) == B_TRUE) {
+			SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
+			    "sata_process_target_node_cleanup: "
+			    "old device target node exists!", NULL);
+			/*
+			 * Unconfigure and remove the target node
+			 */
+			if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) ==
+			    NDI_SUCCESS) {
+				mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
+				    saddr->cport)->cport_mutex);
+				cportinfo->cport_event_flags &=
+				    ~SATA_EVNT_TARGET_NODE_CLEANUP;
+				mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
+				    saddr->cport)->cport_mutex);
+				return;
+			}
+			/*
+			 * Event daemon will retry the cleanup later.
+			 */
+			mutex_enter(&sata_hba_inst->satahba_mutex);
+			sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
+			mutex_exit(&sata_hba_inst->satahba_mutex);
+			mutex_enter(&sata_mutex);
+			sata_event_pending |= SATA_EVNT_MAIN;
+			mutex_exit(&sata_mutex);
+		}
+	} else {
+		mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
+		    saddr->cport)->cport_mutex);
+		cportinfo->cport_event_flags &=
+		    ~SATA_EVNT_TARGET_NODE_CLEANUP;
+		mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
+		    saddr->cport)->cport_mutex);
+	}
+}
+
+
+
+/*
  * sata_set_drive_features function compares current device features setting
  * with the saved device features settings and, if there is a difference,
  * it restores device features setting to the previously saved state.
@@ -12149,3 +12474,65 @@
 		break;
 	}
 }
+
+
+
+/*
+ * Set DEVI_DEVICE_REMOVED state in the SATA device target node.
+ */
+static void
+sata_set_device_removed(dev_info_t *tdip)
+{
+	int circ;
+
+	ASSERT(tdip != NULL);
+
+	ndi_devi_enter(tdip, &circ);
+	mutex_enter(&DEVI(tdip)->devi_lock);
+	DEVI_SET_DEVICE_REMOVED(tdip);
+	mutex_exit(&DEVI(tdip)->devi_lock);
+	ndi_devi_exit(tdip, circ);
+}
+
+
+/*
+ * Set internal event instructing event daemon to try
+ * to perform the target node cleanup.
+ */
+static void
+sata_set_target_node_cleanup(sata_hba_inst_t *sata_hba_inst, int cport)
+{
+	mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
+	SATA_CPORT_EVENT_FLAGS(sata_hba_inst, cport) |=
+	    SATA_EVNT_TARGET_NODE_CLEANUP;
+	SATA_CPORT_INFO(sata_hba_inst, cport)->cport_tgtnode_clean = B_FALSE;
+	mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
+	mutex_enter(&sata_hba_inst->satahba_mutex);
+	sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
+	mutex_exit(&sata_hba_inst->satahba_mutex);
+	mutex_enter(&sata_mutex);
+	sata_event_pending |= SATA_EVNT_MAIN;
+	mutex_exit(&sata_mutex);
+}
+
+
+/*
+ * Check if the SATA device target node is in DEVI_DEVICE_REMOVED state,
+ * i.e. check if the target node state indicates that it belongs to a removed
+ * device.
+ *
+ * Returns B_TRUE if the target node is in DEVI_DEVICE_REMOVED state,
+ * B_FALSE otherwise.
+ *
+ * NOTE: No port multiplier support.
+ */
+static boolean_t
+sata_check_device_removed(dev_info_t *tdip)
+{
+	ASSERT(tdip != NULL);
+
+	if (DEVI_IS_DEVICE_REMOVED(tdip))
+		return (B_TRUE);
+	else
+		return (B_FALSE);
+}
--- a/usr/src/uts/common/sys/sata/impl/sata.h	Fri Mar 30 08:46:35 2007 -0700
+++ b/usr/src/uts/common/sys/sata/impl/sata.h	Fri Mar 30 11:43:17 2007 -0700
@@ -166,6 +166,8 @@
 	clock_t			cport_dev_attach_time;
 
 	struct sata_port_stats	cport_stats;	/* Port statistics */
+
+	boolean_t		cport_tgtnode_clean; /* Target node usable */
 };
 
 typedef struct sata_cport_info sata_cport_info_t;
@@ -292,6 +294,8 @@
 	clock_t		pmport_dev_attach_time;
 
 	struct sata_port_stats	pmport_stats;	/* Port statistics */
+
+	boolean_t	pmport_tgtnode_clean;	/* Target node usable */
 };
 
 typedef	struct sata_pmport_info sata_pmport_info_t;
@@ -351,6 +355,7 @@
 #define	SATA_EVNT_SKIP			0x40000000
 #define	SATA_EVNT_INPROC_DEVICE_RESET	0x08000000
 #define	SATA_EVNT_CLEAR_DEVICE_RESET	0x04000000
+#define	SATA_EVNT_TARGET_NODE_CLEANUP	0x00000100
 
 /*
  * Lock flags - used to serialize configuration operations
@@ -368,9 +373,11 @@
 					SATA_EVNT_DEVICE_DETACHED | \
 					SATA_EVNT_LINK_LOST | \
 					SATA_EVNT_LINK_ESTABLISHED | \
-					SATA_EVNT_PORT_FAILED)
+					SATA_EVNT_PORT_FAILED | \
+					SATA_EVNT_TARGET_NODE_CLEANUP)
 /* Mask for drive events */
-#define	SATA_EVNT_DRIVE_EVENTS		SATA_EVNT_DEVICE_RESET
+#define	SATA_EVNT_DRIVE_EVENTS		(SATA_EVNT_DEVICE_RESET | \
+					SATA_EVNT_INPROC_DEVICE_RESET)
 #define	SATA_EVNT_CONTROLLER_EVENTS	SATA_EVNT_PWR_LEVEL_CHANGED
 
 /* Delays and timeounts definitions */
@@ -413,9 +420,10 @@
 				/* cookies in the current DMA window */
 	uint_t			txlt_curwin_num_dma_cookies;
 
-				/* procesed dma cookies in current DMA win */
+				/* processed dma cookies in current DMA win */
 	uint_t			txlt_curwin_processed_dma_cookies;
 	size_t			txlt_total_residue;
+	ddi_dma_cookie_t	txlt_dma_cookie; /* default dma cookie */
 	int			txlt_dma_cookie_list_len; /* alloc list len */
 	ddi_dma_cookie_t 	*txlt_dma_cookie_list; /* dma cookie list */
 	int			txlt_num_dma_cookies; /* dma cookies in list */
@@ -455,7 +463,6 @@
 
 /*
  * Macros for accessing various structure fields
- *
  */
 
 #define	SATA_TRAN(sata_hba_inst) \
@@ -512,6 +519,9 @@
 #define	SATA_CPORT_STATE(sata_hba_inst, cport) \
 	sata_hba_inst->satahba_dev_port[cport]->cport_state
 
+#define	SATA_CPORT_EVENT_FLAGS(sata_hba_inst, cport) \
+	sata_hba_inst->satahba_dev_port[cport]->cport_event_flags
+
 #define	SATA_CPORT_SCR(sata_hba_inst, cport) \
 	sata_hba_inst->satahba_dev_port[cport]->cport_scr
 
@@ -696,8 +706,6 @@
 #define	SATA_DBG_DMA_SETUP	0x400
 #define	SATA_DBG_DEV_SETTINGS	0x800
 
-extern int sata_debug_flag;
-
 /* Debug macros */
 #define	SATADBG1(flag, sata, format, arg1) \
 	if (sata_debug_flags & (flag)) { \