From b09a0cbe0ab3cc829ef468fc46df597c25b7fed3 Mon Sep 17 00:00:00 2001 From: ken Date: Thu, 8 Jan 2015 16:58:40 +0000 Subject: 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 --- sbin/camcontrol/camcontrol.8 | 58 ++++- sbin/camcontrol/camcontrol.c | 579 ++++++++++++++++++++++++++++++------------- 2 files changed, 453 insertions(+), 184 deletions(-) (limited to 'sbin/camcontrol') diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index c74b5fe..834ed08 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 \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" -- cgit v1.1