diff options
| -rw-r--r-- | Documentation/target/tcmu-design.txt | 43 | ||||
| -rw-r--r-- | drivers/target/target_core_user.c | 46 | ||||
| -rw-r--r-- | include/uapi/linux/target_core_user.h | 44 |
3 files changed, 89 insertions, 44 deletions
diff --git a/Documentation/target/tcmu-design.txt b/Documentation/target/tcmu-design.txt index 5518465290bf..43e94ea6d2ca 100644 --- a/Documentation/target/tcmu-design.txt +++ b/Documentation/target/tcmu-design.txt | |||
| @@ -138,27 +138,40 @@ signals the kernel via a 4-byte write(). When cmd_head equals | |||
| 138 | cmd_tail, the ring is empty -- no commands are currently waiting to be | 138 | cmd_tail, the ring is empty -- no commands are currently waiting to be |
| 139 | processed by userspace. | 139 | processed by userspace. |
| 140 | 140 | ||
| 141 | TCMU commands start with a common header containing "len_op", a 32-bit | 141 | TCMU commands are 8-byte aligned. They start with a common header |
| 142 | value that stores the length, as well as the opcode in the lowest | 142 | containing "len_op", a 32-bit value that stores the length, as well as |
| 143 | unused bits. Currently only two opcodes are defined, TCMU_OP_PAD and | 143 | the opcode in the lowest unused bits. It also contains cmd_id and |
| 144 | TCMU_OP_CMD. When userspace encounters a command with PAD opcode, it | 144 | flags fields for setting by the kernel (kflags) and userspace |
| 145 | should skip ahead by the bytes in "length". (The kernel inserts PAD | 145 | (uflags). |
| 146 | entries to ensure each CMD entry fits contigously into the circular | 146 | |
| 147 | buffer.) | 147 | Currently only two opcodes are defined, TCMU_OP_CMD and TCMU_OP_PAD. |
| 148 | 148 | ||
| 149 | When userspace handles a CMD, it finds the SCSI CDB (Command Data | 149 | When the opcode is CMD, the entry in the command ring is a struct |
| 150 | Block) via tcmu_cmd_entry.req.cdb_off. This is an offset from the | 150 | tcmu_cmd_entry. Userspace finds the SCSI CDB (Command Data Block) via |
| 151 | start of the overall shared memory region, not the entry. The data | 151 | tcmu_cmd_entry.req.cdb_off. This is an offset from the start of the |
| 152 | in/out buffers are accessible via tht req.iov[] array. Note that | 152 | overall shared memory region, not the entry. The data in/out buffers |
| 153 | each iov.iov_base is also an offset from the start of the region. | 153 | are accessible via tht req.iov[] array. iov_cnt contains the number of |
| 154 | 154 | entries in iov[] needed to describe either the Data-In or Data-Out | |
| 155 | TCMU currently does not support BIDI operations. | 155 | buffers. For bidirectional commands, iov_cnt specifies how many iovec |
| 156 | entries cover the Data-Out area, and iov_bidi_count specifies how many | ||
| 157 | iovec entries immediately after that in iov[] cover the Data-In | ||
| 158 | area. Just like other fields, iov.iov_base is an offset from the start | ||
| 159 | of the region. | ||
| 156 | 160 | ||
| 157 | When completing a command, userspace sets rsp.scsi_status, and | 161 | When completing a command, userspace sets rsp.scsi_status, and |
| 158 | rsp.sense_buffer if necessary. Userspace then increments | 162 | rsp.sense_buffer if necessary. Userspace then increments |
| 159 | mailbox.cmd_tail by entry.hdr.length (mod cmdr_size) and signals the | 163 | mailbox.cmd_tail by entry.hdr.length (mod cmdr_size) and signals the |
| 160 | kernel via the UIO method, a 4-byte write to the file descriptor. | 164 | kernel via the UIO method, a 4-byte write to the file descriptor. |
| 161 | 165 | ||
| 166 | When the opcode is PAD, userspace only updates cmd_tail as above -- | ||
| 167 | it's a no-op. (The kernel inserts PAD entries to ensure each CMD entry | ||
| 168 | is contiguous within the command ring.) | ||
| 169 | |||
| 170 | More opcodes may be added in the future. If userspace encounters an | ||
| 171 | opcode it does not handle, it must set UNKNOWN_OP bit (bit 0) in | ||
| 172 | hdr.uflags, update cmd_tail, and proceed with processing additional | ||
| 173 | commands, if any. | ||
| 174 | |||
| 162 | The Data Area: | 175 | The Data Area: |
| 163 | 176 | ||
| 164 | This is shared-memory space after the command ring. The organization | 177 | This is shared-memory space after the command ring. The organization |
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) | |||
| 344 | 344 | ||
| 345 | entry = (void *) mb + CMDR_OFF + cmd_head; | 345 | entry = (void *) mb + CMDR_OFF + cmd_head; |
| 346 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | 346 | tcmu_flush_dcache_range(entry, sizeof(*entry)); |
| 347 | tcmu_hdr_set_op(&entry->hdr, TCMU_OP_PAD); | 347 | tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_PAD); |
| 348 | tcmu_hdr_set_len(&entry->hdr, pad_size); | 348 | tcmu_hdr_set_len(&entry->hdr.len_op, pad_size); |
| 349 | entry->hdr.cmd_id = 0; /* not used for PAD */ | ||
| 350 | entry->hdr.kflags = 0; | ||
| 351 | entry->hdr.uflags = 0; | ||
| 349 | 352 | ||
| 350 | UPDATE_HEAD(mb->cmd_head, pad_size, udev->cmdr_size); | 353 | UPDATE_HEAD(mb->cmd_head, pad_size, udev->cmdr_size); |
| 351 | 354 | ||
| @@ -355,9 +358,11 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
| 355 | 358 | ||
| 356 | entry = (void *) mb + CMDR_OFF + cmd_head; | 359 | entry = (void *) mb + CMDR_OFF + cmd_head; |
| 357 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | 360 | tcmu_flush_dcache_range(entry, sizeof(*entry)); |
| 358 | tcmu_hdr_set_op(&entry->hdr, TCMU_OP_CMD); | 361 | tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_CMD); |
| 359 | tcmu_hdr_set_len(&entry->hdr, command_size); | 362 | tcmu_hdr_set_len(&entry->hdr.len_op, command_size); |
| 360 | entry->cmd_id = tcmu_cmd->cmd_id; | 363 | entry->hdr.cmd_id = tcmu_cmd->cmd_id; |
| 364 | entry->hdr.kflags = 0; | ||
| 365 | entry->hdr.uflags = 0; | ||
| 361 | 366 | ||
| 362 | /* | 367 | /* |
| 363 | * Fix up iovecs, and handle if allocation in data ring wrapped. | 368 | * 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) | |||
| 407 | kunmap_atomic(from); | 412 | kunmap_atomic(from); |
| 408 | } | 413 | } |
| 409 | entry->req.iov_cnt = iov_cnt; | 414 | entry->req.iov_cnt = iov_cnt; |
| 415 | entry->req.iov_bidi_cnt = 0; | ||
| 416 | entry->req.iov_dif_cnt = 0; | ||
| 410 | 417 | ||
| 411 | /* All offsets relative to mb_addr, not start of entry! */ | 418 | /* All offsets relative to mb_addr, not start of entry! */ |
| 412 | cdb_off = CMDR_OFF + cmd_head + base_command_size; | 419 | 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 * | |||
| 464 | return; | 471 | return; |
| 465 | } | 472 | } |
| 466 | 473 | ||
| 474 | if (entry->hdr.uflags & TCMU_UFLAG_UNKNOWN_OP) { | ||
| 475 | UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); | ||
| 476 | pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n", | ||
| 477 | cmd->se_cmd); | ||
| 478 | transport_generic_request_failure(cmd->se_cmd, | ||
| 479 | TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE); | ||
| 480 | cmd->se_cmd = NULL; | ||
| 481 | kmem_cache_free(tcmu_cmd_cache, cmd); | ||
| 482 | return; | ||
| 483 | } | ||
| 484 | |||
| 467 | if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { | 485 | if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { |
| 468 | memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, | 486 | memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, |
| 469 | se_cmd->scsi_sense_length); | 487 | se_cmd->scsi_sense_length); |
| @@ -542,14 +560,16 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) | |||
| 542 | 560 | ||
| 543 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | 561 | tcmu_flush_dcache_range(entry, sizeof(*entry)); |
| 544 | 562 | ||
| 545 | if (tcmu_hdr_get_op(&entry->hdr) == TCMU_OP_PAD) { | 563 | if (tcmu_hdr_get_op(entry->hdr.len_op) == TCMU_OP_PAD) { |
| 546 | UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); | 564 | UPDATE_HEAD(udev->cmdr_last_cleaned, |
| 565 | tcmu_hdr_get_len(entry->hdr.len_op), | ||
| 566 | udev->cmdr_size); | ||
| 547 | continue; | 567 | continue; |
| 548 | } | 568 | } |
| 549 | WARN_ON(tcmu_hdr_get_op(&entry->hdr) != TCMU_OP_CMD); | 569 | WARN_ON(tcmu_hdr_get_op(entry->hdr.len_op) != TCMU_OP_CMD); |
| 550 | 570 | ||
| 551 | spin_lock(&udev->commands_lock); | 571 | spin_lock(&udev->commands_lock); |
| 552 | cmd = idr_find(&udev->commands, entry->cmd_id); | 572 | cmd = idr_find(&udev->commands, entry->hdr.cmd_id); |
| 553 | if (cmd) | 573 | if (cmd) |
| 554 | idr_remove(&udev->commands, cmd->cmd_id); | 574 | idr_remove(&udev->commands, cmd->cmd_id); |
| 555 | spin_unlock(&udev->commands_lock); | 575 | spin_unlock(&udev->commands_lock); |
| @@ -562,7 +582,9 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) | |||
| 562 | 582 | ||
| 563 | tcmu_handle_completion(cmd, entry); | 583 | tcmu_handle_completion(cmd, entry); |
| 564 | 584 | ||
| 565 | UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); | 585 | UPDATE_HEAD(udev->cmdr_last_cleaned, |
| 586 | tcmu_hdr_get_len(entry->hdr.len_op), | ||
| 587 | udev->cmdr_size); | ||
| 566 | 588 | ||
| 567 | handled++; | 589 | handled++; |
| 568 | } | 590 | } |
| @@ -840,14 +862,14 @@ static int tcmu_configure_device(struct se_device *dev) | |||
| 840 | udev->data_size = TCMU_RING_SIZE - CMDR_SIZE; | 862 | udev->data_size = TCMU_RING_SIZE - CMDR_SIZE; |
| 841 | 863 | ||
| 842 | mb = udev->mb_addr; | 864 | mb = udev->mb_addr; |
| 843 | mb->version = 1; | 865 | mb->version = TCMU_MAILBOX_VERSION; |
| 844 | mb->cmdr_off = CMDR_OFF; | 866 | mb->cmdr_off = CMDR_OFF; |
| 845 | mb->cmdr_size = udev->cmdr_size; | 867 | mb->cmdr_size = udev->cmdr_size; |
| 846 | 868 | ||
| 847 | WARN_ON(!PAGE_ALIGNED(udev->data_off)); | 869 | WARN_ON(!PAGE_ALIGNED(udev->data_off)); |
| 848 | WARN_ON(udev->data_size % PAGE_SIZE); | 870 | WARN_ON(udev->data_size % PAGE_SIZE); |
| 849 | 871 | ||
| 850 | info->version = "1"; | 872 | info->version = xstr(TCMU_MAILBOX_VERSION); |
| 851 | 873 | ||
| 852 | info->mem[0].name = "tcm-user command & data buffer"; | 874 | info->mem[0].name = "tcm-user command & data buffer"; |
| 853 | info->mem[0].addr = (phys_addr_t) udev->mb_addr; | 875 | info->mem[0].addr = (phys_addr_t) udev->mb_addr; |
diff --git a/include/uapi/linux/target_core_user.h b/include/uapi/linux/target_core_user.h index b483d1909d3e..b67f99d3c520 100644 --- a/include/uapi/linux/target_core_user.h +++ b/include/uapi/linux/target_core_user.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include <linux/types.h> | 6 | #include <linux/types.h> |
| 7 | #include <linux/uio.h> | 7 | #include <linux/uio.h> |
| 8 | 8 | ||
| 9 | #define TCMU_VERSION "1.0" | 9 | #define TCMU_VERSION "2.0" |
| 10 | 10 | ||
| 11 | /* | 11 | /* |
| 12 | * Ring Design | 12 | * Ring Design |
| @@ -39,9 +39,13 @@ | |||
| 39 | * should process the next packet the same way, and so on. | 39 | * should process the next packet the same way, and so on. |
| 40 | */ | 40 | */ |
| 41 | 41 | ||
| 42 | #define TCMU_MAILBOX_VERSION 1 | 42 | #define TCMU_MAILBOX_VERSION 2 |
| 43 | #define ALIGN_SIZE 64 /* Should be enough for most CPUs */ | 43 | #define ALIGN_SIZE 64 /* Should be enough for most CPUs */ |
| 44 | 44 | ||
| 45 | /* See https://gcc.gnu.org/onlinedocs/cpp/Stringification.html */ | ||
| 46 | #define xstr(s) str(s) | ||
| 47 | #define str(s) #s | ||
| 48 | |||
| 45 | struct tcmu_mailbox { | 49 | struct tcmu_mailbox { |
| 46 | __u16 version; | 50 | __u16 version; |
| 47 | __u16 flags; | 51 | __u16 flags; |
| @@ -64,31 +68,36 @@ enum tcmu_opcode { | |||
| 64 | * Only a few opcodes, and length is 8-byte aligned, so use low bits for opcode. | 68 | * Only a few opcodes, and length is 8-byte aligned, so use low bits for opcode. |
| 65 | */ | 69 | */ |
| 66 | struct tcmu_cmd_entry_hdr { | 70 | struct tcmu_cmd_entry_hdr { |
| 67 | __u32 len_op; | 71 | __u32 len_op; |
| 72 | __u16 cmd_id; | ||
| 73 | __u8 kflags; | ||
| 74 | #define TCMU_UFLAG_UNKNOWN_OP 0x1 | ||
| 75 | __u8 uflags; | ||
| 76 | |||
| 68 | } __packed; | 77 | } __packed; |
| 69 | 78 | ||
| 70 | #define TCMU_OP_MASK 0x7 | 79 | #define TCMU_OP_MASK 0x7 |
| 71 | 80 | ||
| 72 | static inline enum tcmu_opcode tcmu_hdr_get_op(struct tcmu_cmd_entry_hdr *hdr) | 81 | static inline enum tcmu_opcode tcmu_hdr_get_op(__u32 len_op) |
| 73 | { | 82 | { |
| 74 | return hdr->len_op & TCMU_OP_MASK; | 83 | return len_op & TCMU_OP_MASK; |
| 75 | } | 84 | } |
| 76 | 85 | ||
| 77 | static inline void tcmu_hdr_set_op(struct tcmu_cmd_entry_hdr *hdr, enum tcmu_opcode op) | 86 | static inline void tcmu_hdr_set_op(__u32 *len_op, enum tcmu_opcode op) |
| 78 | { | 87 | { |
| 79 | hdr->len_op &= ~TCMU_OP_MASK; | 88 | *len_op &= ~TCMU_OP_MASK; |
| 80 | hdr->len_op |= (op & TCMU_OP_MASK); | 89 | *len_op |= (op & TCMU_OP_MASK); |
| 81 | } | 90 | } |
| 82 | 91 | ||
| 83 | static inline __u32 tcmu_hdr_get_len(struct tcmu_cmd_entry_hdr *hdr) | 92 | static inline __u32 tcmu_hdr_get_len(__u32 len_op) |
| 84 | { | 93 | { |
| 85 | return hdr->len_op & ~TCMU_OP_MASK; | 94 | return len_op & ~TCMU_OP_MASK; |
| 86 | } | 95 | } |
| 87 | 96 | ||
| 88 | static inline void tcmu_hdr_set_len(struct tcmu_cmd_entry_hdr *hdr, __u32 len) | 97 | static inline void tcmu_hdr_set_len(__u32 *len_op, __u32 len) |
| 89 | { | 98 | { |
| 90 | hdr->len_op &= TCMU_OP_MASK; | 99 | *len_op &= TCMU_OP_MASK; |
| 91 | hdr->len_op |= len; | 100 | *len_op |= len; |
| 92 | } | 101 | } |
| 93 | 102 | ||
| 94 | /* Currently the same as SCSI_SENSE_BUFFERSIZE */ | 103 | /* Currently the same as SCSI_SENSE_BUFFERSIZE */ |
| @@ -97,13 +106,14 @@ static inline void tcmu_hdr_set_len(struct tcmu_cmd_entry_hdr *hdr, __u32 len) | |||
| 97 | struct tcmu_cmd_entry { | 106 | struct tcmu_cmd_entry { |
| 98 | struct tcmu_cmd_entry_hdr hdr; | 107 | struct tcmu_cmd_entry_hdr hdr; |
| 99 | 108 | ||
| 100 | uint16_t cmd_id; | ||
| 101 | uint16_t __pad1; | ||
| 102 | |||
| 103 | union { | 109 | union { |
| 104 | struct { | 110 | struct { |
| 111 | uint32_t iov_cnt; | ||
| 112 | uint32_t iov_bidi_cnt; | ||
| 113 | uint32_t iov_dif_cnt; | ||
| 105 | uint64_t cdb_off; | 114 | uint64_t cdb_off; |
| 106 | uint64_t iov_cnt; | 115 | uint64_t __pad1; |
| 116 | uint64_t __pad2; | ||
| 107 | struct iovec iov[0]; | 117 | struct iovec iov[0]; |
| 108 | } req; | 118 | } req; |
| 109 | struct { | 119 | struct { |
