Make camcontrol modepage to use 10 byte commands.

While old devices may not support 10 byte MODE SENSE/MODE SELECT commands,
new ones may not be able to report all mode pages with 6 byte commands.

This patch makes camcontrol by default start with 10 byte commands and
fall back to 6 byte on ILLEGAL REQUEST error, or 6 byte can be forced.

MFC after:	2 weeks
Sponsored by:	iXsystems, Inc.
This commit is contained in:
mav 2019-07-30 20:58:56 +00:00
parent c71b8899d3
commit 166d34c6ff
4 changed files with 185 additions and 121 deletions

View File

@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 25, 2019
.Dd July 30, 2019
.Dt CAMCONTROL 8
.Os
.Sh NAME
@ -122,6 +122,7 @@
.Ic modepage
.Op device id
.Op generic args
.Op Fl 6
.Aq Fl m Ar page[,subpage] | Fl l
.Op Fl P Ar pgctl
.Op Fl b | Fl e
@ -723,6 +724,13 @@ The
.Ic modepage
command takes several arguments:
.Bl -tag -width 12n
.It Fl 6
Use 6 byte MODE commands instead of default 10 byte.
Old devices may not support 10 byte MODE commands, while new devices may
not be able to report all mode pages with 6 byte commands.
If not specified,
.Nm
starts with 10 byte commands and falls back to 6 byte on error.
.It Fl d
Disable block descriptors for mode sense.
.It Fl b

View File

