summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/camcontrol/camcontrol.858
-rw-r--r--sbin/camcontrol/camcontrol.c579
-rw-r--r--sys/cam/scsi/scsi_da.c59
-rw-r--r--sys/cam/scsi/scsi_da.h39
4 files changed, 542 insertions, 193 deletions
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index bd888d3..6ea508e 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -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 code to report that it does not
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
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index cdb379d..b84ee53 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -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, target_id_t target, lun_id_t lun, int scan)
}
#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, int argc, char **argv,
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;
}
- 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));
+ if (num_valid < num_returned) {
+ starting_offset += num_valid;
+ goto next_batch;
+ }
+ break;
+ }
+ case SRDD10_BYTES_FROM_INDEX_FORMAT:
+ case SRDD10_EXT_BFI_FORMAT:
+ {
+ struct scsi_defect_desc_bytes_from_index *dlist;
- num_returned = returned_length /
- sizeof(struct scsi_defect_desc_bytes_from_index);
+ dlist = (struct scsi_defect_desc_bytes_from_index *)
+ (defect_list + hdr_size);
- fprintf(stderr, "Got %d defect", num_returned);
+ for (i = 0; i < num_valid; i++) {
+ uint32_t bfi;
- if ((lists_specified == 0) || (num_returned == 0)) {
- fprintf(stderr, "s.\n");
- break;
- } else if (num_returned == 1)
- fprintf(stderr, ":\n");
+ 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"
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c
index 9bc83b1..c59694b 100644
--- a/sys/cam/scsi/scsi_da.c
+++ b/sys/cam/scsi/scsi_da.c
@@ -3878,9 +3878,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,
@@ -3909,6 +3909,59 @@ scsi_format_unit(struct ccb_scsiio *csio, u_int32_t retries,
}
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,
diff --git a/sys/cam/scsi/scsi_da.h b/sys/cam/scsi/scsi_da.h
index 9e5563d..ad4d0db 100644
--- a/sys/cam/scsi/scsi_da.h
+++ b/sys/cam/scsi/scsi_da.h
@@ -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 *csio, u_int32_t retries,
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,
OpenPOWER on IntegriCloud