Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 8 Jan 2015 16:58:41 +0000 (UTC)
From:      "Kenneth D. Merry" <ken@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r276835 - in head: sbin/camcontrol sys/cam/scsi
Message-ID:  <201501081658.t08GwfUU076220@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ken
Date: Thu Jan  8 16:58:40 2015
New Revision: 276835
URL: https://svnweb.freebsd.org/changeset/base/276835

Log:
  Improve camcontrol(8) handling of drive defect data.
  
  This includes a new summary mode (-s) for camcontrol defects that
  quickly tells the user the most important thing: how many defects
  are in the requested list.  The actual location of the defects is
  less important.
  
  Modern drives frequently have more than the 8191 defects that can
  be reported by the READ DEFECT DATA (10) command.  If they don't
  have that many grown defects, they certainly have more than 8191
  defects in the primary (i.e. factory) defect list.
  
  The READ DEFECT DATA (12) command allows for longer parameter
  lists, as well as indexing into the list of defects, and so allows
  reporting many more defects.
  
  This has been tested with HGST drives and Seagate drives, but
  does not fully work with Seagate drives.  Once I have a Seagate
  spec I may be able to determine whether it is possible to make it
  work with Seagate drives.
  
  scsi_da.h:	Add a definition for the new long block defect
  		format.
  
  		Add bit and mask definitions for the new extended
  		physical sector and bytes from index defect
  		formats.
  
  		Add a prototype for the new scsi_read_defects() CDB
  		building function.
  
  scsi_da.c:	Add a new scsi_read_defects() CDB building function.
  		camcontrol(8) was previously composing CDBs manually.
  		This is long overdue.
  
  camcontrol.c:	Revamp the camcontrol defects subcommand.  We now
  		go through multiple stages in trying to get defect
  		data off the drive while avoiding various drive
  		firmware quirks.
  
  		We start off by requesting the defect header with
  		the 10 byte command.  If we're in summary mode (-s)
  		and the drive reports fewer defects than can be
  		represented in the 10 byte header, we're done.
  		Otherwise, we know that we need to issue the
  		12 byte command if the drive reports the maximum
  		number of defects.
  
  		If we're in summary mode, we're done if we get a
  		good response back when asking for the 12 byte header.
  
  		If the user has asked for the full list, then we
  		use the address descriptor index field in the 12
  		byte CDB to step through the list in 64K chunks.
  		64K is small enough to work with most any ancient
  		or modern SCSI controller.
  
  		Add support for printing the new long block defect
  		format, as well as the extended physical sector and
  		bytes from index formats.  I don't have any drives
  		that support the new formats.
  
  		Add a hexadecimal output format that can be turned
  		on with -X.
  
  		Add a quiet mode (-q) that can be turned on with
  		the summary mode (-s) to just print out a number.
  
  		Revamp the error detection and recovery code for
  		the defects command to work with HGST drives.
  
  		Call the new scsi_read_defects() CDB building
  		function instead of rolling the CDB ourselves.
  
  		Pay attention to the residual from the defect list
  		request when printing it out, so we don't run off
  		the end of the list.
  
  		Use the new scsi_nv library routines to convert
  		from strings to numbers and back.
  
  camcontrol.8:	Document the new defect formats (longblock, extbfi,
  		extphys) and command line options (-q, -s, -S and
  		-X) for the defects subcommand.
  
  		Explain a little more about what drives generally
  		do and don't support.
  
  Sponsored by:	Spectra Logic
  MFC after:	1 week

Modified:
  head/sbin/camcontrol/camcontrol.8
  head/sbin/camcontrol/camcontrol.c
  head/sys/cam/scsi/scsi_da.c
  head/sys/cam/scsi/scsi_da.h

Modified: head/sbin/camcontrol/camcontrol.8
==============================================================================
--- head/sbin/camcontrol/camcontrol.8	Thu Jan  8 16:50:35 2015	(r276834)
+++ head/sbin/camcontrol/camcontrol.8	Thu Jan  8 16:58:40 2015	(r276835)
@@ -110,6 +110,10 @@
 .Aq Fl f Ar format
 .Op Fl P
 .Op Fl G
+.Op Fl q
+.Op Fl s
+.Op Fl S Ar offset
+.Op Fl X
 .Nm
 .Ic modepage
 .Op device id
@@ -513,18 +517,16 @@ connecting to that device.
 Note that this can have a destructive impact
 on the system.
 .It Ic defects
-Send the SCSI READ DEFECT DATA (10) command (0x37) to the given device, and
+Send the
+.Tn SCSI
+READ DEFECT DATA (10) command (0x37) or the 
+.Tn SCSI
+READ DEFECT DATA (12) command (0xB7) to the given device, and
 print out any combination of: the total number of defects, the primary
 defect list (PLIST), and the grown defect list (GLIST).
 .Bl -tag -width 11n
 .It Fl f Ar format
-The three format options are:
-.Em block ,
-to print out the list as logical blocks,
-.Em bfi ,
-to print out the list in bytes from index format, and
-.Em phys ,
-to print out the list in physical sector format.
+Specify the requested format of the defect list.
 The format argument is
 required.
 Most drives support the physical sector format.
