Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 13 Mar 2012 20:33:16 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r232941 - stable/9/sys/cam/scsi
Message-ID:  <201203132033.q2DKXGtm061634@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Tue Mar 13 20:33:15 2012
New Revision: 232941
URL: http://svn.freebsd.org/changeset/base/232941

Log:
  MFC r228846:
  Use READ CAPACITY(16) to get information about device physical sectors.
  As soon as not all devices support READ CAPACITY(16), automatically fall
  back to READ CAPACITY(10) if CAM_REQ_INVALID or SSD_KEY_ILLEGAL_REQUEST
  status returned.
  
  It also provides first bits of information about Logical Block Provisioning
  (aka UNMAP/TRIM) support by the device.

Modified:
  stable/9/sys/cam/scsi/scsi_all.h
  stable/9/sys/cam/scsi/scsi_da.c
Directory Properties:
  stable/9/sys/   (props changed)

Modified: stable/9/sys/cam/scsi/scsi_all.h
==============================================================================
--- stable/9/sys/cam/scsi/scsi_all.h	Tue Mar 13 20:30:23 2012	(r232940)
+++ stable/9/sys/cam/scsi/scsi_all.h	Tue Mar 13 20:33:15 2012	(r232941)
@@ -1397,6 +1397,17 @@ struct scsi_read_capacity_data_long
 {
 	uint8_t addr[8];
 	uint8_t length[4];
+#define	SRC16_PROT_EN		0x01
+#define	SRC16_P_TYPE		0x0e
+	uint8_t prot;
+#define	SRC16_LBPPBE		0x0f
+#define	SRC16_PI_EXPONENT	0xf0
+#define	SRC16_PI_EXPONENT_SHIFT	4
+	uint8_t prot_lbppbe;
+#define	SRC16_LALBA		0x3fff
+#define	SRC16_LBPRZ		0x4000
+#define	SRC16_LBPME		0x8000
+	uint8_t lalba_lbp[2];
 };
 
 struct scsi_report_luns

