changeset 12017:2ff17b2a8ff9

6922272 SATA framework does not handle >2TiB disks 6937622 DKIOCGMEDIAINFOEXT returns the wrong physical block size
author Alan Perry <Alan.Perry@Sun.COM>
date Fri, 26 Mar 2010 15:39:18 -0700
parents 0248e987199b
children 78c92c5b916f
files usr/src/uts/common/io/sata/impl/sata.c usr/src/uts/common/io/scsi/targets/sd.c usr/src/uts/common/sys/sata/impl/sata.h usr/src/uts/common/sys/sata/sata_defs.h
diffstat 4 files changed, 193 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/io/sata/impl/sata.c	Fri Mar 26 17:53:11 2010 -0400
+++ b/usr/src/uts/common/io/sata/impl/sata.c	Fri Mar 26 15:39:18 2010 -0700
@@ -201,6 +201,7 @@
 static	int sata_txlt_test_unit_ready(sata_pkt_txlate_t *);
 static	int sata_txlt_start_stop_unit(sata_pkt_txlate_t *);
 static	int sata_txlt_read_capacity(sata_pkt_txlate_t *);
+static	int sata_txlt_read_capacity16(sata_pkt_txlate_t *);
 static	int sata_txlt_request_sense(sata_pkt_txlate_t *);
 static	int sata_txlt_read(sata_pkt_txlate_t *);
 static	int sata_txlt_write(sata_pkt_txlate_t *);
@@ -2337,6 +2338,7 @@
  * SCMD_TEST_UNIT_READY
  * SCMD_START_STOP
  * SCMD_READ_CAPACITY
+ * SCMD_SVC_ACTION_IN_G4 (READ CAPACITY (16))
  * SCMD_REQUEST_SENSE
  * SCMD_LOG_SENSE_G1
  * SCMD_LOG_SELECT_G1
@@ -2513,6 +2515,12 @@
 		rval = sata_txlt_read_capacity(spx);
 		break;
 
+	case SCMD_SVC_ACTION_IN_G4:		/* READ CAPACITY (16) */
+		if (bp != NULL && (bp->b_flags & (B_PHYS | B_PAGEIO)))
+			bp_mapin(bp);
+		rval = sata_txlt_read_capacity16(spx);
+		break;
+
 	case SCMD_REQUEST_SENSE:
 		/*
 		 * Always No Sense, since we force ARQ
@@ -4389,8 +4397,13 @@
 		sdinfo = sata_get_device_info(
 		    spx->txlt_sata_hba_inst,
 		    &spx->txlt_sata_pkt->satapkt_device);
-		/* Last logical block address */
-		val = sdinfo->satadrv_capacity - 1;
+
+		/*
+		 * As per SBC-3, the "returned LBA" is either the highest
+		 * addressable LBA or 0xffffffff, whichever is smaller.
+		 */
+		val = MIN(sdinfo->satadrv_capacity - 1, UINT32_MAX);
+
 		rbuf = (uchar_t *)bp->b_un.b_addr;
 		/* Need to swap endians to match scsi format */
 		rbuf[0] = (val >> 24) & 0xff;