@@ -541,12 +543,52 @@ If the drive uses a non-standard sense c
 support the requested format,
 .Nm
 will probably see the error as a failure to complete the request.
+.Pp
+The format options are:
+.Bl -tag -width 9n
+.It block
+Print out the list as logical blocks.
+This is limited to 32-bit block sizes, and isn't supported by many modern
+drives.
+.It longblock
+Print out the list as logical blocks.
+This option uses a 64-bit block size.
+.It bfi 
+Print out the list in bytes from index format.
+.It extbfi
+Print out the list in extended bytes from index format.
+The extended format allows for ranges of blocks to be printed.
+.It phys 
+Print out the list in physical sector format.
+Most drives support this format.
+.It extphys
+Print out the list in extended physical sector format.
+The extended format allows for ranges of blocks to be printed.
+.El
+.Pp
 .It Fl G
 Print out the grown defect list.
 This is a list of bad blocks that have
 been remapped since the disk left the factory.
 .It Fl P
 Print out the primary defect list.
+This is the list of defects that were present in the factory.
+.It Fl q
+When printing status information with
+.Fl s ,
+only print the number of defects.
+.It Fl s
+Just print the number of defects, not the list of defects.
+.It Fl S Ar offset
+Specify the starting offset into the defect list.
+This implies using the
+.Tn SCSI
+READ DEFECT DATA (12) command, as the 10 byte version of the command
+doesn't support the address descriptor index field.
+Not all drives support the 12 byte command, and some drives that support
+the 12 byte command don't support the address descriptor index field.
+.It Fl X
+Print out defects in hexadecimal (base 16) form instead of base 10 form.
 .El
 .Pp
 If neither

Modified: head/sbin/camcontrol/camcontrol.c
==============================================================================
--- head/sbin/camcontrol/camcontrol.c	Thu Jan  8 16:50:35 2015	(r276834)
+++ head/sbin/camcontrol/camcontrol.c	Thu Jan  8 16:58:40 2015	(r276835)
@@ -167,7 +167,7 @@ struct ata_set_max_pwd
 };
 
 static const char scsicmd_opts[] = "a:c:dfi:o:r";
-static const char readdefect_opts[] = "f:GP";
+static const char readdefect_opts[] = "f:GPqsS:X";
 static const char negotiate_opts[] = "acD:M:O:qR:T:UW:";
 static const char smprg_opts[] = "l";
 static const char smppc_opts[] = "a:A:d:lm:M:o:p:s:S:T:";
@@ -3369,39 +3369,64 @@ scanlun_or_reset_dev(path_id_t bus, targ
 }
 
 #ifndef MINIMALISTIC
