Add MODESELECT6 support

Add support for MODESELECT6 and add marshalling functions for the
mode pages we support so far.
This commit is contained in:
Ronnie Sahlberg
2013-07-06 16:11:17 -07:00
parent b6e5af558d
commit 7895fb700c
7 changed files with 336 additions and 0 deletions

View File

@@ -635,6 +635,8 @@ EXTERN int
iscsi_set_isid_reserved(struct iscsi_context *iscsi);
struct scsi_mode_page;
/*
@@ -818,6 +820,10 @@ iscsi_writesame16_task(struct iscsi_context *iscsi, int lun, uint64_t lba,
int anchor, int unmap, int wrprotect, int group,
iscsi_command_cb cb, void *private_data);
EXTERN struct scsi_task *
iscsi_modeselect6_task(struct iscsi_context *iscsi, int lun,
int pf, int sp, struct scsi_mode_page *mp,
iscsi_command_cb cb, void *private_data);
EXTERN struct scsi_task *
iscsi_modesense6_task(struct iscsi_context *iscsi, int lun, int dbd,
int pc, int page_code, int sub_page_code,
unsigned char alloc_len, iscsi_command_cb cb,
@@ -870,6 +876,10 @@ EXTERN struct scsi_task *
iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun,
struct scsi_task *task, struct iscsi_data *data);
EXTERN struct scsi_task *
iscsi_modeselect6_sync(struct iscsi_context *iscsi, int lun,
int pf, int sp, struct scsi_mode_page *mp);
EXTERN struct scsi_task *
iscsi_modesense6_sync(struct iscsi_context *iscsi, int lun, int dbd,
int pc, int page_code, int sub_page_code,

View File

@@ -33,6 +33,7 @@ enum scsi_opcode {
SCSI_OPCODE_TESTUNITREADY = 0x00,
SCSI_OPCODE_READ6 = 0x08,
SCSI_OPCODE_INQUIRY = 0x12,
SCSI_OPCODE_MODESELECT6 = 0x15,
SCSI_OPCODE_RESERVE6 = 0x16,
SCSI_OPCODE_RELEASE6 = 0x17,
SCSI_OPCODE_MODESENSE6 = 0x1a,
@@ -747,6 +748,11 @@ struct scsi_mode_sense {
struct scsi_mode_page *pages;
};
EXTERN struct scsi_mode_page *
scsi_modesense_get_page(struct scsi_mode_sense *ms,
enum scsi_modesense_page_code page_code,
int subpage_code);
EXTERN struct scsi_task *scsi_cdb_modesense6(int dbd,
enum scsi_modesense_page_control pc,
enum scsi_modesense_page_code page_code,
@@ -754,6 +760,12 @@ EXTERN struct scsi_task *scsi_cdb_modesense6(int dbd,
unsigned char alloc_len);
EXTERN struct scsi_task *scsi_cdb_modeselect6(int pf, int sp, int param_len);
EXTERN struct scsi_data *
scsi_modesense_dataout_marshall(struct scsi_task *task,
struct scsi_mode_page *mp,
int is_modeselect6);
struct scsi_readcapacity16 {

View File

@@ -1204,6 +1204,43 @@ iscsi_verify16_task(struct iscsi_context *iscsi, int lun, unsigned char *data,
return task;
}
struct scsi_task *
iscsi_modeselect6_task(struct iscsi_context *iscsi, int lun,
int pf, int sp, struct scsi_mode_page *mp,
iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
struct scsi_data *data;
struct iscsi_data d;
task = scsi_cdb_modeselect6(pf, sp, 255);
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"modeselect6 cdb.");
return NULL;
}
data = scsi_modesense_dataout_marshall(task, mp, 1);
if (data == NULL) {
iscsi_set_error(iscsi, "Error: Failed to marshall "
"modesense dataout buffer.");
scsi_free_scsi_task(task);
return NULL;
}
d.data = data->data;
d.size = data->size;
task->cdb[4] = data->size;
task->expxferlen = data->size;
if (iscsi_scsi_command_async(iscsi, lun, task, cb,
&d, private_data) != 0) {
scsi_free_scsi_task(task);
return NULL;
}
return task;
}
struct scsi_task *
iscsi_modesense6_task(struct iscsi_context *iscsi, int lun, int dbd, int pc,
int page_code, int sub_page_code,

View File

@@ -23,6 +23,8 @@ iscsi_login_async
iscsi_login_sync
iscsi_logout_async
iscsi_logout_sync
iscsi_modeselect6_sync
iscsi_modeselect6_task
iscsi_modesense6_sync
iscsi_modesense6_task
iscsi_nop_out_async
@@ -151,6 +153,7 @@ iscsi_writesame16_task
scsi_association_to_str
scsi_cdb_inquiry
scsi_cdb_get_lba_status
scsi_cdb_modeselect6
scsi_cdb_modesense6
scsi_cdb_persistent_reserve_in
scsi_cdb_persistent_reserve_out
@@ -196,6 +199,8 @@ scsi_devtype_to_str
scsi_free_scsi_task
scsi_get_task_private_ptr
scsi_inquiry_pagecode_to_str
scsi_modesense_dataout_marshall
scsi_modesense_get_page
scsi_protocol_identifier_to_str
scsi_reportluns_cdb
scsi_sense_ascq_str

View File

@@ -21,6 +21,8 @@ iscsi_login_async
iscsi_login_sync
iscsi_logout_async
iscsi_logout_sync
iscsi_modeselect6_sync
iscsi_modeselect6_task
iscsi_modesense6_sync
iscsi_modesense6_task
iscsi_nop_out_async
@@ -149,6 +151,7 @@ iscsi_writesame16_task
scsi_association_to_str
scsi_cdb_inquiry
scsi_cdb_get_lba_status
scsi_cdb_modeselect6
scsi_cdb_modesense6
scsi_cdb_persistent_reserve_in
scsi_cdb_persistent_reserve_out
@@ -194,6 +197,8 @@ scsi_devtype_to_str
scsi_free_scsi_task
scsi_get_task_private_ptr
scsi_inquiry_pagecode_to_str
scsi_modesense_dataout_marshall
scsi_modesense_get_page
scsi_protocol_identifier_to_str
scsi_reportluns_cdb
scsi_sense_ascq_str

View File

@@ -2177,6 +2177,58 @@ scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc,
return task;
}
/*
* MODESELECT6
*/
struct scsi_task *
scsi_cdb_modeselect6(int pf, int sp, int param_len)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
memset(task, 0, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_MODESELECT6;
if (pf) {
task->cdb[1] |= 0x10;
}
if (sp) {
task->cdb[1] |= 0x01;
}
task->cdb[4] = param_len;
task->cdb_size = 6;
if (param_len != 0) {
task->xfer_dir = SCSI_XFER_WRITE;
} else {
task->xfer_dir = SCSI_XFER_NONE;
}
task->expxferlen = param_len;
return task;
}
struct scsi_mode_page *
scsi_modesense_get_page(struct scsi_mode_sense *ms,
enum scsi_modesense_page_code page_code,
int subpage_code)
{
struct scsi_mode_page *mp;
for (mp = ms->pages; mp; mp = mp->next) {
if (mp->page_code == page_code
&& mp->subpage_code == subpage_code) {
return mp;
}
}
return NULL;
}
/*
* parse the data in blob and calculate the size of a full
* modesense6 datain structure
@@ -2377,6 +2429,201 @@ scsi_modesense_datain_unmarshall(struct scsi_task *task)
return ms;
}
static struct scsi_data *
scsi_modesense_marshall_caching(struct scsi_task *task,
struct scsi_mode_page *mp,
int hdr_size)
{
struct scsi_data *data;
data = scsi_malloc(task, sizeof(struct scsi_data));
data->size = 20 + hdr_size;
data->data = scsi_malloc(task, data->size);
if (mp->caching.ic) data->data[hdr_size + 2] |= 0x80;
if (mp->caching.abpf) data->data[hdr_size + 2] |= 0x40;
if (mp->caching.cap) data->data[hdr_size + 2] |= 0x20;
if (mp->caching.disc) data->data[hdr_size + 2] |= 0x10;
if (mp->caching.size) data->data[hdr_size + 2] |= 0x08;
if (mp->caching.wce) data->data[hdr_size + 2] |= 0x04;
if (mp->caching.mf) data->data[hdr_size + 2] |= 0x02;
if (mp->caching.rcd) data->data[hdr_size + 2] |= 0x01;
data->data[hdr_size + 3] |= (mp->caching.demand_read_retention_priority << 4) & 0xf0;
data->data[hdr_size + 3] |= mp->caching.write_retention_priority & 0x0f;
scsi_set_uint16(&data->data[hdr_size + 4], mp->caching.disable_prefetch_transfer_length);
scsi_set_uint16(&data->data[hdr_size + 6], mp->caching.minimum_prefetch);
scsi_set_uint16(&data->data[hdr_size + 8], mp->caching.maximum_prefetch);
scsi_set_uint16(&data->data[hdr_size + 10], mp->caching.maximum_prefetch_ceiling);
if (mp->caching.fsw) data->data[hdr_size + 12] |= 0x80;
if (mp->caching.lbcss) data->data[hdr_size + 12] |= 0x40;
if (mp->caching.dra) data->data[hdr_size + 12] |= 0x20;
if (mp->caching.nv_dis) data->data[hdr_size + 12] |= 0x01;
data->data[hdr_size + 13] = mp->caching.number_of_cache_segments;
scsi_set_uint16(&data->data[hdr_size + 14], mp->caching.cache_segment_size);
return data;
}
static struct scsi_data *
scsi_modesense_marshall_control(struct scsi_task *task,
struct scsi_mode_page *mp,
int hdr_size)
{
struct scsi_data *data;
data = scsi_malloc(task, sizeof(struct scsi_data));
data->size = 12 + hdr_size;
data->data = scsi_malloc(task, data->size);
data->data[hdr_size + 2] |= (mp->control.tst << 5) & 0xe0;
if (mp->control.tmf_only) data->data[hdr_size + 2] |= 0x10;
if (mp->control.dpicz) data->data[hdr_size + 2] |= 0x08;
if (mp->control.d_sense) data->data[hdr_size + 2] |= 0x04;
if (mp->control.gltsd) data->data[hdr_size + 2] |= 0x02;
if (mp->control.rlec) data->data[hdr_size + 2] |= 0x01;
data->data[hdr_size + 3] |= (mp->control.queue_algorithm_modifier << 4) & 0xf0;
if (mp->control.nuar) data->data[hdr_size + 3] |= 0x08;
data->data[hdr_size + 3] |= (mp->control.qerr << 1) & 0x06;
if (mp->control.vs) data->data[hdr_size + 4] |= 0x80;
if (mp->control.rac) data->data[hdr_size + 4] |= 0x40;
data->data[hdr_size + 4] |= (mp->control.ua_intlck_ctrl << 4) & 0x30;
if (mp->control.swp) data->data[hdr_size + 4] |= 0x08;
if (mp->control.ato) data->data[hdr_size + 5] |= 0x80;
if (mp->control.tas) data->data[hdr_size + 5] |= 0x40;
if (mp->control.atmpe) data->data[hdr_size + 5] |= 0x20;
if (mp->control.rwwp) data->data[hdr_size + 5] |= 0x10;
data->data[hdr_size + 5] |= mp->control.autoload_mode & 0x07;
scsi_set_uint16(&data->data[hdr_size + 8], mp->control.busy_timeout_period);
scsi_set_uint16(&data->data[hdr_size + 10], mp->control.extended_selftest_completion_time);
return data;
}
static struct scsi_data *
scsi_modesense_marshall_disconnect_reconnect(struct scsi_task *task,
struct scsi_mode_page *mp,
int hdr_size)
{
struct scsi_data *data;
data = scsi_malloc(task, sizeof(struct scsi_data));
data->size = 16 + hdr_size;
data->data = scsi_malloc(task, data->size);
data->data[hdr_size + 2] = mp->disconnect_reconnect.buffer_full_ratio;
data->data[hdr_size + 3] = mp->disconnect_reconnect.buffer_empty_ratio;
scsi_set_uint16(&data->data[hdr_size + 4], mp->disconnect_reconnect.bus_inactivity_limit);
scsi_set_uint16(&data->data[hdr_size + 6], mp->disconnect_reconnect.disconnect_time_limit);
scsi_set_uint16(&data->data[hdr_size + 8], mp->disconnect_reconnect.connect_time_limit);
scsi_set_uint16(&data->data[hdr_size + 10], mp->disconnect_reconnect.maximum_burst_size);
if (mp->disconnect_reconnect.emdp) data->data[hdr_size + 12] |= 0x80;
data->data[hdr_size + 12] |= (mp->disconnect_reconnect.fair_arbitration << 4) & 0x70;
if (mp->disconnect_reconnect.dimm) data->data[hdr_size + 12] |= 0x08;
data->data[hdr_size + 12] |= mp->disconnect_reconnect.dtdc & 0x07;
scsi_set_uint16(&data->data[hdr_size + 14], mp->disconnect_reconnect.first_burst_size);
return data;
}
static struct scsi_data *
scsi_modesense_marshall_informational_exceptions_control(struct scsi_task *task,
struct scsi_mode_page *mp,
int hdr_size)
{
struct scsi_data *data;
data = scsi_malloc(task, sizeof(struct scsi_data));
data->size = 12 + hdr_size;
data->data = scsi_malloc(task, data->size);
if (mp->iec.perf) data->data[hdr_size + 2] |= 0x80;
if (mp->iec.ebf) data->data[hdr_size + 2] |= 0x20;
if (mp->iec.ewasc) data->data[hdr_size + 2] |= 0x10;
if (mp->iec.dexcpt) data->data[hdr_size + 2] |= 0x08;
if (mp->iec.test) data->data[hdr_size + 2] |= 0x04;
if (mp->iec.ebackerr) data->data[hdr_size + 2] |= 0x02;
if (mp->iec.logerr) data->data[hdr_size + 2] |= 0x01;
data->data[hdr_size + 3] |= mp->iec.mrie & 0x0f;
scsi_set_uint32(&data->data[hdr_size + 4], mp->iec.interval_timer);
scsi_set_uint32(&data->data[hdr_size + 8], mp->iec.report_count);
return data;
}
/*
* marshall the mode sense data out buffer
*/
struct scsi_data *
scsi_modesense_dataout_marshall(struct scsi_task *task,
struct scsi_mode_page *mp,
int is_modeselect6)
{
struct scsi_data *data;
int hdr_size = is_modeselect6 ? 4 : 8;
switch (mp->page_code) {
case SCSI_MODESENSE_PAGECODE_CACHING:
data = scsi_modesense_marshall_caching(task, mp, hdr_size);
break;
case SCSI_MODESENSE_PAGECODE_CONTROL:
data = scsi_modesense_marshall_control(task, mp, hdr_size);
break;
case SCSI_MODESENSE_PAGECODE_DISCONNECT_RECONNECT:
data = scsi_modesense_marshall_disconnect_reconnect(task, mp, hdr_size);
break;
case SCSI_MODESENSE_PAGECODE_INFORMATIONAL_EXCEPTIONS_CONTROL:
data = scsi_modesense_marshall_informational_exceptions_control(task, mp, hdr_size);
break;
default:
/* TODO error reporting ? */
return NULL;
}
if (data == NULL) {
return NULL;
}
if (is_modeselect6) {
data->data[0] = data->size - 1;
} else {
data->data[0] = (data->size - 2) >> 8;
data->data[1] = (data->size - 2) & 0xff;
}
data->data[hdr_size + 0] = mp->page_code & 0x3f;
if (mp->ps) {
data->data[hdr_size + 0] |= 0x80;
}
if (mp->spf) {
data->data[hdr_size + 0] |= 0x40;
data->data[hdr_size + 1] = mp->subpage_code;
scsi_set_uint16(&data->data[hdr_size + 2], data->size -hdr_size - 4);
} else {
data->data[hdr_size + 1] = (data->size - hdr_size - 2) & 0xff;
}
return data;
}
/*
* STARTSTOPUNIT
*/

View File

@@ -1156,6 +1156,26 @@ iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun,
}
struct scsi_task *
iscsi_modeselect6_sync(struct iscsi_context *iscsi, int lun,
int pf, int sp, struct scsi_mode_page *mp)
{
struct iscsi_sync_state state;
memset(&state, 0, sizeof(state));
if (iscsi_modeselect6_task(iscsi, lun, pf, sp, mp,
scsi_sync_cb, &state) == NULL) {
iscsi_set_error(iscsi,
"Failed to send MODE_SELECT6 command");
return NULL;
}
event_loop(iscsi, &state);
return state.task;
}
struct scsi_task *
iscsi_modesense6_sync(struct iscsi_context *iscsi, int lun, int dbd,
int pc, int page_code, int sub_page_code,