summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/camcontrol/camcontrol.82
-rw-r--r--sbin/camcontrol/camcontrol.c73
-rw-r--r--sys/cam/scsi/scsi_da.h1
3 files changed, 69 insertions, 7 deletions
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index bf728c6..66a45eb 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -497,6 +497,8 @@ is specified,
.Nm
will print out the number of defects given in the READ DEFECT DATA header
returned from the drive.
+Some drives will report 0 defects if neither the primary or grown defect
+lists are requested.
.It Ic modepage
Allows the user to display and optionally edit a SCSI mode page.
The mode
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index 3e3456f..bae6543 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -1799,13 +1799,14 @@ readdefects(struct cam_device *device, int argc, char **argv,
union ccb *ccb = NULL;
struct scsi_read_defect_data_10 *rdd_cdb;
u_int8_t *defect_list = NULL;
- u_int32_t dlist_length = 65000;
+ u_int32_t max_dlist_length = SRDD10_MAX_LENGTH, dlist_length = 0;
u_int32_t returned_length = 0;
u_int32_t num_returned = 0;
u_int8_t returned_format;
unsigned int i;
int c, error = 0;
- int lists_specified = 0;
+ int lists_specified;
+ int get_length = 1;
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c){
@@ -1842,20 +1843,33 @@ readdefects(struct cam_device *device, int argc, char **argv,
ccb = cam_getccb(device);
/*
- * Hopefully 65000 bytes is enough to hold the defect list. If it
- * isn't, the disk is probably dead already. We'd have to go with
- * 12 byte command (i.e. alloc_length is 32 bits instead of 16)
- * to hold them all.
+ * Eventually we should probably support the 12 byte READ DEFECT
+ * DATA command. It supports a longer parameter list, which may be
+ * necessary on newer drives with lots of defects. According to
+ * the SBC-3 spec, drives are supposed to return an illegal request
+ * if they have more defect data than will fit in 64K.
*/
- defect_list = malloc(dlist_length);
+ defect_list = malloc(max_dlist_length);
if (defect_list == NULL) {
warnx("can't malloc memory for defect list");
error = 1;
goto defect_bailout;
}
+ /*
+ * We start off asking for just the header to determine how much
+ * defect data is available. Some Hitachi drives return an error
+ * if you ask for more data than the drive has. Once we know the
+ * length, we retry the command with the returned length.
+ */
+ dlist_length = sizeof(struct scsi_read_defect_data_hdr_10);
+
rdd_cdb =(struct scsi_read_defect_data_10 *)&ccb->csio.cdb_io.cdb_bytes;
+retry:
+
+ lists_specified = 0;
+
/*
* cam_getccb() zeros the CCB header only. So we need to zero the
* payload portion of the ccb.
@@ -1917,6 +1931,51 @@ readdefects(struct cam_device *device, int argc, char **argv,
returned_length = scsi_2btoul(((struct
scsi_read_defect_data_hdr_10 *)defect_list)->length);
+ if (get_length != 0) {
+ get_length = 0;
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
+ CAM_SCSI_STATUS_ERROR) {
+ struct scsi_sense_data *sense;
+ int error_code, sense_key, asc, ascq;
+
+ sense = &ccb->csio.sense_data;
+ scsi_extract_sense_len(sense, ccb->csio.sense_len -
+ ccb->csio.sense_resid, &error_code, &sense_key,
+ &asc, &ascq, /*show_errors*/ 1);
+
+ /*
+ * If the drive is reporting that it just doesn't
+ * support the defect list format, go ahead and use
+ * the length it reported. Otherwise, the length
+ * may not be valid, so use the maximum.
+ */
+ if ((sense_key == SSD_KEY_RECOVERED_ERROR)
+ && (asc == 0x1c) && (ascq == 0x00)
+ && (returned_length > 0)) {
+ dlist_length = returned_length +
+ sizeof(struct scsi_read_defect_data_hdr_10);
+ dlist_length = min(dlist_length,
+ SRDD10_MAX_LENGTH);
+ } else
+ dlist_length = max_dlist_length;
+ } else if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=
+ CAM_REQ_CMP){
+ error = 1;
+ warnx("Error reading defect header");
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ goto defect_bailout;
+ } else {
+ dlist_length = returned_length +
+ sizeof(struct scsi_read_defect_data_hdr_10);
+ dlist_length = min(dlist_length, SRDD10_MAX_LENGTH);
+ }
+
+ goto retry;
+ }
+
returned_format = ((struct scsi_read_defect_data_hdr_10 *)
defect_list)->format;
diff --git a/sys/cam/scsi/scsi_da.h b/sys/cam/scsi/scsi_da.h
index 7605b1a..5799238 100644
--- a/sys/cam/scsi/scsi_da.h
+++ b/sys/cam/scsi/scsi_da.h
@@ -111,6 +111,7 @@ struct scsi_read_defect_data_10
u_int8_t reserved[4];
u_int8_t alloc_length[2];
+#define SRDD10_MAX_LENGTH 0xffff
u_int8_t control;
};
OpenPOWER on IntegriCloud