+
+static struct scsi_nv defect_list_type_map[] = {
+	{ "block", SRDD10_BLOCK_FORMAT },
+	{ "extbfi", SRDD10_EXT_BFI_FORMAT },
+	{ "extphys", SRDD10_EXT_PHYS_FORMAT },
+	{ "longblock", SRDD10_LONG_BLOCK_FORMAT },
+	{ "bfi", SRDD10_BYTES_FROM_INDEX_FORMAT },
+	{ "phys", SRDD10_PHYSICAL_SECTOR_FORMAT }
+};
+
 static int
 readdefects(struct cam_device *device, int argc, char **argv,
 	    char *combinedopt, int retry_count, int timeout)
 {
 	union ccb *ccb = NULL;
-	struct scsi_read_defect_data_10 *rdd_cdb;
+	struct scsi_read_defect_data_hdr_10 *hdr10 = NULL;
+	struct scsi_read_defect_data_hdr_12 *hdr12 = NULL;
+	size_t hdr_size = 0, entry_size = 0;
+	int use_12byte = 0;
+	int hex_format = 0;
 	u_int8_t *defect_list = NULL;
-	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;
+	u_int8_t list_format = 0;
+	int list_type_set = 0;
+	u_int32_t dlist_length = 0;
+	u_int32_t returned_length = 0, valid_len = 0;
+	u_int32_t num_returned = 0, num_valid = 0;
+	u_int32_t max_possible_size = 0, hdr_max = 0;
+	u_int32_t starting_offset = 0;
+	u_int8_t returned_format, returned_type;
 	unsigned int i;
+	int summary = 0, quiet = 0;
 	int c, error = 0;
-	int lists_specified;
-	int get_length = 1;
+	int lists_specified = 0;
+	int get_length = 1, first_pass = 1;
+	int mads = 0;
 
 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
 		switch(c){
 		case 'f':
 		{
-			char *tstr;
-			tstr = optarg;
-			while (isspace(*tstr) && (*tstr != '\0'))
-				tstr++;
-			if (strcmp(tstr, "block") == 0)
-				arglist |= CAM_ARG_FORMAT_BLOCK;
-			else if (strcmp(tstr, "bfi") == 0)
-				arglist |= CAM_ARG_FORMAT_BFI;
-			else if (strcmp(tstr, "phys") == 0)
-				arglist |= CAM_ARG_FORMAT_PHYS;
-			else {
+			scsi_nv_status status;
+			int entry_num = 0;
+
+			status = scsi_get_nv(defect_list_type_map,
+			    sizeof(defect_list_type_map) /
+			    sizeof(defect_list_type_map[0]), optarg,
+			    &entry_num, SCSI_NV_FLAG_IG_CASE);
+
+			if (status == SCSI_NV_FOUND) {
+				list_format = defect_list_type_map[
+				    entry_num].value;
+				list_type_set = 1;
+			} else {
+				warnx("%s: %s %s option %s", __func__,
+				    (status == SCSI_NV_AMBIGUOUS) ?
+				    "ambiguous" : "invalid", "defect list type",
+				    optarg);
 				error = 1;
-				warnx("invalid defect format %s", tstr);
 				goto defect_bailout;
 			}
 			break;
@@ -3412,40 +3437,82 @@ readdefects(struct cam_device *device, i
 		case 'P':
 			arglist |= CAM_ARG_PLIST;
 			break;
+		case 'q':
+			quiet = 1;
+			break;
+		case 's':
+			summary = 1;
+			break;
+		case 'S': {
+			char *endptr;
+
+			starting_offset = strtoul(optarg, &endptr, 0);
+			if (*endptr != '\0') {
+				error = 1;
+				warnx("invalid starting offset %s", optarg);
+				goto defect_bailout;
+			}
+			break;
+		}
+		case 'X':
+			hex_format = 1;
+			break;
 		default:
 			break;
 		}
 	}
 
-	ccb = cam_getccb(device);
-
-	/*
-	 * 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(max_dlist_length);
-	if (defect_list == NULL) {
-		warnx("can't malloc memory for defect list");
+	if (list_type_set == 0) {
 		error = 1;
+		warnx("no defect list format specified");
 		goto defect_bailout;
 	}
 
+	if (arglist & CAM_ARG_PLIST) {
+		list_format |= SRDD10_PLIST;
+		lists_specified++;
+	}
+
+	if (arglist & CAM_ARG_GLIST) {
+		list_format |= SRDD10_GLIST;
+		lists_specified++;
+	}
+
+	/*
+	 * This implies a summary, and was the previous behavior.
+	 */
+	if (lists_specified == 0)
+		summary = 1;
+
+	ccb = cam_getccb(device);
+
+retry_12byte:
+
 	/*
 	 * 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;
+	if (use_12byte == 0)
+		dlist_length = sizeof(*hdr10);
+	else
+		dlist_length = sizeof(*hdr12);
 
 retry:
+	if (defect_list != NULL) {
+		free(defect_list);
+		defect_list = NULL;
+	}
+	defect_list = malloc(dlist_length);
+	if (defect_list == NULL) {
+		warnx("can't malloc memory for defect list");
+		error = 1;
+		goto defect_bailout;
+	}
 
-	lists_specified = 0;
+next_batch:
+	bzero(defect_list, dlist_length);
 
 	/*
 	 * cam_getccb() zeros the CCB header only.  So we need to zero the
@@ -3454,41 +3521,17 @@ retry:
 	bzero(&(&ccb->ccb_h)[1],
 	      sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
 
-	cam_fill_csio(&ccb->csio,
-		      /*retries*/ retry_count,
-		      /*cbfcnp*/ NULL,
-		      /*flags*/ CAM_DIR_IN | ((arglist & CAM_ARG_ERR_RECOVER) ?
-					      CAM_PASS_ERR_RECOVER : 0),
-		      /*tag_action*/ MSG_SIMPLE_Q_TAG,
-		      /*data_ptr*/ defect_list,
-		      /*dxfer_len*/ dlist_length,
-		      /*sense_len*/ SSD_FULL_SIZE,
-		      /*cdb_len*/ sizeof(struct scsi_read_defect_data_10),
-		      /*timeout*/ timeout ? timeout : 5000);
-
-	rdd_cdb->opcode = READ_DEFECT_DATA_10;
-	if (arglist & CAM_ARG_FORMAT_BLOCK)
-		rdd_cdb->format = SRDD10_BLOCK_FORMAT;
-	else if (arglist & CAM_ARG_FORMAT_BFI)
-		rdd_cdb->format = SRDD10_BYTES_FROM_INDEX_FORMAT;
-	else if (arglist & CAM_ARG_FORMAT_PHYS)
-		rdd_cdb->format = SRDD10_PHYSICAL_SECTOR_FORMAT;
-	else {
-		error = 1;
-		warnx("no defect list format specified");
-		goto defect_bailout;
-	}
-	if (arglist & CAM_ARG_PLIST) {
-		rdd_cdb->format |= SRDD10_PLIST;
-		lists_specified++;
-	}
-
-	if (arglist & CAM_ARG_GLIST) {
-		rdd_cdb->format |= SRDD10_GLIST;
-		lists_specified++;
-	}
-
-	scsi_ulto2b(dlist_length, rdd_cdb->alloc_length);
+	scsi_read_defects(&ccb->csio,
+			  /*retries*/ retry_count,
+			  /*cbfcnp*/ NULL,
+			  /*tag_action*/ MSG_SIMPLE_Q_TAG,
+			  /*list_format*/ list_format,
+			  /*addr_desc_index*/ starting_offset,
+			  /*data_ptr*/ defect_list,
+			  /*dxfer_len*/ dlist_length,
+			  /*minimum_cmd_size*/ use_12byte ? 12 : 0,
+			  /*sense_len*/ SSD_FULL_SIZE,
+			  /*timeout*/ timeout ? timeout : 5000);
 
 	/* Disable freezing the device queue */
 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
@@ -3505,8 +3548,61 @@ retry:
 		goto defect_bailout;
 	}
 
-	returned_length = scsi_2btoul(((struct
-		scsi_read_defect_data_hdr_10 *)defect_list)->length);
+	valid_len = ccb->csio.dxfer_len - ccb->csio.resid;
+
+	if (use_12byte == 0) {
+		hdr10 = (struct scsi_read_defect_data_hdr_10 *)defect_list;
+		hdr_size = sizeof(*hdr10);
+		hdr_max = SRDDH10_MAX_LENGTH;
+
+		if (valid_len >= hdr_size) {
+			returned_length = scsi_2btoul(hdr10->length);
+			returned_format = hdr10->format;
+		} else {
+			returned_length = 0;
+			returned_format = 0;
+		}
+	} else {
+		hdr12 = (struct scsi_read_defect_data_hdr_12 *)defect_list;
+		hdr_size = sizeof(*hdr12);
+		hdr_max = SRDDH12_MAX_LENGTH;
+
+		if (valid_len >= hdr_size) {
+			returned_length = scsi_4btoul(hdr12->length);
+			returned_format = hdr12->format;
+		} else {
+			returned_length = 0;
+			returned_format = 0;
+		}
+	}
+
+	returned_type = returned_format & SRDDH10_DLIST_FORMAT_MASK;
+	switch (returned_type) {
+	case SRDD10_BLOCK_FORMAT:
+		entry_size = sizeof(struct scsi_defect_desc_block);
+		break;
+	case SRDD10_LONG_BLOCK_FORMAT:
+		entry_size = sizeof(struct scsi_defect_desc_long_block);
+		break;
+	case SRDD10_EXT_PHYS_FORMAT:
+	case SRDD10_PHYSICAL_SECTOR_FORMAT:
+		entry_size = sizeof(struct scsi_defect_desc_phys_sector);
+		break;
+	case SRDD10_EXT_BFI_FORMAT:
+	case SRDD10_BYTES_FROM_INDEX_FORMAT:
+		entry_size = sizeof(struct scsi_defect_desc_bytes_from_index);
+		break;
+	default:
+		warnx("Unknown defect format 0x%x\n", returned_type);
+		error = 1;
+		goto defect_bailout;
+		break;
+	} 
+
+	max_possible_size = (hdr_max / entry_size) * entry_size;
+	num_returned = returned_length / entry_size;
+	num_valid = min(returned_length, valid_len - hdr_size);
+	num_valid /= entry_size;
 
 	if (get_length != 0) {
 		get_length = 0;
@@ -3530,12 +3626,66 @@ retry:
 			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;
+				if ((use_12byte == 0)
+				 && (returned_length >= max_possible_size)) {
+					get_length = 1;
+					use_12byte = 1;
+					goto retry_12byte;
+				}
+				dlist_length = returned_length + hdr_size;
+			} else if ((sense_key == SSD_KEY_RECOVERED_ERROR)
+				&& (asc == 0x1f) && (ascq == 0x00)
+				&& (returned_length > 0)) {
+				/* Partial defect list transfer */
+				/*
+				 * Hitachi drives return this error
+				 * along with a partial defect list if they
+				 * have more defects than the 10 byte
+				 * command can support.  Retry with the 12
+				 * byte command.
+				 */
+				if (use_12byte == 0) {
+					get_length = 1;
+					use_12byte = 1;
+					goto retry_12byte;
+				}
+				dlist_length = returned_length + hdr_size;
+			} else if ((sense_key == SSD_KEY_ILLEGAL_REQUEST)
+				&& (asc == 0x24) && (ascq == 0x00)) {
+				/* Invalid field in CDB */
+				/*
+				 * SBC-3 says that if the drive has more
+				 * defects than can be reported with the
+				 * 10 byte command, it should return this
+	 			 * error and no data.  Retry with the 12
+				 * byte command.
+				 */
+				if (use_12byte == 0) {
+					get_length = 1;
+					use_12byte = 1;
+					goto retry_12byte;
+				}
+				dlist_length = returned_length + hdr_size;
+			} else {
+				/*
+				 * If we got a SCSI error and no valid length,
+				 * just use the 10 byte maximum.  The 12
+				 * byte maximum is too large.
+				 */
+				if (returned_length == 0)
+					dlist_length = SRDD10_MAX_LENGTH;
+				else {
+					if ((use_12byte == 0)
+					 && (returned_length >=
+					     max_possible_size)) {
+						get_length = 1;
+						use_12byte = 1;
+						goto retry_12byte;
+					}
+					dlist_length = returned_length +
+					    hdr_size;
+				}
+			}
 		} else if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=
 			    CAM_REQ_CMP){
 			error = 1;
@@ -3545,16 +3695,40 @@ retry:
 						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);
