Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 29 Jun 2012 22:33:48 +0000 (UTC)
From:      "Kenneth D. Merry" <ken@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: r237828 - in stable/9: sbin/camcontrol sys/cam/scsi
Message-ID:  <201206292233.q5TMXm0c074386@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ken
Date: Fri Jun 29 22:33:48 2012
New Revision: 237828
URL: http://svn.freebsd.org/changeset/base/237828

Log:
  MFC r237452:
  
    r237452 | ken | 2012-06-22 12:57:06 -0600 (Fri, 22 Jun 2012) | 25 lines
  
    Change 'camcontrol defects' to first probe a drive to find out how much
    defect information it has before grabbing the full defect list.
  
    This works around a bug with some Hitachi drives that generate data overrun
    errors when they are asked for more defect data than they have.
  
    The change is done in a spec-compliant way, so it should have no negative
    impact on drives that don't have this issue.
  
    This is based on work originally done at Sandvine.
  
    scsi_da.h:	Add a define for the maximum amount of data that can be
    		contained in a defect list.
  
    camcontrol.c:	Update the readdefects() function to issue an initial
    		command to determine the length of the defect list, and
    		then use that length in the request for the full defect
    		list.
  
    camcontrol.8:	Add a note that some drives will report 0 defects available
    		if you don't request either the PLIST or GLIST.
  
    Submitted by:	Mark Johnston <markjdb@gmail.com> (original version)

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

Modified: stable/9/sbin/camcontrol/camcontrol.8
==============================================================================
--- stable/9/sbin/camcontrol/camcontrol.8	Fri Jun 29 22:28:31 2012	(r237827)
+++ stable/9/sbin/camcontrol/camcontrol.8	Fri Jun 29 22:33:48 2012	(r237828)
@@ -497,6 +497,8 @@ is specified,
 .Nm
 will print out the number of defects given in the READ DEFECT DATA header
 returned from the drive.
+Some drives will report 0 defects if neither the primary or grown defect
+lists are requested.
 .It Ic modepage
 Allows the user to display and optionally edit a SCSI mode page.
 The mode

Modified: stable/9/sbin/camcontrol/camcontrol.c
==============================================================================
--- stable/9/sbin/camcontrol/camcontrol.c	Fri Jun 29 22:28:31 2012	(r237827)
+++ stable/9/sbin/camcontrol/camcontrol.c	Fri Jun 29 22:33:48 2012	(r237828)
@@ -1799,13 +1799,14 @@ readdefects(struct cam_device *device, i
 	union ccb *ccb = NULL;
 	struct scsi_read_defect_data_10 *rdd_cdb;
 	u_int8_t *defect_list = NULL;
-	u_int32_t dlist_length = 65000;
+	u_int32_t max_dlist_length = SRDD10_MAX_LENGTH, dlist_length = 0;
 	u_int32_t returned_length = 0;
 	u_int32_t num_returned = 0;
 	u_int8_t returned_format;
 	unsigned int i;
 	int c, error = 0;
-	int lists_specified = 0;
+	int lists_specified;
+	int get_length = 1;
 
 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
 		switch(c){
@@ -1842,20 +1843,33 @@ readdefects(struct cam_device *device, i
 	ccb = cam_getccb(device);
 
 	/*
-	 * Hopefully 65000 bytes is enough to hold the defect list.  If it
-	 * isn't, the disk is probably dead already.  We'd have to go with
-	 * 12 byte command (i.e. alloc_length is 32 bits instead of 16)
-	 * to hold them all.
+	 * Eventually we should probably support the 12 byte READ DEFECT
+	 * DATA command.  It supports a longer parameter list, which may be
+	 * necessary on newer drives with lots of defects.  According to
+	 * the SBC-3 spec, drives are supposed to return an illegal request
+	 * if they have more defect data than will fit in 64K.
 	 */
-	defect_list = malloc(dlist_length);
+	defect_list = malloc(max_dlist_length);
 	if (defect_list == NULL) {
 		warnx("can't malloc memory for defect list");
 		error = 1;
 		goto defect_bailout;
 	}
 
+	/*
+	 * We start off asking for just the header to determine how much
+	 * defect data is available.  Some Hitachi drives return an error
+	 * if you ask for more data than the drive has.  Once we know the
+	 * length, we retry the command with the returned length.
+	 */
+	dlist_length = sizeof(struct scsi_read_defect_data_hdr_10);
+
 	rdd_cdb =(struct scsi_read_defect_data_10 *)&ccb->csio.cdb_io.cdb_bytes;
 
+retry:
+
+	lists_specified = 0;
+
 	/*
 	 * cam_getccb() zeros the CCB header only.  So we need to zero the
 	 * payload portion of the ccb.
@@ -1917,6 +1931,51 @@ readdefects(struct cam_device *device, i
 	returned_length = scsi_2btoul(((struct
 		scsi_read_defect_data_hdr_10 *)defect_list)->length);
 
+	if (get_length != 0) {
+		get_length = 0;
+
+		if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
+		     CAM_SCSI_STATUS_ERROR) {
+			struct scsi_sense_data *sense;
+			int error_code, sense_key, asc, ascq;
+
+			sense = &ccb->csio.sense_data;
+			scsi_extract_sense_len(sense, ccb->csio.sense_len -
+			    ccb->csio.sense_resid, &error_code, &sense_key,
+			    &asc, &ascq, /*show_errors*/ 1);
+
+			/*
+			 * If the drive is reporting that it just doesn't
+			 * support the defect list format, go ahead and use
+			 * the length it reported.  Otherwise, the length
+			 * may not be valid, so use the maximum.
+			 */
+			if ((sense_key == SSD_KEY_RECOVERED_ERROR)
+			 && (asc == 0x1c) && (ascq == 0x00)
+			 && (returned_length > 0)) {
+				dlist_length = returned_length +
+				    sizeof(struct scsi_read_defect_data_hdr_10);
+				dlist_length = min(dlist_length,
+						   SRDD10_MAX_LENGTH);
+			} else
+				dlist_length = max_dlist_length;
+		} else if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=
+			    CAM_REQ_CMP){
+			error = 1;
+			warnx("Error reading defect header");
+			if (arglist & CAM_ARG_VERBOSE)
+				cam_error_print(device, ccb, CAM_ESF_ALL,
+						CAM_EPF_ALL, stderr);
+			goto defect_bailout;
+		} else {
+			dlist_length = returned_length +
+			    sizeof(struct scsi_read_defect_data_hdr_10);
+			dlist_length = min(dlist_length, SRDD10_MAX_LENGTH);
+		}
+
+		goto retry;
+	}
+
 	returned_format = ((struct scsi_read_defect_data_hdr_10 *)
 			defect_list)->format;
 

Modified: stable/9/sys/cam/scsi/scsi_da.h
==============================================================================
--- stable/9/sys/cam/scsi/scsi_da.h	Fri Jun 29 22:28:31 2012	(r237827)
+++ stable/9/sys/cam/scsi/scsi_da.h	Fri Jun 29 22:33:48 2012	(r237828)
@@ -111,6 +111,7 @@ struct scsi_read_defect_data_10
 	u_int8_t reserved[4];
 
 	u_int8_t alloc_length[2];
+#define	SRDD10_MAX_LENGTH		0xffff
 
 	u_int8_t control;
 };



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