diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index c74b5fe67ba..834ed083dba 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 cdb379dc6d2..b84ee5357a8 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; + 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 + - sizeof(struct scsi_read_defect_data_hdr_10)); + dlist = (struct scsi_defect_desc_phys_sector *) + (defect_list + hdr_size); - num_returned = returned_length / - sizeof(struct scsi_defect_desc_phys_sector); + for (i = 0; i < num_valid; i++) { + uint32_t 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"); - else - fprintf(stderr, "s:\n"); - - for (i = 0; i < num_returned; i++) { - fprintf(stdout, "%d:%d:%d\n", + 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)); - } - break; - } - 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"); + 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].bytes_from_index)); - } - break; + scsi_4btoul(dlist[i].sector), + 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 SRDD10_BYTES_FROM_INDEX_FORMAT: + case SRDD10_EXT_BFI_FORMAT: + { + struct scsi_defect_desc_bytes_from_index *dlist; - dlist = (struct scsi_defect_desc_block *)(defect_list + - sizeof(struct scsi_read_defect_data_hdr_10)); + dlist = (struct scsi_defect_desc_bytes_from_index *) + (defect_list + hdr_size); - num_returned = returned_length / - sizeof(struct scsi_defect_desc_block); + for (i = 0; i < num_valid; i++) { + uint32_t bfi; - 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"); + 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"); + fprintf(stdout, "0x%x:0x%x:0x%x%s", + scsi_3btoul(dlist[i].cylinder), + dlist[i].head, + scsi_4btoul(dlist[i].bytes_from_index), + mads ? " - " : "\n"); - for (i = 0; i < num_returned; i++) + mads = 0; + } + 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 + hdr_size); + + for (i = 0; i < num_valid; i++) { + if (hex_format == 0) fprintf(stdout, "%u\n", scsi_4btoul(dlist[i].address)); - break; + else + fprintf(stdout, "0x%x\n", + scsi_4btoul(dlist[i].address)); } - default: - fprintf(stderr, "Unknown defect format %d\n", - returned_format & SRDDH10_DLIST_FORMAT_MASK); - error = 1; - break; + + if (num_valid < num_returned) { + starting_offset += num_valid; + goto next_batch; + } + + 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(stdout, "0x%jx\n", + (uintmax_t)scsi_8btou64( + dlist[i].address)); + } + + if (num_valid < num_returned) { + starting_offset += num_valid; + goto next_batch; + } + 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" diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index d5e49372576..44925795cd8 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -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, @@ -3902,6 +3902,59 @@ scsi_format_unit(struct ccb_scsiio *csio, u_int32_t retries, 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) +{ + 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 *), diff --git a/sys/cam/scsi/scsi_da.h b/sys/cam/scsi/scsi_da.h index 9e5563dc71e..ad4d0dba1b3 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,