+			if ((use_12byte == 0)
+			 && (returned_length >= max_possible_size)) {
+				get_length = 1;
+				use_12byte = 1;
+				goto retry_12byte;
+			}
+			dlist_length = returned_length + hdr_size;
+		}
+		if (summary != 0) {
+			fprintf(stdout, "%u", num_returned);
+			if (quiet == 0) {
+				fprintf(stdout, " defect%s",
+					(num_returned != 1) ? "s" : "");
+			}
+			fprintf(stdout, "\n");
+
+			goto defect_bailout;
 		}
 
+		/*
+		 * We always limit the list length to the 10-byte maximum
+		 * length (0xffff).  The reason is that some controllers
+		 * can't handle larger I/Os, and we can transfer the entire
+		 * 10 byte list in one shot.  For drives that support the 12
+		 * byte read defects command, we'll step through the list
+		 * by specifying a starting offset.  For drives that don't
+		 * support the 12 byte command's starting offset, we'll
+		 * just display the first 64K.
+		 */
+		dlist_length = min(dlist_length, SRDD10_MAX_LENGTH);
+
 		goto retry;
 	}
 
-	returned_format = ((struct scsi_read_defect_data_hdr_10 *)
-			defect_list)->format;
 
 	if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)
 	 && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)
@@ -3571,32 +3745,32 @@ retry:
 		 * According to the SCSI spec, if the disk doesn't support
 		 * the requested format, it will generally return a sense
 		 * key of RECOVERED ERROR, and an additional sense code