Modified: stable/9/sys/cam/scsi/scsi_da.c
==============================================================================
--- stable/9/sys/cam/scsi/scsi_da.c	Tue Mar 13 20:30:23 2012	(r232940)
+++ stable/9/sys/cam/scsi/scsi_da.c	Tue Mar 13 20:33:15 2012	(r232941)
@@ -82,7 +82,9 @@ typedef enum {
 	DA_FLAG_WENT_IDLE	= 0x040,
 	DA_FLAG_RETRY_UA	= 0x080,
 	DA_FLAG_OPEN		= 0x100,
-	DA_FLAG_SCTX_INIT	= 0x200
+	DA_FLAG_SCTX_INIT	= 0x200,
+	DA_FLAG_CAN_RC16	= 0x400,
+	DA_FLAG_CAN_LBPME	= 0x800
 } da_flags;
 
 typedef enum {
@@ -1544,6 +1546,14 @@ daregister(struct cam_periph *periph, vo
 	else if (softc->minimum_cmd_size > 12)
 		softc->minimum_cmd_size = 16;
 
+	/* Predict whether device may support READ CAPACITY(16). */
+	if (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC3 ||
+	    (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC &&
+	     (cgd->inq_data.spc3_flags & SPC3_SID_PROTECT))) {
+		softc->flags |= DA_FLAG_CAN_RC16;
+		softc->state = DA_STATE_PROBE2;
+	}
+
 	/*
 	 * Register this media as a disk.
 	 */
@@ -1940,10 +1950,14 @@ dadone(struct cam_periph *periph, union 
 			struct disk_params *dp;
 			uint32_t block_size;
 			uint64_t maxsector;
+			u_int lbppbe;	/* LB per physical block exponent. */
+			u_int lalba;	/* Lowest aligned LBA. */
 
 			if (softc->state == DA_STATE_PROBE) {
 				block_size = scsi_4btoul(rdcap->length);
 				maxsector = scsi_4btoul(rdcap->addr);
+				lbppbe = 0;
+				lalba = 0;
 
 				/*
 				 * According to SBC-2, if the standard 10
@@ -1963,6 +1977,8 @@ dadone(struct cam_periph *periph, union 
 			} else {
 				block_size = scsi_4btoul(rcaplong->length);
 				maxsector = scsi_8btou64(rcaplong->addr);
+				lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE;
+				lalba = scsi_2btoul(rcaplong->lalba_lbp);
 			}
 
 			/*
@@ -1980,7 +1996,12 @@ dadone(struct cam_periph *periph, union 
 				announce_buf[0] = '\0';
 				cam_periph_invalidate(periph);
 			} else {
-				dasetgeom(periph, block_size, maxsector, 0, 0);
+				dasetgeom(periph, block_size, maxsector,
+				    lbppbe, lalba & SRC16_LALBA);
+				if (lalba & SRC16_LBPME)
+					softc->flags |= DA_FLAG_CAN_LBPME;
+				else
+					softc->flags &= ~DA_FLAG_CAN_LBPME;
 				dp = &softc->params;
 				snprintf(announce_buf, sizeof(announce_buf),
 				        "%juMB (%ju %u byte sectors: %dH %dS/T "
@@ -2047,6 +2068,24 @@ dadone(struct cam_periph *periph, union 
 					    &ascq, /*show_errors*/ 1);
 				}
 				/*
+				 * If we tried READ CAPACITY(16) and failed,
+				 * fallback to READ CAPACITY(10).
+				 */
+				if ((softc->state == DA_STATE_PROBE2) &&
+				    (softc->flags & DA_FLAG_CAN_RC16) &&
+				    (((csio->ccb_h.status & CAM_STATUS_MASK) ==
+					CAM_REQ_INVALID) ||
+				     ((have_sense) &&
+				      (error_code == SSD_CURRENT_ERROR) &&
+				      (sense_key == SSD_KEY_ILLEGAL_REQUEST)))) {
+					softc->flags &= ~DA_FLAG_CAN_RC16;
+					softc->state = DA_STATE_PROBE;
+					free(rdcap, M_SCSIDA);
+					xpt_release_ccb(done_ccb);
+					xpt_schedule(periph, priority);
+					return;
+				} else
+				/*
 				 * Attach to anything that claims to be a
 				 * direct access or optical disk device,
 				 * as long as it doesn't return a "Logical
@@ -2223,13 +2262,18 @@ dagetcapacity(struct cam_periph *periph)
 	struct scsi_read_capacity_data_long *rcaplong;
 	uint32_t block_len;
 	uint64_t maxsector;
-	int error;
+	int error, rc16failed;
 	u_int32_t sense_flags;
+	u_int lbppbe;	/* Logical blocks per physical block exponent. */
+	u_int lalba;	/* Lowest aligned LBA. */
 
 	softc = (struct da_softc *)periph->softc;
 	block_len = 0;
 	maxsector = 0;
+	lbppbe = 0;
+	lalba = 0;
 	error = 0;
+	rc16failed = 0;
 	sense_flags = SF_RETRY_UA;
 	if (softc->flags & DA_FLAG_PACK_REMOVABLE)
 		sense_flags |= SF_NO_PRINT;
@@ -2237,11 +2281,63 @@ dagetcapacity(struct cam_periph *periph)
 	/* Do a read capacity */
 	rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcaplong),
 							M_SCSIDA,
-							M_NOWAIT);
+							M_NOWAIT | M_ZERO);
 	if (rcap == NULL)
 		return (ENOMEM);
-		
+	rcaplong = (struct scsi_read_capacity_data_long *)rcap;
+
 	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
