git: 40a492d38ee1 - main - camcontrol: Add a sense subcommand

From: Kenneth D. Merry <ken_at_FreeBSD.org>
Date: Thu, 28 Dec 2023 21:24:09 UTC
The branch main has been updated by ken:

URL: https://cgit.FreeBSD.org/src/commit/?id=40a492d38ee10ecf9d9a099c5cdecc072e24d2d1

commit 40a492d38ee10ecf9d9a099c5cdecc072e24d2d1
Author:     Kenneth D. Merry <ken@FreeBSD.org>
AuthorDate: 2023-12-28 21:23:16 +0000
Commit:     Kenneth D. Merry <ken@FreeBSD.org>
CommitDate: 2023-12-28 21:23:16 +0000

    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
    MFC after:      3 days
    Differential Revision:  https://reviews.freebsd.org/D43225
---
 sbin/camcontrol/camcontrol.8 |  17 +++++-
 sbin/camcontrol/camcontrol.c | 122 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index 2113c4fb2aab..0151376ff39f 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -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
@@ -488,6 +494,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,
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index 9417943afcb3..555a67001443 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -111,6 +111,7 @@ typedef enum {
 	CAM_CMD_DEVTYPE,
 	CAM_CMD_AMA,
 	CAM_CMD_DEPOP,
+	CAM_CMD_REQSENSE
 } cam_cmd;
 
 typedef enum {
@@ -233,6 +234,7 @@ static struct camcontrol_opts option_table[] = {
 	{"epc", CAM_CMD_EPC, CAM_ARG_NONE, "c:dDeHp:Pr:sS:T:"},
 	{"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"},
 	{"depop", CAM_CMD_DEPOP, CAM_ARG_NONE, "ac:de:ls"},
+	{"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},
@@ -279,6 +281,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,
@@ -840,6 +845,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)
@@ -9869,6 +9982,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"
@@ -9957,6 +10071,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"
@@ -10021,6 +10136,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"
@@ -10491,6 +10609,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);