-		 * of "DEFECT LIST NOT FOUND".  So, we check for that, and
-		 * also check to make sure that the returned length is
-		 * greater than 0, and then print out whatever format the
-		 * disk gave us.
+		 * of "DEFECT LIST NOT FOUND".  HGST drives also return
+		 * Primary/Grown defect list not found errors.  So just
+		 * check for an ASC of 0x1c.
 		 */
 		if ((sense_key == SSD_KEY_RECOVERED_ERROR)
-		 && (asc == 0x1c) && (ascq == 0x00)
-		 && (returned_length > 0)) {
-			warnx("requested defect format not available");
-			switch(returned_format & SRDDH10_DLIST_FORMAT_MASK) {
-			case SRDD10_BLOCK_FORMAT:
-				warnx("Device returned block format");
-				break;
-			case SRDD10_BYTES_FROM_INDEX_FORMAT:
-				warnx("Device returned bytes from index"
-				      " format");
-				break;
-			case SRDD10_PHYSICAL_SECTOR_FORMAT:
-				warnx("Device returned physical sector format");
-				break;
-			default:
+		 && (asc == 0x1c)) {
+			const char *format_str;
+
+			format_str = scsi_nv_to_str(defect_list_type_map,
+			    sizeof(defect_list_type_map) /
+			    sizeof(defect_list_type_map[0]),
+			    list_format & SRDD10_DLIST_FORMAT_MASK);
+			warnx("requested defect format %s not available",
+			    format_str ? format_str : "unknown");
+
+			format_str = scsi_nv_to_str(defect_list_type_map,
+			    sizeof(defect_list_type_map) /
+			    sizeof(defect_list_type_map[0]), returned_type);
+			if (format_str != NULL) {
+				warnx("Device returned %s format",
+				    format_str);
+			} else {
 				error = 1;
 				warnx("Device returned unknown defect"
-				     " data format %#x", returned_format);
+				     " data format %#x", returned_type);
 				goto defect_bailout;
-				break; /* NOTREACHED */
 			}
 		} else {
 			error = 1;
@@ -3615,99 +3789,151 @@ retry:
 		goto defect_bailout;
 	}
 
+	if (first_pass != 0) {
+		fprintf(stderr, "Got %d defect", num_returned);
+
+		if ((lists_specified == 0) || (num_returned == 0)) {
+			fprintf(stderr, "s.\n");
+			goto defect_bailout;
+		} else if (num_returned == 1)
+			fprintf(stderr, ":\n");
+		else
+			fprintf(stderr, "s:\n");
+
+		first_pass = 0;
+	}
+
 	/*
 	 * XXX KDM  I should probably clean up the printout format for the
 	 * disk defects.
 	 */
