From 7895fb700c436b267e2950714ec35991f3997f7e Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sat, 6 Jul 2013 16:11:17 -0700 Subject: [PATCH] Add MODESELECT6 support Add support for MODESELECT6 and add marshalling functions for the mode pages we support so far. --- include/iscsi.h | 10 ++ include/scsi-lowlevel.h | 12 ++ lib/iscsi-command.c | 37 ++++++ lib/libiscsi.def | 5 + lib/libiscsi.syms | 5 + lib/scsi-lowlevel.c | 247 ++++++++++++++++++++++++++++++++++++++++ lib/sync.c | 20 ++++ 7 files changed, 336 insertions(+) diff --git a/include/iscsi.h b/include/iscsi.h index a4ed932..3616b0a 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -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, diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 5c60f15..6232a4e 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -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 { diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index 37d0a42..096d2b7 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -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, diff --git a/lib/libiscsi.def b/lib/libiscsi.def index f6aca16..209c73b 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -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 diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 0feb660..fb4d57c 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -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 diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index c0113db..f520763 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -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 */ diff --git a/lib/sync.c b/lib/sync.c index f69dbf5..c20c9a5 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -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,