@@ -4436,6 +4449,170 @@
 }
 
 /*
+ * SATA translate command:  Read Capacity (16).
+ * Emulated command for SATA disks.
+ * Info is retrieved from cached Identify Device data.
+ * Implemented to SBC-3 (draft 21) and SAT-2 (final) specifications.
+ *
+ * Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
+ */
+static int
+sata_txlt_read_capacity16(sata_pkt_txlate_t *spx)
+{
+	struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
+	struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
+	sata_drive_info_t *sdinfo;
+	uint64_t val;
+	uint16_t l2p_exp;
+	uchar_t *rbuf;
+	int rval, reason;
+
+	SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
+	    "sata_txlt_read_capacity: ", NULL);
+
+	mutex_enter(&(SATA_TXLT_CPORT_MUTEX(spx)));
+
+	if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
+	    TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
+		mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx)));
+		return (rval);
+	}
+
+	scsipkt->pkt_reason = CMD_CMPLT;
+	scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+	    STATE_SENT_CMD | STATE_GOT_STATUS;
+	if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
+		/*
+		 * Because it is fully emulated command storing data
+		 * programatically in the specified buffer, release
+		 * preallocated DMA resources before storing data in the buffer,
+		 * so no unwanted DMA sync would take place.
+		 */
+		sata_scsi_dmafree(NULL, scsipkt);
+
+		/* Check SERVICE ACTION field */
+		if ((scsipkt->pkt_cdbp[1] & 0x1f) !=
+		    SSVC_ACTION_READ_CAPACITY_G4) {
+			mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx)));
+			return (sata_txlt_check_condition(spx,
+			    KEY_ILLEGAL_REQUEST,
+			    SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
+		}
+
+		/* Check LBA field */
+		if ((scsipkt->pkt_cdbp[2] != 0) ||
+		    (scsipkt->pkt_cdbp[3] != 0) ||
+		    (scsipkt->pkt_cdbp[4] != 0) ||
+		    (scsipkt->pkt_cdbp[5] != 0) ||
+		    (scsipkt->pkt_cdbp[6] != 0) ||
+		    (scsipkt->pkt_cdbp[7] != 0) ||
+		    (scsipkt->pkt_cdbp[8] != 0) ||
+		    (scsipkt->pkt_cdbp[9] != 0)) {
+			mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx)));
+			return (sata_txlt_check_condition(spx,
+			    KEY_ILLEGAL_REQUEST,
+			    SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
+		}
+
+		/* Check PMI bit */
+		if (scsipkt->pkt_cdbp[14] & 0x1) {
+			mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx)));
+			return (sata_txlt_check_condition(spx,
+			    KEY_ILLEGAL_REQUEST,
+			    SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
+		}
+
+		*scsipkt->pkt_scbp = STATUS_GOOD;
+
+		sdinfo = sata_get_device_info(
+		    spx->txlt_sata_hba_inst,
+		    &spx->txlt_sata_pkt->satapkt_device);
+
+		/* last logical block address */
+		val = MIN(sdinfo->satadrv_capacity - 1,
+		    SCSI_READ_CAPACITY16_MAX_LBA);
+
+		/* logical to physical block size exponent */
+		l2p_exp = 0;
+		if (sdinfo->satadrv_id.ai_phys_sect_sz & SATA_L2PS_CHECK_BIT) {
+			/* physical/logical sector size word is valid */
+
+			if (sdinfo->satadrv_id.ai_phys_sect_sz &
+			    SATA_L2PS_HAS_MULT) {
+				/* multiple logical sectors per phys sectors */
+				l2p_exp =
+				    sdinfo->satadrv_id.ai_phys_sect_sz &
+				    SATA_L2PS_EXP_MASK;
+			}
+		}
+
+		rbuf = (uchar_t *)bp->b_un.b_addr;
+		bzero(rbuf, bp->b_bcount);
+
+		/* returned logical block address */
+		rbuf[0] = (val >> 56) & 0xff;
+		rbuf[1] = (val >> 48) & 0xff;
+		rbuf[2] = (val >> 40) & 0xff;
+		rbuf[3] = (val >> 32) & 0xff;
+		rbuf[4] = (val >> 24) & 0xff;
+		rbuf[5] = (val >> 16) & 0xff;
+		rbuf[6] = (val >> 8) & 0xff;
+		rbuf[7] = val & 0xff;
+
+		/* logical block length in bytes = 512 (for now) */
+		/* rbuf[8] = 0; */
+		/* rbuf[9] = 0; */
+		rbuf[10] = 0x02;
+		/* rbuf[11] = 0; */
+
+		/* p_type, prot_en, unspecified by SAT-2 */
+		/* rbuf[12] = 0; */
+
+		/* p_i_exponent, undefined by SAT-2 */
+		/* logical blocks per physical block exponent */
+		rbuf[13] = l2p_exp;
+
+		/* tpe, tprz, undefined by SAT-2 */
+		/* lowest aligned logical block address = 0 (for now) */
+		/* rbuf[14] = 0; */
+		/* rbuf[15] = 0; */
+
+		scsipkt->pkt_state |= STATE_XFERRED_DATA;
+		scsipkt->pkt_resid = 0;
+
+		SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, "%llu\n",
+		    sdinfo->satadrv_capacity -1);
+	}
+
+	mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx)));
+
+	/*
+	 * If a callback was requested, do it now.
+	 */
+	SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
+	    "Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
+
+	if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
+	    scsipkt->pkt_comp != NULL) {
+		/* scsi callback required */
+		if (servicing_interrupt()) {
+			if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
+			    (task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
+			    (void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) == NULL) {
+				return (TRAN_BUSY);
+			}
+		} else 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);
+}
+
+/*
  * SATA translate command: Mode Sense.
  * Translated into appropriate SATA command or emulated.
  * Saved Values Page Control (03) are not supported.
--- a/usr/src/uts/common/io/scsi/targets/sd.c	Fri Mar 26 17:53:11 2010 -0400
+++ b/usr/src/uts/common/io/scsi/targets/sd.c	Fri Mar 26 15:39:18 2010 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -20190,11 +20190,11 @@
 		}
 
 		/*
-		 * Read capacity and block size from the READ CAPACITY 10 data.
+		 * Read capacity and block size from the READ CAPACITY 16 data.
 		 * This data may be adjusted later due to device specific
 		 * issues.
 		 *
-		 * According to the SCSI spec, the READ CAPACITY 10
+		 * According to the SCSI spec, the READ CAPACITY 16
 		 * command returns the following:
 		 *
 		 *  bytes 0-7: Maximum logical block address available.
@@ -20207,7 +20207,7 @@
 		 */
 		capacity = BE_64(capacity16_buf[0]);
 		lbasize = BE_32(*(uint32_t *)&capacity16_buf[1]);