@ -221,7 +221,7 @@ static struct camcontrol_opts option_table[] = {
{"devlist", CAM_CMD_DEVTREE, CAM_ARG_NONE, "-b"},
{"devtype", CAM_CMD_DEVTYPE, CAM_ARG_NONE, ""},
{"periphlist", CAM_CMD_DEVLIST, CAM_ARG_NONE, NULL},
{"modepage", CAM_CMD_MODE_PAGE, CAM_ARG_NONE, "bdelm:P:"},
{"modepage", CAM_CMD_MODE_PAGE, CAM_ARG_NONE, "6bdelm:P:"},
{"tags", CAM_CMD_TAG, CAM_ARG_NONE, "N:q"},
{"negotiate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
{"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
@ -4586,18 +4586,25 @@ reassignblocks(struct cam_device *device, u_int32_t *blocks, int num_blocks)
#endif
void
mode_sense(struct cam_device *device, int dbd, int pc, int page, int subpage,
int task_attr, int retry_count, int timeout, u_int8_t *data,
int datalen)
mode_sense(struct cam_device *device, int *cdb_len, int dbd, int pc, int page,
int subpage, int task_attr, int retry_count, int timeout, u_int8_t *data,
int datalen)
{
union ccb *ccb;
int retval;
int error_code, sense_key, asc, ascq;
ccb = cam_getccb(device);
if (ccb == NULL)
errx(1, "mode_sense: couldn't allocate CCB");
retry:
/*
* MODE SENSE(6) can't handle more then 255 bytes. If there are more,
* device must return error, so we should not get trucated data.
*/
if (*cdb_len == 6 && datalen > 255)
datalen = 255;
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
scsi_mode_sense_subpage(&ccb->csio,
@ -4610,36 +4617,47 @@ mode_sense(struct cam_device *device, int dbd, int pc, int page, int subpage,
/* subpage */ subpage,
/* param_buf */ data,
/* param_len */ datalen,
/* minimum_cmd_size */ 0,
/* minimum_cmd_size */ *cdb_len,
/* sense_len */ SSD_FULL_SIZE,
/* timeout */ timeout ? timeout : 5000);
/* Record what CDB size the above function really set. */
*cdb_len = ccb->csio.cdb_len;
if (arglist & CAM_ARG_ERR_RECOVER)
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
/* Disable freezing the device queue */
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
if (((retval = cam_send_ccb(device, ccb)) < 0)
|| ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
if (cam_send_ccb(device, ccb) < 0)
err(1, "error sending mode sense command");
/* In case of ILLEGEL REQUEST try to fall back to 6-byte command. */
if (*cdb_len != 6 &&
((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID ||
(scsi_extract_sense_ccb(ccb, &error_code, &sense_key, &asc, &ascq)
&& sense_key == SSD_KEY_ILLEGAL_REQUEST))) {
*cdb_len = 6;
goto retry;
}
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
if (arglist & CAM_ARG_VERBOSE) {
cam_error_print(device, ccb, CAM_ESF_ALL,
CAM_EPF_ALL, stderr);
}
cam_freeccb(ccb);
cam_close_device(device);
if (retval < 0)
err(1, "error sending mode sense command");
else
errx(1, "error sending mode sense command");
errx(1, "mode sense command returned error");
}
cam_freeccb(ccb);
}
void
mode_select(struct cam_device *device, int save_pages, int task_attr,
int retry_count, int timeout, u_int8_t *data, int datalen)
mode_select(struct cam_device *device, int cdb_len, int save_pages,
int task_attr, int retry_count, int timeout, u_int8_t *data, int datalen)
{
union ccb *ccb;
int retval;
@ -4651,7 +4669,7 @@ mode_select(struct cam_device *device, int save_pages, int task_attr,
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
scsi_mode_select(&ccb->csio,
scsi_mode_select_len(&ccb->csio,
/* retries */ retry_count,
/* cbfcnp */ NULL,
/* tag_action */ task_attr,
@ -4659,6 +4677,7 @@ mode_select(struct cam_device *device, int save_pages, int task_attr,
/* save_pages */ save_pages,
/* param_buf */ data,
/* param_len */ datalen,
/* minimum_cmd_size */ cdb_len,
/* sense_len */ SSD_FULL_SIZE,
/* timeout */ timeout ? timeout : 5000);
@ -4693,10 +4712,13 @@ modepage(struct cam_device *device, int argc, char **argv, char *combinedopt,
{
char *str_subpage;
int c, page = -1, subpage = -1, pc = 0;
int binary = 0, dbd = 0, edit = 0, list = 0;
int binary = 0, cdb_len = 10, dbd = 0, edit = 0, list = 0;
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c) {
case '6':
cdb_len = 6;
break;
case 'b':
binary = 1;
break;
@ -4736,11 +4758,11 @@ modepage(struct cam_device *device, int argc, char **argv, char *combinedopt,
errx(1, "you must specify a mode page!");
if (list != 0) {
mode_list(device, dbd, pc, list > 1, task_attr, retry_count,
timeout);
mode_list(device, cdb_len, dbd, pc, list > 1, task_attr,
retry_count, timeout);
} else {
mode_edit(device, dbd, pc, page, subpage, edit, binary,
task_attr, retry_count, timeout);
mode_edit(device, cdb_len, dbd, pc, page, subpage, edit,
binary, task_attr, retry_count, timeout);
}
}

View File

@ -88,16 +88,17 @@ int epc(struct cam_device *device, int argc, char **argv, char *combinedopt,
int timestamp(struct cam_device *device, int argc, char **argv,
char *combinedopt, int task_attr, int retry_count, int timeout,
int verbosemode);
void mode_sense(struct cam_device *device, int dbd, int pc, int page,
int subpage, int task_attr, int retry_count, int timeout,
uint8_t *data, int datalen);
void mode_select(struct cam_device *device, int save_pages, int task_attr,
int retry_count, int timeout, u_int8_t *data, int datalen);
void mode_edit(struct cam_device *device, int dbd, int pc, int page,
int subpage, int edit, int binary, int task_attr,
void mode_sense(struct cam_device *device, int *cdb_len, int dbd, int pc,
int page, int subpage, int task_attr, int retry_count,
int timeout, uint8_t *data, int datalen);
void mode_select(struct cam_device *device, int cdb_len, int save_pages,
int task_attr, int retry_count, int timeout, u_int8_t *data,
int datalen);
void mode_edit(struct cam_device *device, int cdb_len, int dbd, int pc,
int page, int subpage, int edit, int binary, int task_attr,
int retry_count, int timeout);
void mode_list(struct cam_device *device, int dbd, int pc, int subpages,
int task_attr, int retry_count, int timeout);
void mode_list(struct cam_device *device, int cdb_len, int dbd, int pc,
int subpages, int task_attr, int retry_count, int timeout);
int scsidoinquiry(struct cam_device *device, int argc, char **argv,
char *combinedopt, int task_attr, int retry_count,
int timeout);

View File

@ -60,15 +60,9 @@ __FBSDID("$FreeBSD$");
#define PAGENAME_START '"' /* Page name delimiter. */
#define PAGENAME_END '"' /* Page name delimiter. */
#define PAGEENTRY_END ';' /* Page entry terminator (optional). */
#define MAX_COMMAND_SIZE 255 /* Mode/Log sense data buffer size. */
#define MAX_DATA_SIZE 4096 /* Mode/Log sense data buffer size. */
#define PAGE_CTRL_SHIFT 6 /* Bit offset to page control field. */
/* Macros for working with mode pages. */
#define MODE_PAGE_HEADER(mh) \
(struct scsi_mode_page_header *)find_mode_page_6(mh)
struct editentry {
STAILQ_ENTRY(editentry) link;
char *name;
@ -106,13 +100,12 @@ static int editentry_save(void *hook, char *name);
static struct editentry *editentry_lookup(char *name);
static int editentry_set(char *name, char *newvalue,
int editonly);
static void editlist_populate(struct cam_device *device, int dbd,
int pc, int page, int subpage,
int task_attr, int retries,
int timeout);
static void editlist_save(struct cam_device *device, int dbd,
int pc, int page, int subpage,
int task_attr, int retries, int timeout);
static void editlist_populate(struct cam_device *device,
int cdb_len, int dbd, int pc, int page, int subpage,
int task_attr, int retries, int timeout);
static void editlist_save(struct cam_device *device, int cdb_len,
int dbd, int pc, int page, int subpage,
int task_attr, int retries, int timeout);
static void nameentry_create(int page, int subpage, char *name);
static struct pagename *nameentry_lookup(int page, int subpage);
static int load_format(const char *pagedb_path, int lpage,
@ -120,9 +113,9 @@ static int load_format(const char *pagedb_path, int lpage,
static int modepage_write(FILE *file, int editonly);
static int modepage_read(FILE *file);
static void modepage_edit(void);
static void modepage_dump(struct cam_device *device, int dbd,
int pc, int page, int subpage, int task_attr,
int retries, int timeout);
static void modepage_dump(struct cam_device *device, int cdb_len,
int dbd, int pc, int page, int subpage,
int task_attr, int retries, int timeout);
static void cleanup_editfile(void);
@ -552,12 +545,11 @@ load_format(const char *pagedb_path, int lpage, int lsubpage)
}
static void
editlist_populate(struct cam_device *device, int dbd, int pc, int page,
int subpage, int task_attr, int retries, int timeout)
editlist_populate(struct cam_device *device, int cdb_len, int dbd, int pc,
int page, int subpage, int task_attr, int retries, int timeout)
{
u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
u_int8_t data[MAX_DATA_SIZE];/* Buffer to hold sense data. */
u_int8_t *mode_pars; /* Pointer to modepage params. */
struct scsi_mode_header_6 *mh; /* Location of mode header. */
struct scsi_mode_page_header *mph;
struct scsi_mode_page_header_sp *mphsp;
size_t len;
@ -565,11 +557,18 @@ editlist_populate(struct cam_device *device, int dbd, int pc, int page,
STAILQ_INIT(&editlist);
/* Fetch changeable values; use to build initial editlist. */
mode_sense(device, dbd, 1, page, subpage, task_attr, retries, timeout,
data, sizeof(data));
mode_sense(device, &cdb_len, dbd, 1, page, subpage, task_attr, retries,
timeout, data, sizeof(data));
mh = (struct scsi_mode_header_6 *)data;
mph = MODE_PAGE_HEADER(mh);
if (cdb_len == 6) {
struct scsi_mode_header_6 *mh =
(struct scsi_mode_header_6 *)data;
mph = find_mode_page_6(mh);
} else {
struct scsi_mode_header_10 *mh =
(struct scsi_mode_header_10 *)data;
mph = find_mode_page_10(mh);
}
if ((mph->page_code & SMPH_SPF) == 0) {
mode_pars = (uint8_t *)(mph + 1);
len = mph->page_length;
@ -584,54 +583,30 @@ editlist_populate(struct cam_device *device, int dbd, int pc, int page,
buff_decode_visit(mode_pars, len, format, editentry_create, 0);
/* Fetch the current/saved values; use to set editentry values. */
mode_sense(device, dbd, pc, page, subpage, task_attr, retries, timeout,
data, sizeof(data));
mode_sense(device, &cdb_len, dbd, pc, page, subpage, task_attr,
retries, timeout, data, sizeof(data));
buff_decode_visit(mode_pars, len, format, editentry_update, 0);
}
static void
editlist_save(struct cam_device *device, int dbd, int pc, int page,
int subpage, int task_attr, int retries, int timeout)
editlist_save(struct cam_device *device, int cdb_len, int dbd, int pc,
int page, int subpage, int task_attr, int retries, int timeout)
{
u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
u_int8_t data[MAX_DATA_SIZE];/* Buffer to hold sense data. */
u_int8_t *mode_pars; /* Pointer to modepage params. */
struct scsi_mode_header_6 *mh; /* Location of mode header. */
struct scsi_mode_page_header *mph;
struct scsi_mode_page_header_sp *mphsp;
size_t len, hlen;
size_t len, hlen, mphlen;
/* Make sure that something changed before continuing. */
if (! editlist_changed)
return;
/* Preload the CDB buffer with the current mode page data. */
mode_sense(device, dbd, pc, page, subpage, task_attr, retries, timeout,
data, sizeof(data));
mode_sense(device, &cdb_len, dbd, pc, page, subpage, task_attr,
retries, timeout, data, sizeof(data));
/* Initial headers & offsets. */
mh = (struct scsi_mode_header_6 *)data;
mph = MODE_PAGE_HEADER(mh);
if ((mph->page_code & SMPH_SPF) == 0) {
hlen = sizeof(*mph);
mode_pars = (uint8_t *)(mph + 1);
len = mph->page_length;
} else {
mphsp = (struct scsi_mode_page_header_sp *)mph;
hlen = sizeof(*mphsp);
mode_pars = (uint8_t *)(mphsp + 1);
len = scsi_2btoul(mphsp->page_length);
}
len = MIN(len, sizeof(data) - (mode_pars - data));
/* Encode the value data to be passed back to the device. */
buff_encode_visit(mode_pars, len, format, editentry_save, 0);
/* Eliminate block descriptors. */
bcopy(mph, mh + 1, hlen + len);
/* Recalculate headers & offsets. */
mh->data_length = 0; /* Reserved for MODE SELECT command. */
mh->blk_desc_len = 0; /* No block descriptors. */
/*
* Tape drives include write protect (WP), Buffered Mode and Speed
* settings in the device-specific parameter. Clearing this
@ -644,9 +619,52 @@ editlist_save(struct cam_device *device, int dbd, int pc, int page,
* clear this for disks (and other non-tape devices) to avoid
* potential errors from the target device.
*/
if (device->pd_type != T_SEQUENTIAL)
mh->dev_spec = 0;
mph = MODE_PAGE_HEADER(mh);
if (cdb_len == 6) {
struct scsi_mode_header_6 *mh =
(struct scsi_mode_header_6 *)data;
hlen = sizeof(*mh);
/* Eliminate block descriptors. */
if (mh->blk_desc_len > 0) {
bcopy(find_mode_page_6(mh), mh + 1,
mh->data_length + 1 - hlen -
mh->blk_desc_len);
mh->blk_desc_len = 0;
}
mh->data_length = 0; /* Reserved for MODE SELECT command. */
if (device->pd_type != T_SEQUENTIAL)
mh->dev_spec = 0; /* See comment above */
mph = find_mode_page_6(mh);
} else {
struct scsi_mode_header_10 *mh =
(struct scsi_mode_header_10 *)data;
hlen = sizeof(*mh);
/* Eliminate block descriptors. */
if (scsi_2btoul(mh->blk_desc_len) > 0) {
bcopy(find_mode_page_10(mh), mh + 1,
scsi_2btoul(mh->data_length) + 1 - hlen -
scsi_2btoul(mh->blk_desc_len));
scsi_ulto2b(0, mh->blk_desc_len);
}
scsi_ulto2b(0, mh->data_length); /* Reserved for MODE SELECT. */
if (device->pd_type != T_SEQUENTIAL)
mh->dev_spec = 0; /* See comment above */
mph = find_mode_page_10(mh);
}
if ((mph->page_code & SMPH_SPF) == 0) {
mphlen = sizeof(*mph);
mode_pars = (uint8_t *)(mph + 1);
len = mph->page_length;
} else {
mphsp = (struct scsi_mode_page_header_sp *)mph;
mphlen = sizeof(*mphsp);
mode_pars = (uint8_t *)(mphsp + 1);
len = scsi_2btoul(mphsp->page_length);
}
len = MIN(len, sizeof(data) - (mode_pars - data));
/* Encode the value data to be passed back to the device. */
buff_encode_visit(mode_pars, len, format, editentry_save, 0);
mph->page_code &= ~SMPH_PS; /* Reserved for MODE SELECT command. */
/*
@ -654,9 +672,8 @@ editlist_save(struct cam_device *device, int dbd, int pc, int page,
* page 3 (saved values) then request the changes be permanently
* recorded.
*/
mode_select(device, (pc << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED),
task_attr, retries, timeout, (u_int8_t *)mh,
sizeof(*mh) + hlen + len);
mode_select(device, cdb_len, (pc << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED),
task_attr, retries, timeout, data, hlen + mphlen + len);
}
static int
@ -825,21 +842,27 @@ modepage_edit(void)
}
static void
modepage_dump(struct cam_device *device, int dbd, int pc, int page, int subpage,
int task_attr, int retries, int timeout)
modepage_dump(struct cam_device *device, int cdb_len, int dbd, int pc,
int page, int subpage, int task_attr, int retries, int timeout)
{
u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
u_int8_t data[MAX_DATA_SIZE];/* Buffer to hold sense data. */
u_int8_t *mode_pars; /* Pointer to modepage params. */
struct scsi_mode_header_6 *mh; /* Location of mode header. */
struct scsi_mode_page_header *mph;
struct scsi_mode_page_header_sp *mphsp;
size_t indx, len;
mode_sense(device, dbd, pc, page, subpage, task_attr, retries, timeout,
data, sizeof(data));
mode_sense(device, &cdb_len, dbd, pc, page, subpage, task_attr,
retries, timeout, data, sizeof(data));
mh = (struct scsi_mode_header_6 *)data;
mph = MODE_PAGE_HEADER(mh);
if (cdb_len == 6) {
struct scsi_mode_header_6 *mh =
(struct scsi_mode_header_6 *)data;
mph = find_mode_page_6(mh);
} else {
struct scsi_mode_header_10 *mh =
(struct scsi_mode_header_10 *)data;
mph = find_mode_page_10(mh);
}
if ((mph->page_code & SMPH_SPF) == 0) {
mode_pars = (uint8_t *)(mph + 1);
len = mph->page_length;
@ -869,8 +892,9 @@ cleanup_editfile(void)
}
void
mode_edit(struct cam_device *device, int dbd, int pc, int page, int subpage,
int edit, int binary, int task_attr, int retry_count, int timeout)
mode_edit(struct cam_device *device, int cdb_len, int dbd, int pc, int page,
int subpage, int edit, int binary, int task_attr, int retry_count,
int timeout)
{
const char *pagedb_path; /* Path to modepage database. */
@ -901,8 +925,8 @@ mode_edit(struct cam_device *device, int dbd, int pc, int page, int subpage,
exit(EX_OSFILE);
}
editlist_populate(device, dbd, pc, page, subpage, task_attr,
retry_count, timeout);
editlist_populate(device, cdb_len, dbd, pc, page, subpage,
task_attr, retry_count, timeout);
}
if (edit) {
@ -911,12 +935,12 @@ mode_edit(struct cam_device *device, int dbd, int pc, int page, int subpage,
errx(EX_USAGE, "it only makes sense to edit page 0 "
"(current) or page 3 (saved values)");
modepage_edit();
editlist_save(device, dbd, pc, page, subpage, task_attr,
retry_count, timeout);
editlist_save(device, cdb_len, dbd, pc, page, subpage,
task_attr, retry_count, timeout);
} else if (binary || STAILQ_EMPTY(&editlist)) {
/* Display without formatting information. */
modepage_dump(device, dbd, pc, page, subpage, task_attr,
retry_count, timeout);
modepage_dump(device, cdb_len, dbd, pc, page, subpage,
task_attr, retry_count, timeout);
} else {
/* Display with format. */
modepage_write(stdout, 0);
@ -924,16 +948,15 @@ mode_edit(struct cam_device *device, int dbd, int pc, int page, int subpage,
}
void
mode_list(struct cam_device *device, int dbd, int pc, int subpages,
mode_list(struct cam_device *device, int cdb_len, int dbd, int pc, int subpages,
int task_attr, int retry_count, int timeout)
{
u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
struct scsi_mode_header_6 *mh; /* Location of mode header. */
u_int8_t data[MAX_DATA_SIZE];/* Buffer to hold sense data. */
struct scsi_mode_page_header *mph;
struct scsi_mode_page_header_sp *mphsp;
struct pagename *nameentry;
const char *pagedb_path;
int len, page, subpage;
int len, off, page, subpage;
if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
pagedb_path = DEFAULT_SCSI_MODE_DB;
@ -944,26 +967,36 @@ mode_list(struct cam_device *device, int dbd, int pc, int subpages,
}
/* Build the list of all mode pages by querying the "all pages" page. */
mode_sense(device, dbd, pc, SMS_ALL_PAGES_PAGE,
mode_sense(device, &cdb_len, dbd, pc, SMS_ALL_PAGES_PAGE,
subpages ? SMS_SUBPAGE_ALL : 0,
task_attr, retry_count, timeout, data, sizeof(data));
mh = (struct scsi_mode_header_6 *)data;
len = sizeof(*mh) + mh->blk_desc_len; /* Skip block descriptors. */
/* Skip block descriptors. */
if (cdb_len == 6) {
struct scsi_mode_header_6 *mh =
(struct scsi_mode_header_6 *)data;
len = mh->data_length;
off = sizeof(*mh) + mh->blk_desc_len;
} else {
struct scsi_mode_header_10 *mh =
(struct scsi_mode_header_10 *)data;
len = scsi_2btoul(mh->data_length);
off = sizeof(*mh) + scsi_2btoul(mh->blk_desc_len);
}
/* Iterate through the pages in the reply. */
while (len < mh->data_length) {
while (off < len) {
/* Locate the next mode page header. */
mph = (struct scsi_mode_page_header *)((intptr_t)mh + len);
mph = (struct scsi_mode_page_header *)(data + off);
if ((mph->page_code & SMPH_SPF) == 0) {
page = mph->page_code & SMS_PAGE_CODE;
subpage = 0;
len += sizeof(*mph) + mph->page_length;
off += sizeof(*mph) + mph->page_length;
} else {
mphsp = (struct scsi_mode_page_header_sp *)mph;
page = mphsp->page_code & SMS_PAGE_CODE;
subpage = mphsp->subpage;
len += sizeof(*mphsp) + scsi_2btoul(mphsp->page_length);
off += sizeof(*mphsp) + scsi_2btoul(mphsp->page_length);
}
nameentry = nameentry_lookup(page, subpage);