diff options
author | Andy Grover <agrover@redhat.com> | 2015-04-14 17:30:04 -0700 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-04-19 22:40:26 -0700 |
commit | 0ad46af8a618fc38e0cdc3927cfa9f7b42cc9423 (patch) | |
tree | 92ff9184b1c24d7886403f6f2bbb25115022e16f /drivers/target | |
parent | 65204c84d7666a78e965da0dd8696bdb007606dd (diff) | |
download | linux-stable-0ad46af8a618fc38e0cdc3927cfa9f7b42cc9423.tar.gz linux-stable-0ad46af8a618fc38e0cdc3927cfa9f7b42cc9423.tar.bz2 linux-stable-0ad46af8a618fc38e0cdc3927cfa9f7b42cc9423.zip |
target: Version 2 of TCMU ABI
The initial version of TCMU (in 3.18) does not properly handle
bidirectional SCSI commands -- those with both an in and out buffer. In
looking to fix this it also became clear that TCMU's support for adding
new types of entries (opcodes) to the command ring was broken. We need
to fix this now, so that future issues can be handled properly by adding
new opcodes.
We make the most of this ABI break by enabling bidi cmd handling within
TCMP_OP_CMD opcode. Add an iov_bidi_cnt field to tcmu_cmd_entry.req.
This enables TCMU to describe bidi commands, but further kernel work is
needed for full bidi support.
Enlarge tcmu_cmd_entry_hdr by 32 bits by pulling in cmd_id and __pad1. Turn
__pad1 into two 8 bit flags fields, for kernel-set and userspace-set flags,
"kflags" and "uflags" respectively.
Update version fields so userspace can tell the interface is changed.
Update tcmu-design.txt with details of how new stuff works:
- Specify an additional requirement for userspace to set UNKNOWN_OP
(bit 0) in hdr.uflags for unknown/unhandled opcodes.
- Define how Data-In and Data-Out fields are described in req.iov[]
Changed in v2:
- Change name of SKIPPED bit to UNKNOWN bit
- PAD op does not set the bit any more
- Change len_op helper functions to take just len_op, not the whole struct
- Change version to 2 in missed spots, and use defines
- Add 16 unused bytes to cmd_entry.req, in case additional SAM cmd
parameters need to be included
- Add iov_dif_cnt field to specify buffers used for DIF info in iov[]
- Rearrange fields to naturally align cdb_off
- Handle if userspace sets UNKNOWN_OP by indicating failure of the cmd
- Wrap some overly long UPDATE_HEAD lines
(Add missing req.iov_bidi_cnt + req.iov_dif_cnt zeroing - Ilias)
Signed-off-by: Andy Grover <agrover@redhat.com>
Reviewed-by: Ilias Tsitsimpis <iliastsi@arrikto.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_user.c | 46 |
1 files changed, 34 insertions, 12 deletions
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 1fbf304a9491..dbc872a6c981 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -344,8 +344,11 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) entry = (void *) mb + CMDR_OFF + cmd_head; tcmu_flush_dcache_range(entry, sizeof(*entry)); - tcmu_hdr_set_op(&entry->hdr, TCMU_OP_PAD); - tcmu_hdr_set_len(&entry->hdr, pad_size); + tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_PAD); + tcmu_hdr_set_len(&entry->hdr.len_op, pad_size); + entry->hdr.cmd_id = 0; /* not used for PAD */ + entry->hdr.kflags = 0; + entry->hdr.uflags = 0; UPDATE_HEAD(mb->cmd_head, pad_size, udev->cmdr_size); @@ -355,9 +358,11 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) entry = (void *) mb + CMDR_OFF + cmd_head; tcmu_flush_dcache_range(entry, sizeof(*entry)); - tcmu_hdr_set_op(&entry->hdr, TCMU_OP_CMD); - tcmu_hdr_set_len(&entry->hdr, command_size); - entry->cmd_id = tcmu_cmd->cmd_id; + tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_CMD); + tcmu_hdr_set_len(&entry->hdr.len_op, command_size); + entry->hdr.cmd_id = tcmu_cmd->cmd_id; + entry->hdr.kflags = 0; + entry->hdr.uflags = 0; /* * Fix up iovecs, and handle if allocation in data ring wrapped. @@ -407,6 +412,8 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) kunmap_atomic(from); } entry->req.iov_cnt = iov_cnt; + entry->req.iov_bidi_cnt = 0; + entry->req.iov_dif_cnt = 0; /* All offsets relative to mb_addr, not start of entry! */ cdb_off = CMDR_OFF + cmd_head + base_command_size; @@ -464,6 +471,17 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * return; } + if (entry->hdr.uflags & TCMU_UFLAG_UNKNOWN_OP) { + UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n", + cmd->se_cmd); + transport_generic_request_failure(cmd->se_cmd, + TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE); + cmd->se_cmd = NULL; + kmem_cache_free(tcmu_cmd_cache, cmd); + return; + } + if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, se_cmd->scsi_sense_length); @@ -542,14 +560,16 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) tcmu_flush_dcache_range(entry, sizeof(*entry)); - if (tcmu_hdr_get_op(&entry->hdr) == TCMU_OP_PAD) { - UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); + if (tcmu_hdr_get_op(entry->hdr.len_op) == TCMU_OP_PAD) { + UPDATE_HEAD(udev->cmdr_last_cleaned, + tcmu_hdr_get_len(entry->hdr.len_op), + udev->cmdr_size); continue; } - WARN_ON(tcmu_hdr_get_op(&entry->hdr) != TCMU_OP_CMD); + WARN_ON(tcmu_hdr_get_op(entry->hdr.len_op) != TCMU_OP_CMD); spin_lock(&udev->commands_lock); - cmd = idr_find(&udev->commands, entry->cmd_id); + cmd = idr_find(&udev->commands, entry->hdr.cmd_id); if (cmd) idr_remove(&udev->commands, cmd->cmd_id); spin_unlock(&udev->commands_lock); @@ -562,7 +582,9 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) tcmu_handle_completion(cmd, entry); - UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); + UPDATE_HEAD(udev->cmdr_last_cleaned, + tcmu_hdr_get_len(entry->hdr.len_op), + udev->cmdr_size); handled++; } @@ -840,14 +862,14 @@ static int tcmu_configure_device(struct se_device *dev) udev->data_size = TCMU_RING_SIZE - CMDR_SIZE; mb = udev->mb_addr; - mb->version = 1; + mb->version = TCMU_MAILBOX_VERSION; mb->cmdr_off = CMDR_OFF; mb->cmdr_size = udev->cmdr_size; WARN_ON(!PAGE_ALIGNED(udev->data_off)); WARN_ON(udev->data_size % PAGE_SIZE); - info->version = "1"; + info->version = xstr(TCMU_MAILBOX_VERSION); info->mem[0].name = "tcm-user command & data buffer"; info->mem[0].addr = (phys_addr_t) udev->mb_addr; |