-		lbpb_exp = (BE_64(capacity16_buf[1]) >> 40) & 0x0f;
+		lbpb_exp = (BE_64(capacity16_buf[1]) >> 16) & 0x0f;
 
 		pbsize = lbasize << lbpb_exp;
 
--- a/usr/src/uts/common/sys/sata/impl/sata.h	Fri Mar 26 17:53:11 2010 -0400
+++ b/usr/src/uts/common/sys/sata/impl/sata.h	Fri Mar 26 15:39:18 2010 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -543,6 +543,8 @@
  * in sys/scsi/targets/sddefs.h as MODEPAGE_ERR_RECOV
  */
 #define	MODEPAGE_RW_ERRRECOV			0x01 /* read/write recovery */
+/* Missing from sys/scsi/impl/commands.h */
+#define	SCSI_READ_CAPACITY16_MAX_LBA		0xfffffffffffffffe
 
 /*
  * medium access command
--- a/usr/src/uts/common/sys/sata/sata_defs.h	Fri Mar 26 17:53:11 2010 -0400
+++ b/usr/src/uts/common/sys/sata/sata_defs.h	Fri Mar 26 15:39:18 2010 -0700
@@ -314,6 +314,12 @@
 /* IDLE IMMEDIATE with UNLOAD FEATURE supported */
 #define	SATA_IDLE_UNLOAD_SUPPORTED	0x2000
 
+/* Identify Device: physical sector size - word 106 */
+#define	SATA_L2PS_CHECK_BIT	0x4000	/* Set when this word valid */
+#define	SATA_L2PS_HAS_MULT	0x2000	/* Multiple logical sectors per phys */
+#define	SATA_L2PS_BIG_SECTORS	0x1000	/* Logical sector size > 512 */
+#define	SATA_L2PS_EXP_MASK	0x000f	/* Logical sectors per phys exponent */
+
 /* Identify (Packet) Device word 63,  ATA/ATAPI-6 & 7 */
 #define	SATA_MDMA_SEL_MASK	0x0700	/* Multiword DMA selected */
 #define	SATA_MDMA_2_SEL		0x0400	/* Multiword DMA mode 2 selected */
@@ -612,7 +618,7 @@
 
 /*
  * SMART specific data
- * These eventually need to go to a generic scsi hearder file
+ * These eventually need to go to a generic scsi header file
  * for now they will reside here
  */
 #define	PC_CUMULATIVE_VALUES			0x01