camcontrol: Add a sense subcommand

As the name suggests, this sends a SCSI REQUEST SENSE to a device,
and prints out decoded sense information.  It can also print out a
hexdump of the sense data.

sbin/camcontrol/camcontrol.c:
	Add the new sense subcommand.

sbin/camcontrol/camcontrol.8:
	Document camcontrol sense.

Sponsored by:	Spectra Logic
Reviewed by:	mav
Differential Revision:  https://reviews.freebsd.org/D43225

(cherry picked from commit 40a492d38e)
This commit is contained in:
Kenneth D. Merry 2023-12-28 16:23:16 -05:00
parent 1b959537ed
commit 658a9820b0
2 changed files with 139 additions and 2 deletions

View File

@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd June 1, 2023
.Dd December 28, 2023
.Dt CAMCONTROL 8
.Os
.Sh NAME
@ -51,6 +51,12 @@
.Op device id
.Op generic args
.Nm
.Ic sense
.Op device id
.Op generic args
.Op Fl D
.Op Fl x
.Nm
.Ic inquiry
.Op device id
.Op generic args
@ -481,6 +487,15 @@ Send the SCSI test unit ready (0x00) command to the given device.
The
.Nm
utility will report whether the device is ready or not.
.It Ic sense
Send a SCSI REQUEST SENSE command (0x03) to a device.
The decoded sense (or hexdump) is printed to stdout.
.Bl -tag -width 4n
.It Fl D
Request descriptor sense instead of fixed sense.
.It Fl x
Do a hexdump of the returned sense data.
.El
.It Ic inquiry
Send a SCSI inquiry command (0x12) to a device.
By default,

View File

@ -108,7 +108,8 @@ typedef enum {
CAM_CMD_MMCSD_CMD = 0x00000029,
CAM_CMD_POWER_MODE = 0x0000002a,
CAM_CMD_DEVTYPE = 0x0000002b,
CAM_CMD_AMA = 0x0000002c,
CAM_CMD_AMA = 0x0000002c,
CAM_CMD_REQSENSE = 0x0000002d,
} cam_cmdmask;
typedef enum {
@ -226,6 +227,7 @@ static struct camcontrol_opts option_table[] = {
{"zone", CAM_CMD_ZONE, CAM_ARG_NONE, "ac:l:No:P:"},
{"epc", CAM_CMD_EPC, CAM_ARG_NONE, "c:dDeHp:Pr:sS:T:"},
{"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"},
{"sense", CAM_CMD_REQSENSE, CAM_ARG_NONE, "Dx"},
{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
{"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@ -273,6 +275,9 @@ static int print_dev_mmcsd(struct device_match_result *dev_result,
#ifdef WITH_NVME
static int print_dev_nvme(struct device_match_result *dev_result, char *tmpstr);
#endif
static int requestsense(struct cam_device *device, int argc, char **argv,
char *combinedopt, int task_attr, int retry_count,
int timeout);
static int testunitready(struct cam_device *device, int task_attr,
int retry_count, int timeout, int quiet);
static int scsistart(struct cam_device *device, int startstop, int loadeject,
@ -834,6 +839,114 @@ print_dev_nvme(struct device_match_result *dev_result, char *tmpstr)
}
#endif
static int
requestsense(struct cam_device *device, int argc, char **argv,
char *combinedopt, int task_attr, int retry_count, int timeout)
{
int c;
int descriptor_sense = 0;
int do_hexdump = 0;
struct scsi_sense_data sense;
union ccb *ccb = NULL;
int error = 0;
size_t returned_bytes;
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
case 'D':
descriptor_sense = 1;
break;
case 'x':
do_hexdump = 1;
break;
default:
break;
}
}
ccb = cam_getccb(device);
if (ccb == NULL) {
warnx("couldn't allocate CCB");
return (1);
}
/* cam_getccb cleans up the header, caller has to zero the payload */
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
bzero(&sense, sizeof(sense));
scsi_request_sense(&ccb->csio,
/*retries*/ retry_count,
/*cbfcnp*/ NULL,
/*data_ptr*/ (void *)&sense,
/*dxfer_len*/ sizeof(sense),
/*tag_action*/ task_attr,
/*sense_len*/ SSD_FULL_SIZE,
/*timeout*/ timeout ? timeout : 60000);
if (descriptor_sense != 0) {
struct scsi_request_sense *cdb;
cdb = (struct scsi_request_sense *)&ccb->csio.cdb_io.cdb_bytes;
cdb->byte2 |= SRS_DESC;
}
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
if (arglist & CAM_ARG_ERR_RECOVER)
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
if (cam_send_ccb(device, ccb) < 0) {
warn("error sending REQUEST SENSE command");
cam_freeccb(ccb);
error = 1;
goto bailout;
}
/*
* REQUEST SENSE is not generally supposed to fail. But there can
* be transport or other errors that might cause it to fail. It
* may also fail if the user asks for descriptor sense and the
* device doesn't support it. So we check the CCB status here to see.
*/
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
warnx("REQUEST SENSE failed");
cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
error = 1;
goto bailout;
}
returned_bytes = ccb->csio.dxfer_len - ccb->csio.resid;
if (do_hexdump != 0) {
hexdump(&sense, returned_bytes, NULL, 0);
} else {
char path_str[80];
struct sbuf *sb;
cam_path_string(device, path_str, sizeof(path_str));
sb = sbuf_new_auto();
if (sb == NULL) {
warnx("%s: cannot allocate sbuf", __func__);
error = 1;
goto bailout;
}
scsi_sense_only_sbuf(&sense, returned_bytes, sb, path_str,
&device->inq_data, scsiio_cdb_ptr(&ccb->csio),
ccb->csio.cdb_len);
sbuf_finish(sb);
printf("%s", sbuf_data(sb));
sbuf_delete(sb);
}
bailout:
if (ccb != NULL)
cam_freeccb(ccb);
return (error);
}
static int
testunitready(struct cam_device *device, int task_attr, int retry_count,
int timeout, int quiet)
@ -9920,6 +10033,7 @@ usage(int printlong)
" camcontrol devlist [-b] [-v]\n"
" camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n"
" camcontrol tur [dev_id][generic args]\n"
" camcontrol sense [dev_id][generic args][-D][-x]\n"
" camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n"
" camcontrol identify [dev_id][generic args] [-v]\n"
" camcontrol reportluns [dev_id][generic args] [-c] [-l] [-r report]\n"
@ -10007,6 +10121,7 @@ usage(int printlong)
"Specify one of the following options:\n"
"devlist list all CAM devices\n"
"periphlist list all CAM peripheral drivers attached to a device\n"
"sense send a request sense command to the named device\n"
"tur send a test unit ready to the named device\n"
"inquiry send a SCSI inquiry command to the named device\n"
"identify send a ATA identify command to the named device\n"
@ -10070,6 +10185,9 @@ usage(int printlong)
"-f format specify defect list format (block, bfi or phys)\n"
"-G get the grown defect list\n"
"-P get the permanent defect list\n"
"sense arguments:\n"
"-D request descriptor sense data\n"
"-x do a hexdump of the sense data\n"
"inquiry arguments:\n"
"-D get the standard inquiry data\n"
"-S get the serial number\n"
@ -10533,6 +10651,10 @@ main(int argc, char **argv)
case CAM_CMD_DEVTYPE:
error = getdevtype(cam_dev);
break;
case CAM_CMD_REQSENSE:
error = requestsense(cam_dev, argc, argv, combinedopt,
task_attr, retry_count, timeout);
break;
case CAM_CMD_TUR:
error = testunitready(cam_dev, task_attr, retry_count,
timeout, 0);