+
+	/* Try READ CAPACITY(16) first if we think it should work. */
+	if (softc->flags & DA_FLAG_CAN_RC16) {
+		scsi_read_capacity_16(&ccb->csio,
+			      /*retries*/ 4,
+			      /*cbfcnp*/ dadone,
+			      /*tag_action*/ MSG_SIMPLE_Q_TAG,
+			      /*lba*/ 0,
+			      /*reladr*/ 0,
+			      /*pmi*/ 0,
+			      rcaplong,
+			      /*sense_len*/ SSD_FULL_SIZE,
+			      /*timeout*/ 60000);
+		ccb->ccb_h.ccb_bp = NULL;
+
+		error = cam_periph_runccb(ccb, daerror,
+				  /*cam_flags*/CAM_RETRY_SELTO,
+				  sense_flags,
+				  softc->disk->d_devstat);
+
+		if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
+			cam_release_devq(ccb->ccb_h.path,
+				 /*relsim_flags*/0,
+				 /*reduction*/0,
+				 /*timeout*/0,
+				 /*getcount_only*/0);
+
+		if (error == 0)
+			goto rc16ok;
+
+		/* If we got ILLEGAL REQUEST, do not prefer RC16 any more. */
+		if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
+		     CAM_REQ_INVALID) {
+			softc->flags &= ~DA_FLAG_CAN_RC16;
+		} else if (((ccb->ccb_h.status & CAM_STATUS_MASK) ==
+		     CAM_SCSI_STATUS_ERROR) &&
+		    (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) &&
+		    (ccb->ccb_h.status & CAM_AUTOSNS_VALID) &&
+		    ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) &&
+		    ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) {
+			int sense_key, error_code, asc, ascq;
+
+			scsi_extract_sense(&ccb->csio.sense_data,
+				   &error_code, &sense_key, &asc, &ascq);
+			if (sense_key == SSD_KEY_ILLEGAL_REQUEST)
+				softc->flags &= ~DA_FLAG_CAN_RC16;
+		}
+		rc16failed = 1;
+	}
+
+	/* Do READ CAPACITY(10). */
 	scsi_read_capacity(&ccb->csio,
 			   /*retries*/4,
 			   /*cbfncp*/dadone,
@@ -2267,13 +2363,12 @@ dagetcapacity(struct cam_periph *periph)
 		block_len = scsi_4btoul(rcap->length);
 		maxsector = scsi_4btoul(rcap->addr);
 
-		if (maxsector != 0xffffffff)
+		if (maxsector != 0xffffffff || rc16failed)
 			goto done;
 	} else
 		goto done;
 
-	rcaplong = (struct scsi_read_capacity_data_long *)rcap;
-
+	/* If READ CAPACITY(10) returned overflow, use READ CAPACITY(16) */
 	scsi_read_capacity_16(&ccb->csio,
 			      /*retries*/ 4,
 			      /*cbfcnp*/ dadone,
@@ -2299,8 +2394,11 @@ dagetcapacity(struct cam_periph *periph)
 				 /*getcount_only*/0);
 
 	if (error == 0) {
+rc16ok:
 		block_len = scsi_4btoul(rcaplong->length);
 		maxsector = scsi_8btou64(rcaplong->addr);
+		lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE;
+		lalba = scsi_2btoul(rcaplong->lalba_lbp);
 	}
 
 done:
@@ -2311,8 +2409,14 @@ done:
 			    "unsupportable block size %ju\n",
 			    (uintmax_t) block_len);
 			error = EINVAL;
-		} else
-			dasetgeom(periph, block_len, maxsector, 0, 0);
+		} else {
+			dasetgeom(periph, block_len, maxsector,
+			    lbppbe, lalba & SRC16_LALBA);
+			if (lalba & SRC16_LBPME)
+				softc->flags |= DA_FLAG_CAN_LBPME;
+			else
+				softc->flags &= ~DA_FLAG_CAN_LBPME;
+		}
 	}
 
 	xpt_release_ccb(ccb);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201203132033.q2DKXGtm061634>