-	switch (returned_format & SRDDH10_DLIST_FORMAT_MASK){
-		case SRDDH10_PHYSICAL_SECTOR_FORMAT:
-		{
-			struct scsi_defect_desc_phys_sector *dlist;
-
-			dlist = (struct scsi_defect_desc_phys_sector *)
-				(defect_list +
-				sizeof(struct scsi_read_defect_data_hdr_10));
-
-			num_returned = returned_length /
-				sizeof(struct scsi_defect_desc_phys_sector);
-
-			fprintf(stderr, "Got %d defect", num_returned);
-
-			if ((lists_specified == 0) || (num_returned == 0)) {
-				fprintf(stderr, "s.\n");
-				break;
-			} else if (num_returned == 1)
-				fprintf(stderr, ":\n");
+	switch (returned_type) {
+	case SRDD10_PHYSICAL_SECTOR_FORMAT:
+	case SRDD10_EXT_PHYS_FORMAT:
+	{
+		struct scsi_defect_desc_phys_sector *dlist;
+
+		dlist = (struct scsi_defect_desc_phys_sector *)
+			(defect_list + hdr_size);
+
+		for (i = 0; i < num_valid; i++) {
+			uint32_t sector;
+
+			sector = scsi_4btoul(dlist[i].sector);
+			if (returned_type == SRDD10_EXT_PHYS_FORMAT) {
+				mads = (sector & SDD_EXT_PHYS_MADS) ?
+				       0 : 1;
+				sector &= ~SDD_EXT_PHYS_FLAG_MASK;
+			}
+			if (hex_format == 0)
+				fprintf(stdout, "%d:%d:%d%s",
+					scsi_3btoul(dlist[i].cylinder),
+					dlist[i].head,
+					scsi_4btoul(dlist[i].sector),
+					mads ? " - " : "\n");
 			else
-				fprintf(stderr, "s:\n");
-
-			for (i = 0; i < num_returned; i++) {
-				fprintf(stdout, "%d:%d:%d\n",
+				fprintf(stdout, "0x%x:0x%x:0x%x%s",
 					scsi_3btoul(dlist[i].cylinder),
 					dlist[i].head,
-					scsi_4btoul(dlist[i].sector));
-			}
-			break;
+					scsi_4btoul(dlist[i].sector),
+					mads ? " - " : "\n");
+			mads = 0;
+		}
+		if (num_valid < num_returned) {
+			starting_offset += num_valid;
+			goto next_batch;
 		}
-		case SRDDH10_BYTES_FROM_INDEX_FORMAT:
-		{
-			struct scsi_defect_desc_bytes_from_index *dlist;
-
-			dlist = (struct scsi_defect_desc_bytes_from_index *)
-				(defect_list +
-				sizeof(struct scsi_read_defect_data_hdr_10));
-
-			num_returned = returned_length /
-			      sizeof(struct scsi_defect_desc_bytes_from_index);
-
-			fprintf(stderr, "Got %d defect", num_returned);
-
-			if ((lists_specified == 0) || (num_returned == 0)) {
-				fprintf(stderr, "s.\n");
-				break;
-			} else if (num_returned == 1)
-				fprintf(stderr, ":\n");
+		break;
+	}
+	case SRDD10_BYTES_FROM_INDEX_FORMAT:
+	case SRDD10_EXT_BFI_FORMAT:
+	{
+		struct scsi_defect_desc_bytes_from_index *dlist;
+
+		dlist = (struct scsi_defect_desc_bytes_from_index *)
+			(defect_list + hdr_size);
+
+		for (i = 0; i < num_valid; i++) {
+			uint32_t bfi;
+
+			bfi = scsi_4btoul(dlist[i].bytes_from_index);
+			if (returned_type == SRDD10_EXT_BFI_FORMAT) {
+				mads = (bfi & SDD_EXT_BFI_MADS) ? 1 : 0;
+				bfi &= ~SDD_EXT_BFI_FLAG_MASK;
+			}
+			if (hex_format == 0)
+				fprintf(stdout, "%d:%d:%d%s",
+					scsi_3btoul(dlist[i].cylinder),
+					dlist[i].head,
+					scsi_4btoul(dlist[i].bytes_from_index),
+					mads ? " - " : "\n");
 			else
-				fprintf(stderr, "s:\n");
-
-			for (i = 0; i < num_returned; i++) {
-				fprintf(stdout, "%d:%d:%d\n",
+				fprintf(stdout, "0x%x:0x%x:0x%x%s",
 					scsi_3btoul(dlist[i].cylinder),
 					dlist[i].head,
-					scsi_4btoul(dlist[i].bytes_from_index));
-			}
-			break;
+					scsi_4btoul(dlist[i].bytes_from_index),
+					mads ? " - " : "\n");
+
+			mads = 0;
 		}
-		case SRDDH10_BLOCK_FORMAT:
-		{
-			struct scsi_defect_desc_block *dlist;
+		if (num_valid < num_returned) {
+			starting_offset += num_valid;
+			goto next_batch;
+		}
+		break;
+	}
+	case SRDDH10_BLOCK_FORMAT:
+	{
+		struct scsi_defect_desc_block *dlist;
 
-			dlist = (struct scsi_defect_desc_block *)(defect_list +
-				sizeof(struct scsi_read_defect_data_hdr_10));
+		dlist = (struct scsi_defect_desc_block *)
+			(defect_list + hdr_size);
 
-			num_returned = returned_length /
-			      sizeof(struct scsi_defect_desc_block);
+		for (i = 0; i < num_valid; i++) {
+			if (hex_format == 0)
+				fprintf(stdout, "%u\n",
+					scsi_4btoul(dlist[i].address));
+			else
+				fprintf(stdout, "0x%x\n",
+					scsi_4btoul(dlist[i].address));
+		}
 
-			fprintf(stderr, "Got %d defect", num_returned);
+		if (num_valid < num_returned) {
+			starting_offset += num_valid;
+			goto next_batch;
+		}
 
-			if ((lists_specified == 0) || (num_returned == 0)) {
-				fprintf(stderr, "s.\n");
-				break;
-			} else if (num_returned == 1)
-				fprintf(stderr, ":\n");
+		break;
+	}
+	case SRDD10_LONG_BLOCK_FORMAT:
+	{
+		struct scsi_defect_desc_long_block *dlist;
+
+		dlist = (struct scsi_defect_desc_long_block *)
+			(defect_list + hdr_size);
+
+		for (i = 0; i < num_valid; i++) {
+			if (hex_format == 0)
+				fprintf(stdout, "%ju\n",
+					(uintmax_t)scsi_8btou64(
+					dlist[i].address));
 			else
-				fprintf(stderr, "s:\n");
+				fprintf(stdout, "0x%jx\n",
+					(uintmax_t)scsi_8btou64(
+					dlist[i].address));
+		}
 
-			for (i = 0; i < num_returned; i++)
-				fprintf(stdout, "%u\n",
-					scsi_4btoul(dlist[i].address));
-			break;
+		if (num_valid < num_returned) {
+			starting_offset += num_valid;
+			goto next_batch;
 		}
-		default:
-			fprintf(stderr, "Unknown defect format %d\n",
-				returned_format & SRDDH10_DLIST_FORMAT_MASK);
-			error = 1;
-			break;
+		break;
+	}
+	default:
+		fprintf(stderr, "Unknown defect format 0x%x\n",
+			returned_type);
+		error = 1;
+		break;
 	}
 defect_bailout:
 
@@ -7801,6 +8027,7 @@ usage(int printlong)
 "        camcontrol reset      <all | bus[:target:lun]>\n"
 #ifndef MINIMALISTIC
 "        camcontrol defects    [dev_id][generic args] <-f format> [-P][-G]\n"
+"                              [-q][-s][-S offset][-X]\n"
 "        camcontrol modepage   [dev_id][generic args] <-m page | -l>\n"
 "                              [-P pagectl][-e | -b][-d]\n"
 "        camcontrol cmd        [dev_id][generic args]\n"

Modified: head/sys/cam/scsi/scsi_da.c
==============================================================================
--- head/sys/cam/scsi/scsi_da.c	Thu Jan  8 16:50:35 2015	(r276834)
+++ head/sys/cam/scsi/scsi_da.c	Thu Jan  8 16:58:40 2015	(r276835)
@@ -3872,9 +3872,9 @@ dashutdown(void * arg, int howto)
 #else /* !_KERNEL */
 
 /*
- * XXX This is only left out of the kernel build to silence warnings.  If,
- * for some reason this function is used in the kernel, the ifdefs should
- * be moved so it is included both in the kernel and userland.
+ * XXX These are only left out of the kernel build to silence warnings.  If,
+ * for some reason these functions are used in the kernel, the ifdefs should
+ * be moved so they are included both in the kernel and userland.
  */
 void
 scsi_format_unit(struct ccb_scsiio *csio, u_int32_t retries,
@@ -3903,6 +3903,59 @@ scsi_format_unit(struct ccb_scsiio *csio
 }
 
 void
+scsi_read_defects(struct ccb_scsiio *csio, uint32_t retries,
+		  void (*cbfcnp)(struct cam_periph *, union ccb *),
+		  uint8_t tag_action, uint8_t list_format,
+		  uint32_t addr_desc_index, uint8_t *data_ptr,
+		  uint32_t dxfer_len, int minimum_cmd_size, 
+		  uint8_t sense_len, uint32_t timeout)
+{
+	uint8_t cdb_len;
+
+	/*
+	 * These conditions allow using the 10 byte command.  Otherwise we
+	 * need to use the 12 byte command.
+	 */
+	if ((minimum_cmd_size <= 10)
+	 && (addr_desc_index == 0) 
+	 && (dxfer_len <= SRDD10_MAX_LENGTH)) {
+		struct scsi_read_defect_data_10 *cdb10;
+
+		cdb10 = (struct scsi_read_defect_data_10 *)
+			&csio->cdb_io.cdb_bytes;
+
+		cdb_len = sizeof(*cdb10);
+		bzero(cdb10, cdb_len);
+                cdb10->opcode = READ_DEFECT_DATA_10;
+                cdb10->format = list_format;
+                scsi_ulto2b(dxfer_len, cdb10->alloc_length);
+	} else {
+		struct scsi_read_defect_data_12 *cdb12;
+
+		cdb12 = (struct scsi_read_defect_data_12 *)
+			&csio->cdb_io.cdb_bytes;
+
+		cdb_len = sizeof(*cdb12);
+		bzero(cdb12, cdb_len);
+                cdb12->opcode = READ_DEFECT_DATA_12;
+                cdb12->format = list_format;
+                scsi_ulto4b(dxfer_len, cdb12->alloc_length);
+		scsi_ulto4b(addr_desc_index, cdb12->address_descriptor_index);
+	}
+
+	cam_fill_csio(csio,
+		      retries,
+		      cbfcnp,
+		      /*flags*/ CAM_DIR_IN,
+		      tag_action,
+		      data_ptr,
+		      dxfer_len,
+		      sense_len,
+		      cdb_len,
+		      timeout);
+}
+
+void
 scsi_sanitize(struct ccb_scsiio *csio, u_int32_t retries,
 	      void (*cbfcnp)(struct cam_periph *, union ccb *),
 	      u_int8_t tag_action, u_int8_t byte2, u_int16_t control,

Modified: head/sys/cam/scsi/scsi_da.h
==============================================================================
--- head/sys/cam/scsi/scsi_da.h	Thu Jan  8 16:50:35 2015	(r276834)
+++ head/sys/cam/scsi/scsi_da.h	Thu Jan  8 16:58:40 2015	(r276835)
@@ -98,8 +98,12 @@ struct scsi_read_defect_data_10
 #define SRDD10_PLIST 0x10
 #define SRDD10_DLIST_FORMAT_MASK 0x07
 #define SRDD10_BLOCK_FORMAT            0x00
+#define SRDD10_EXT_BFI_FORMAT 	       0x01
+#define SRDD10_EXT_PHYS_FORMAT 	       0x02
+#define SRDD10_LONG_BLOCK_FORMAT       0x03
 #define SRDD10_BYTES_FROM_INDEX_FORMAT 0x04
 #define SRDD10_PHYSICAL_SECTOR_FORMAT  0x05
+#define SRDD10_VENDOR_FORMAT	       0x06
 	uint8_t format;
 	uint8_t reserved[4];
 	uint8_t alloc_length[2];
@@ -138,12 +142,13 @@ struct scsi_read_defect_data_12
 #define SRDD12_GLIST 0x08
 #define SRDD12_PLIST 0x10
 #define SRDD12_DLIST_FORMAT_MASK 0x07
-#define SRDD12_BLOCK_FORMAT            0x00
-#define SRDD12_BYTES_FROM_INDEX_FORMAT 0x04
-#define SRDD12_PHYSICAL_SECTOR_FORMAT  0x05
+#define SRDD12_BLOCK_FORMAT            SRDD10_BLOCK_FORMAT
+#define SRDD12_BYTES_FROM_INDEX_FORMAT SRDD10_BYTES_FROM_INDEX_FORMAT
+#define SRDD12_PHYSICAL_SECTOR_FORMAT  SRDD10_PHYSICAL_SECTOR_FORMAT
 	uint8_t format;
 	uint8_t address_descriptor_index[4];
 	uint8_t alloc_length[4];
+#define	SRDD12_MAX_LENGTH		0xffffffff
 	uint8_t reserved;
 	uint8_t control;
 };
@@ -325,6 +330,8 @@ struct scsi_read_defect_data_hdr_10
 #define SRDDH10_PHYSICAL_SECTOR_FORMAT  0x05
 	u_int8_t format;
 	u_int8_t length[2];
+#define	SRDDH10_MAX_LENGTH	SRDD10_MAX_LENGTH -			     \
+				sizeof(struct scsi_read_defect_data_hdr_10) 
 };
 
 struct scsi_defect_desc_block
@@ -332,10 +339,18 @@ struct scsi_defect_desc_block
 	u_int8_t address[4];
 };
 
+struct scsi_defect_desc_long_block
+{
+	u_int8_t address[8];
+};
+
 struct scsi_defect_desc_bytes_from_index
 {
 	u_int8_t cylinder[3];
 	u_int8_t head;
+#define	SDD_EXT_BFI_MADS		0x80000000
+#define	SDD_EXT_BFI_FLAG_MASK		0xf0000000
+#define	SDD_EXT_BFI_ENTIRE_TRACK	0x0fffffff
 	u_int8_t bytes_from_index[4];
 };
 
@@ -343,6 +358,9 @@ struct scsi_defect_desc_phys_sector
 {
 	u_int8_t cylinder[3];
 	u_int8_t head;
+#define	SDD_EXT_PHYS_MADS		0x80000000
+#define	SDD_EXT_PHYS_FLAG_MASK		0xf0000000
+#define	SDD_EXT_PHYS_ENTIRE_TRACK	0x0fffffff
 	u_int8_t sector[4];
 };
 
@@ -358,6 +376,8 @@ struct scsi_read_defect_data_hdr_12
 	u_int8_t format;
 	u_int8_t generation[2];
 	u_int8_t length[4];
+#define	SRDDH12_MAX_LENGTH	SRDD12_MAX_LENGTH -			    \
+				sizeof(struct scsi_read_defect_data_hdr_12)
 };
 
 union	disk_pages /* this is the structure copied from osf */
@@ -536,9 +556,9 @@ struct scsi_da_rw_recovery_page {
 
 __BEGIN_DECLS
 /*
- * XXX This is only left out of the kernel build to silence warnings.  If,
- * for some reason this function is used in the kernel, the ifdefs should
- * be moved so it is included both in the kernel and userland.
+ * XXX These are only left out of the kernel build to silence warnings.  If,
+ * for some reason these functions are used in the kernel, the ifdefs should
+ * be moved so they are included both in the kernel and userland.
  */
 #ifndef _KERNEL
 void scsi_format_unit(struct ccb_scsiio *csio, u_int32_t retries,
@@ -547,6 +567,13 @@ void scsi_format_unit(struct ccb_scsiio 
 		      u_int8_t *data_ptr, u_int32_t dxfer_len,
 		      u_int8_t sense_len, u_int32_t timeout);
 
+void scsi_read_defects(struct ccb_scsiio *csio, uint32_t retries,
+		       void (*cbfcnp)(struct cam_periph *, union ccb *),
+		       uint8_t tag_action, uint8_t list_format,
+		       uint32_t addr_desc_index, uint8_t *data_ptr,
+		       uint32_t dxfer_len, int minimum_cmd_size, 
+		       uint8_t sense_len, uint32_t timeout);
+
 void scsi_sanitize(struct ccb_scsiio *csio, u_int32_t retries,
 		   void (*cbfcnp)(struct cam_periph *, union ccb *),
 		   u_int8_t tag_action, u_int8_t byte2, u_int16_t control,



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