summaryrefslogtreecommitdiffstats
path: root/sbin/camcontrol
diff options
context:
space:
mode:
authorsjg <sjg@FreeBSD.org>2015-05-27 01:19:58 +0000
committersjg <sjg@FreeBSD.org>2015-05-27 01:19:58 +0000
commit65145fa4c81da358fcbc3b650156dab705dfa34e (patch)
tree55c065b6730aaac2afb6c29933ee6ec5fa4c4249 /sbin/camcontrol
parent60ff4eb0dff94a04d75d0d52a3957aaaf5f8c693 (diff)
parente6b664c390af88d4a87208bc042ce503da664c3b (diff)
downloadFreeBSD-src-65145fa4c81da358fcbc3b650156dab705dfa34e.zip
FreeBSD-src-65145fa4c81da358fcbc3b650156dab705dfa34e.tar.gz
Merge sync of head
Diffstat (limited to 'sbin/camcontrol')
-rw-r--r--sbin/camcontrol/Makefile3
-rw-r--r--sbin/camcontrol/camcontrol.8101
-rw-r--r--sbin/camcontrol/camcontrol.c692
3 files changed, 570 insertions, 226 deletions
diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile
index 4f11f19..f23ef52 100644
--- a/sbin/camcontrol/Makefile
+++ b/sbin/camcontrol/Makefile
@@ -11,8 +11,7 @@ CFLAGS+= -DMINIMALISTIC
.if ${MACHINE_CPUARCH} == "arm"
WARNS?= 3
.endif
-DPADD= ${LIBCAM} ${LIBSBUF} ${LIBUTIL}
-LDADD= -lcam -lsbuf -lutil
+LIBADD= cam sbuf util
MAN= camcontrol.8
.include <bsd.prog.mk>
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index 0fc7c1a..15e1862 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd August 31, 2014
+.Dd March 19, 2015
.Dt CAMCONTROL 8
.Os
.Sh NAME
@@ -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
@@ -235,6 +239,16 @@
.Op device id
.Op generic args
.Nm
+.Ic apm
+.Op device id
+.Op generic args
+.Op Fl l Ar level
+.Nm
+.Ic aam
+.Op device id
+.Op generic args
+.Op Fl l Ar level
+.Nm
.Ic fwdownload
.Op device id
.Op generic args
@@ -513,18 +527,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 +553,51 @@ 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
.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
@@ -1249,6 +1300,19 @@ Value 0 disables timer.
Put ATA device into SLEEP state.
Note that the only way get device out of
this state may be reset.
+.It Ic apm
+It optional parameter
+.Pq Fl l
+specified, enables and sets advanced power management level, where
+1 -- minimum power, 127 -- maximum performance with standby,
+128 -- minimum power without standby, 254 -- maximum performance.
+If not specified -- APM is disabled.
+.It Ic aam
+It optional parameter
+.Pq Fl l
+specified, enables and sets automatic acoustic management level, where
+1 -- minimum noise, 254 -- maximum performance.
+If not specified -- AAM is disabled.
.It Ic security
Update or report security settings, using an ATA identify command (0xec).
By default,
@@ -1364,8 +1428,8 @@ Defaults to
Confirm yes to dangerous options such as
.Fl e
without prompting for confirmation.
-.Pp
.El
+.Pp
If the password specified for any action commands does not match the configured
password for the specified user the command will fail.
.Pp
@@ -1434,8 +1498,8 @@ additional unlock calls until after a power-on reset.
Confirm yes to dangerous options such as
.Fl e
without prompting for confirmation
-.Pp
.El
+.Pp
The password for all HPA commands is limited to 32 characters, longer passwords
will fail.
.It Ic fwdownload
@@ -1613,7 +1677,7 @@ For example:
.It FC
A Fibre Channel Transport ID consists of
.Dq fcp,
-followed by a 64-bit Fibre Channel World Wide Name.
+followed by a 64-bit Fibre Channel World Wide Name.
For example:
.Pp
.Dl fcp,0x1234567812345678
@@ -1735,11 +1799,11 @@ May also be specified as
.Dq exclusive_access .
.It wr_ex_ro
Write Exclusive Registrants Only mode.
-May also be specified as
+May also be specified as
.Dq write_exclusive_reg_only .
.It ex_ac_ro
Exclusive Access Registrants Only mode.
-May also be specified as
+May also be specified as
.Dq exclusive_access_reg_only .
.It wr_ex_ar
Write Exclusive All Registrants mode.
@@ -1747,7 +1811,7 @@ May also be specified as
.Dq write_exclusive_all_regs .
.It ex_ac_ar
Exclusive Access All Registrants mode.
-May also be specified as
+May also be specified as
.Dq exclusive_access_all_regs .
.El
.It Fl U
@@ -1924,14 +1988,13 @@ power-on or hardware reset!
.Pp
.Em DO NOT
use this on a device which has an active filesystem!
-.Pp
.Bd -literal -offset indent
camcontrol persist da0 -v -i read_keys
.Ed
.Pp
This will read any persistent reservation keys registered with da0, and
display any errors encountered when sending the PERSISTENT RESERVE IN
-.Tn SCSI
+.Tn SCSI
command.
.Bd -literal -offset indent
camcontrol persist da0 -v -o register -a -K 0x12345678
@@ -1976,9 +2039,9 @@ camcontrol persist da0 -v -o register_move -k 0x87654321 \e
.Ed
.Pp
This will move the registration from the current initiator, whose
-Registration Key is 0x87654321, to the Fibre Channel initiator with the
+Registration Key is 0x87654321, to the Fibre Channel initiator with the
Fiber Channel World Wide Node Name 0x1234567812345678.
-A new registration key, 0x12345678, will be registered for the initiator
+A new registration key, 0x12345678, will be registered for the initiator
with the Fibre Channel World Wide Node Name 0x1234567812345678, and the
current initiator will be unregistered from the target.
The reservation will be moved to relative target port 2 on the target
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index cdb379d..c0a1344 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -96,7 +96,9 @@ typedef enum {
CAM_CMD_SECURITY = 0x0000001d,
CAM_CMD_HPA = 0x0000001e,
CAM_CMD_SANITIZE = 0x0000001f,
- CAM_CMD_PERSIST = 0x00000020
+ CAM_CMD_PERSIST = 0x00000020,
+ CAM_CMD_APM = 0x00000021,
+ CAM_CMD_AAM = 0x00000022
} cam_cmdmask;
typedef enum {
@@ -167,7 +169,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:";
@@ -216,6 +218,8 @@ static struct camcontrol_opts option_table[] = {
{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"},
{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},
+ {"apm", CAM_CMD_APM, CAM_ARG_NONE, "l:"},
+ {"aam", CAM_CMD_AAM, CAM_ARG_NONE, "l:"},
{"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"},
{"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"},
{"hpa", CAM_CMD_HPA, CAM_ARG_NONE, "Pflp:qs:U:y"},
@@ -1406,7 +1410,7 @@ atacapprint(struct ata_params *parm)
parm->enabled.command2 & ATA_SUPPORT_APM ? "yes" : "no");
if (parm->support.command2 & ATA_SUPPORT_APM) {
printf(" %d/0x%02X\n",
- parm->apm_value, parm->apm_value);
+ parm->apm_value & 0xff, parm->apm_value & 0xff);
} else
printf("\n");
printf("automatic acoustic management %s %s",
@@ -3369,39 +3373,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 +3441,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 +3525,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 +3552,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 +3630,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 +3699,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 +3749,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 +3793,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:
@@ -7178,7 +7408,7 @@ getdevid(struct cam_devitem *item)
retry:
ccb->ccb_h.func_code = XPT_DEV_ADVINFO;
ccb->ccb_h.flags = CAM_DIR_IN;
- ccb->cdai.flags = 0;
+ ccb->cdai.flags = CDAI_FLAG_NONE;
ccb->cdai.buftype = CDAI_TYPE_SCSI_DEVID;
ccb->cdai.bufsiz = item->device_id_len;
if (item->device_id_len != 0)
@@ -7738,39 +7968,83 @@ atapm(struct cam_device *device, int argc, char **argv,
else
sc = 253;
- cam_fill_ataio(&ccb->ataio,
- retry_count,
- NULL,
- /*flags*/CAM_DIR_NONE,
- MSG_SIMPLE_Q_TAG,
- /*data_ptr*/NULL,
- /*dxfer_len*/0,
- timeout ? timeout : 30 * 1000);
- ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, sc);
-
- /* Disable freezing the device queue */
- ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+ retval = ata_do_28bit_cmd(device,
+ ccb,
+ /*retries*/retry_count,
+ /*flags*/CAM_DIR_NONE,
+ /*protocol*/AP_PROTO_NON_DATA,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/cmd,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/sc,
+ /*data_ptr*/NULL,
+ /*dxfer_len*/0,
+ /*timeout*/timeout ? timeout : 30 * 1000,
+ /*quiet*/1);
- if (arglist & CAM_ARG_ERR_RECOVER)
- ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+ cam_freeccb(ccb);
+ return (retval);
+}
- if (cam_send_ccb(device, ccb) < 0) {
- warn("error sending command");
+static int
+ataaxm(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ int retval = 0;
+ int l = -1;
+ int c;
+ u_char cmd, sc;
- if (arglist & CAM_ARG_VERBOSE)
- cam_error_print(device, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
+ ccb = cam_getccb(device);
- retval = 1;
- goto bailout;
+ if (ccb == NULL) {
+ warnx("%s: error allocating ccb", __func__);
+ return (1);
}
- if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
- cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
- retval = 1;
- goto bailout;
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch (c) {
+ case 'l':
+ l = atoi(optarg);
+ break;
+ default:
+ break;
+ }
}
-bailout:
+ sc = 0;
+ if (strcmp(argv[1], "apm") == 0) {
+ if (l == -1)
+ cmd = 0x85;
+ else {
+ cmd = 0x05;
+ sc = l;
+ }
+ } else /* aam */ {
+ if (l == -1)
+ cmd = 0xC2;
+ else {
+ cmd = 0x42;
+ sc = l;
+ }
+ }
+
+ retval = ata_do_28bit_cmd(device,
+ ccb,
+ /*retries*/retry_count,
+ /*flags*/CAM_DIR_NONE,
+ /*protocol*/AP_PROTO_NON_DATA,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/ATA_SETFEATURES,
+ /*features*/cmd,
+ /*lba*/0,
+ /*sector_count*/sc,
+ /*data_ptr*/NULL,
+ /*dxfer_len*/0,
+ /*timeout*/timeout ? timeout : 30 * 1000,
+ /*quiet*/1);
+
cam_freeccb(ccb);
return (retval);
}
@@ -7801,6 +8075,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"
@@ -7831,6 +8106,8 @@ usage(int printlong)
" camcontrol idle [dev_id][generic args][-t time]\n"
" camcontrol standby [dev_id][generic args][-t time]\n"
" camcontrol sleep [dev_id][generic args]\n"
+" camcontrol apm [dev_id][generic args][-l level]\n"
+" camcontrol aam [dev_id][generic args][-l level]\n"
" camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-y][-s]\n"
" camcontrol security [dev_id][generic args]\n"
" <-d pwd | -e pwd | -f | -h pwd | -k pwd>\n"
@@ -8351,6 +8628,11 @@ main(int argc, char **argv)
error = atapm(cam_dev, argc, argv,
combinedopt, retry_count, timeout);
break;
+ case CAM_CMD_APM:
+ case CAM_CMD_AAM:
+ error = ataaxm(cam_dev, argc, argv,
+ combinedopt, retry_count, timeout);
+ break;
case CAM_CMD_SECURITY:
error = atasecurity(cam_dev, retry_count, timeout,
argc, argv, combinedopt);
OpenPOWER on IntegriCloud