From f1dbd087cc7a28c6c174cb28cf98c19f4efb1fba Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Fri, 26 Feb 2016 14:59:55 -0800 Subject: target/user: Use iovec[] to describe continuous area We don't need use one iovec per scatter-gather list entry, since data area are continuous. Reviewed-by: Andy Grover Signed-off-by: Sheng Yang Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_user.c | 41 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) (limited to 'drivers/target/target_core_user.c') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 94f5154ac788..93de51fbe392 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -231,6 +231,23 @@ static inline size_t head_to_end(size_t head, size_t size) return size - head; } +static inline void new_iov(struct iovec **iov, int *iov_cnt, + struct tcmu_dev *udev) +{ + struct iovec *iovec; + + if (*iov_cnt != 0) + (*iov)++; + (*iov_cnt)++; + + iovec = *iov; + memset(iovec, 0, sizeof(struct iovec)); + + /* Even iov_base is relative to mb_addr */ + iovec->iov_base = (void __user *) udev->data_off + + udev->data_head; +} + #define UPDATE_HEAD(head, used, size) smp_store_release(&head, ((head % size) + used) % size) static void alloc_and_scatter_data_area(struct tcmu_dev *udev, @@ -242,6 +259,10 @@ static void alloc_and_scatter_data_area(struct tcmu_dev *udev, size_t copy_bytes; struct scatterlist *sg; + if (data_nents == 0) + return; + + new_iov(iov, iov_cnt, udev); for_each_sg(data_sg, sg, data_nents, i) { copy_bytes = min_t(size_t, sg->length, head_to_end(udev->data_head, udev->data_size)); @@ -253,12 +274,7 @@ static void alloc_and_scatter_data_area(struct tcmu_dev *udev, tcmu_flush_dcache_range(to, copy_bytes); } - /* Even iov_base is relative to mb_addr */ - (*iov)->iov_len = copy_bytes; - (*iov)->iov_base = (void __user *) udev->data_off + - udev->data_head; - (*iov_cnt)++; - (*iov)++; + (*iov)->iov_len += copy_bytes; UPDATE_HEAD(udev->data_head, copy_bytes, udev->data_size); @@ -268,9 +284,8 @@ static void alloc_and_scatter_data_area(struct tcmu_dev *udev, copy_bytes = sg->length - copy_bytes; + new_iov(iov, iov_cnt, udev); (*iov)->iov_len = copy_bytes; - (*iov)->iov_base = (void __user *) udev->data_off + - udev->data_head; if (copy_data) { to = (void *) udev->mb_addr + @@ -279,8 +294,6 @@ static void alloc_and_scatter_data_area(struct tcmu_dev *udev, tcmu_flush_dcache_range(to, copy_bytes); } - (*iov_cnt)++; - (*iov)++; UPDATE_HEAD(udev->data_head, copy_bytes, udev->data_size); @@ -393,12 +406,10 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) * Must be a certain minimum size for response sense info, but * also may be larger if the iov array is large. * - * iovs = sgl_nents+1, for end-of-ring case, plus another 1 - * b/c size == offsetof one-past-element. + * 3 iovs since we can describe the whole continuous are using one + * for data, one for bidi and one more in the case of wrap. */ - base_command_size = max(offsetof(struct tcmu_cmd_entry, - req.iov[se_cmd->t_bidi_data_nents + - se_cmd->t_data_nents + 2]), + base_command_size = max(offsetof(struct tcmu_cmd_entry, req.iov[3]), sizeof(struct tcmu_cmd_entry)); command_size = base_command_size + round_up(scsi_command_size(se_cmd->t_task_cdb), TCMU_OP_ALIGN_SIZE); -- cgit v1.2.3 From 0c28481ffb4683ef21c6664d15dbd5ae5a6cd027 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Fri, 26 Feb 2016 14:59:56 -0800 Subject: target/user: Free data ring in unified function Prepare for data_bitmap in the next patch. Reviewed-by: Andy Grover Signed-off-by: Sheng Yang Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_user.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/target/target_core_user.c') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 93de51fbe392..baa8720ab24f 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -303,6 +303,11 @@ static void alloc_and_scatter_data_area(struct tcmu_dev *udev, } } +static void free_data_area(struct tcmu_dev *udev, size_t length) +{ + UPDATE_HEAD(udev->data_tail, length, udev->data_size); +} + static void gather_and_free_data_area(struct tcmu_dev *udev, struct scatterlist *data_sg, unsigned int data_nents) { @@ -323,7 +328,7 @@ static void gather_and_free_data_area(struct tcmu_dev *udev, tcmu_flush_dcache_range(from, copy_bytes); memcpy(to, from, copy_bytes); - UPDATE_HEAD(udev->data_tail, copy_bytes, udev->data_size); + free_data_area(udev, copy_bytes); /* Uh oh, wrapped the data buffer for this sg's data */ if (sg->length != copy_bytes) { @@ -336,8 +341,7 @@ static void gather_and_free_data_area(struct tcmu_dev *udev, tcmu_flush_dcache_range(from, copy_bytes); memcpy(to_skip, from, copy_bytes); - UPDATE_HEAD(udev->data_tail, - copy_bytes, udev->data_size); + free_data_area(udev, copy_bytes); } kunmap_atomic(to - sg->offset); } @@ -543,24 +547,22 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { /* cmd has been completed already from timeout, just reclaim data ring space */ - UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + free_data_area(udev, cmd->data_length); return; } if (entry->hdr.uflags & TCMU_UFLAG_UNKNOWN_OP) { - UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + free_data_area(udev, cmd->data_length); pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n", cmd->se_cmd); entry->rsp.scsi_status = SAM_STAT_CHECK_CONDITION; } else if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, se_cmd->scsi_sense_length); - - UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + free_data_area(udev, cmd->data_length); } else if (se_cmd->se_cmd_flags & SCF_BIDI) { /* Discard data_out buffer */ - UPDATE_HEAD(udev->data_tail, - (size_t)se_cmd->t_data_sg->length, udev->data_size); + free_data_area(udev, (size_t)se_cmd->t_data_sg->length); /* Get Data-In buffer */ gather_and_free_data_area(udev, @@ -569,7 +571,7 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * gather_and_free_data_area(udev, se_cmd->t_data_sg, se_cmd->t_data_nents); } else if (se_cmd->data_direction == DMA_TO_DEVICE) { - UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + free_data_area(udev, cmd->data_length); } else if (se_cmd->data_direction != DMA_NONE) { pr_warn("TCMU: data direction was %d!\n", se_cmd->data_direction); -- cgit v1.2.3 From 26418649eead52619d8dd6cbc6760a1b144dbcd2 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Fri, 26 Feb 2016 14:59:57 -0800 Subject: target/user: Introduce data_bitmap, replace data_length/data_head/data_tail The data_bitmap was introduced to support asynchornization accessing of data area. We divide mailbox data area into blocks, and use data_bitmap to track the usage of data area. All the new command's data would start with a new block, and may left unusable space after it end. But it's easy to track using data_bitmap. Now we can allocate data area for asynchronization accessing from userspace, since we can track the allocation using data_bitmap. The userspace part would be the same as Maxim's previous asynchronized implementation. Signed-off-by: Sheng Yang Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_user.c | 221 +++++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 100 deletions(-) (limited to 'drivers/target/target_core_user.c') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index baa8720ab24f..b1539f357429 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -63,8 +64,11 @@ #define TCMU_TIME_OUT (30 * MSEC_PER_SEC) +#define DATA_BLOCK_BITS 256 +#define DATA_BLOCK_SIZE 4096 + #define CMDR_SIZE (16 * 4096) -#define DATA_SIZE (257 * 4096) +#define DATA_SIZE (DATA_BLOCK_BITS * DATA_BLOCK_SIZE) #define TCMU_RING_SIZE (CMDR_SIZE + DATA_SIZE) @@ -93,12 +97,11 @@ struct tcmu_dev { u32 cmdr_size; u32 cmdr_last_cleaned; /* Offset of data ring from start of mb */ + /* Must add data_off and mb_addr to get the address */ size_t data_off; size_t data_size; - /* Ring head + tail values. */ - /* Must add data_off and mb_addr to get the address */ - size_t data_head; - size_t data_tail; + + DECLARE_BITMAP(data_bitmap, DATA_BLOCK_BITS); wait_queue_head_t wait_cmdr; /* TODO should this be a mutex? */ @@ -122,9 +125,9 @@ struct tcmu_cmd { uint16_t cmd_id; - /* Can't use se_cmd->data_length when cleaning up expired cmds, because if + /* Can't use se_cmd when cleaning up expired cmds, because if cmd has been completed then accessing se_cmd is off limits */ - size_t data_length; + DECLARE_BITMAP(data_bitmap, DATA_BLOCK_BITS); unsigned long deadline; @@ -168,13 +171,6 @@ static struct tcmu_cmd *tcmu_alloc_cmd(struct se_cmd *se_cmd) tcmu_cmd->se_cmd = se_cmd; tcmu_cmd->tcmu_dev = udev; - tcmu_cmd->data_length = se_cmd->data_length; - - if (se_cmd->se_cmd_flags & SCF_BIDI) { - BUG_ON(!(se_cmd->t_bidi_data_sg && se_cmd->t_bidi_data_nents)); - tcmu_cmd->data_length += se_cmd->t_bidi_data_sg->length; - } - tcmu_cmd->deadline = jiffies + msecs_to_jiffies(TCMU_TIME_OUT); idr_preload(GFP_KERNEL); @@ -242,111 +238,115 @@ static inline void new_iov(struct iovec **iov, int *iov_cnt, iovec = *iov; memset(iovec, 0, sizeof(struct iovec)); - - /* Even iov_base is relative to mb_addr */ - iovec->iov_base = (void __user *) udev->data_off + - udev->data_head; } #define UPDATE_HEAD(head, used, size) smp_store_release(&head, ((head % size) + used) % size) +/* offset is relative to mb_addr */ +static inline size_t get_block_offset(struct tcmu_dev *dev, + int block, int remaining) +{ + return dev->data_off + block * DATA_BLOCK_SIZE + + DATA_BLOCK_SIZE - remaining; +} + +static inline size_t iov_tail(struct tcmu_dev *udev, struct iovec *iov) +{ + return (size_t)iov->iov_base + iov->iov_len; +} + static void alloc_and_scatter_data_area(struct tcmu_dev *udev, struct scatterlist *data_sg, unsigned int data_nents, struct iovec **iov, int *iov_cnt, bool copy_data) { - int i; + int i, block; + int block_remaining = 0; void *from, *to; - size_t copy_bytes; + size_t copy_bytes, to_offset; struct scatterlist *sg; - if (data_nents == 0) - return; - - new_iov(iov, iov_cnt, udev); for_each_sg(data_sg, sg, data_nents, i) { - copy_bytes = min_t(size_t, sg->length, - head_to_end(udev->data_head, udev->data_size)); + int sg_remaining = sg->length; from = kmap_atomic(sg_page(sg)) + sg->offset; - to = (void *) udev->mb_addr + udev->data_off + udev->data_head; - - if (copy_data) { - memcpy(to, from, copy_bytes); - tcmu_flush_dcache_range(to, copy_bytes); - } - - (*iov)->iov_len += copy_bytes; - - UPDATE_HEAD(udev->data_head, copy_bytes, udev->data_size); - - /* Uh oh, we wrapped the buffer. Must split sg across 2 iovs. */ - if (sg->length != copy_bytes) { - void *from_skip = from + copy_bytes; - - copy_bytes = sg->length - copy_bytes; - - new_iov(iov, iov_cnt, udev); - (*iov)->iov_len = copy_bytes; - + while (sg_remaining > 0) { + if (block_remaining == 0) { + block = find_first_zero_bit(udev->data_bitmap, + DATA_BLOCK_BITS); + block_remaining = DATA_BLOCK_SIZE; + set_bit(block, udev->data_bitmap); + } + copy_bytes = min_t(size_t, sg_remaining, + block_remaining); + to_offset = get_block_offset(udev, block, + block_remaining); + to = (void *)udev->mb_addr + to_offset; + if (*iov_cnt != 0 && + to_offset == iov_tail(udev, *iov)) { + (*iov)->iov_len += copy_bytes; + } else { + new_iov(iov, iov_cnt, udev); + (*iov)->iov_base = (void __user *) to_offset; + (*iov)->iov_len = copy_bytes; + } if (copy_data) { - to = (void *) udev->mb_addr + - udev->data_off + udev->data_head; - memcpy(to, from_skip, copy_bytes); + memcpy(to, from + sg->length - sg_remaining, + copy_bytes); tcmu_flush_dcache_range(to, copy_bytes); } - - - UPDATE_HEAD(udev->data_head, - copy_bytes, udev->data_size); + sg_remaining -= copy_bytes; + block_remaining -= copy_bytes; } - kunmap_atomic(from - sg->offset); } } -static void free_data_area(struct tcmu_dev *udev, size_t length) +static void free_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd) { - UPDATE_HEAD(udev->data_tail, length, udev->data_size); + bitmap_xor(udev->data_bitmap, udev->data_bitmap, cmd->data_bitmap, + DATA_BLOCK_BITS); } -static void gather_and_free_data_area(struct tcmu_dev *udev, - struct scatterlist *data_sg, unsigned int data_nents) +static void gather_data_area(struct tcmu_dev *udev, unsigned long *cmd_bitmap, + struct scatterlist *data_sg, unsigned int data_nents) { - int i; + int i, block; + int block_remaining = 0; void *from, *to; - size_t copy_bytes; + size_t copy_bytes, from_offset; struct scatterlist *sg; - /* It'd be easier to look at entry's iovec again, but UAM */ for_each_sg(data_sg, sg, data_nents, i) { - copy_bytes = min_t(size_t, sg->length, - head_to_end(udev->data_tail, udev->data_size)); - + int sg_remaining = sg->length; to = kmap_atomic(sg_page(sg)) + sg->offset; - WARN_ON(sg->length + sg->offset > PAGE_SIZE); - from = (void *) udev->mb_addr + - udev->data_off + udev->data_tail; - tcmu_flush_dcache_range(from, copy_bytes); - memcpy(to, from, copy_bytes); - - free_data_area(udev, copy_bytes); - - /* Uh oh, wrapped the data buffer for this sg's data */ - if (sg->length != copy_bytes) { - void *to_skip = to + copy_bytes; - - from = (void *) udev->mb_addr + - udev->data_off + udev->data_tail; - WARN_ON(udev->data_tail); - copy_bytes = sg->length - copy_bytes; + while (sg_remaining > 0) { + if (block_remaining == 0) { + block = find_first_bit(cmd_bitmap, + DATA_BLOCK_BITS); + block_remaining = DATA_BLOCK_SIZE; + clear_bit(block, cmd_bitmap); + } + copy_bytes = min_t(size_t, sg_remaining, + block_remaining); + from_offset = get_block_offset(udev, block, + block_remaining); + from = (void *) udev->mb_addr + from_offset; tcmu_flush_dcache_range(from, copy_bytes); - memcpy(to_skip, from, copy_bytes); + memcpy(to + sg->length - sg_remaining, from, + copy_bytes); - free_data_area(udev, copy_bytes); + sg_remaining -= copy_bytes; + block_remaining -= copy_bytes; } kunmap_atomic(to - sg->offset); } } +static inline size_t spc_bitmap_free(unsigned long *bitmap) +{ + return DATA_BLOCK_SIZE * (DATA_BLOCK_BITS - + bitmap_weight(bitmap, DATA_BLOCK_BITS)); +} + /* * We can't queue a command until we have space available on the cmd ring *and* * space available on the data ring. @@ -380,10 +380,10 @@ static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_size, size_t d return false; } - space = spc_free(udev->data_head, udev->data_tail, udev->data_size); + space = spc_bitmap_free(udev->data_bitmap); if (space < data_needed) { - pr_debug("no data space: %zu %zu %zu\n", udev->data_head, - udev->data_tail, udev->data_size); + pr_debug("no data space: only %lu available, but ask for %lu\n", + space, data_needed); return false; } @@ -402,6 +402,8 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) uint32_t cmd_head; uint64_t cdb_off; bool copy_to_data_area; + size_t data_length; + DECLARE_BITMAP(old_bitmap, DATA_BLOCK_BITS); if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) return -EINVAL; @@ -410,10 +412,12 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) * Must be a certain minimum size for response sense info, but * also may be larger if the iov array is large. * - * 3 iovs since we can describe the whole continuous are using one - * for data, one for bidi and one more in the case of wrap. + * We prepare way too many iovs for potential uses here, because it's + * expensive to tell how many regions are freed in the bitmap */ - base_command_size = max(offsetof(struct tcmu_cmd_entry, req.iov[3]), + base_command_size = max(offsetof(struct tcmu_cmd_entry, + req.iov[se_cmd->t_bidi_data_nents + + se_cmd->t_data_nents]), sizeof(struct tcmu_cmd_entry)); command_size = base_command_size + round_up(scsi_command_size(se_cmd->t_task_cdb), TCMU_OP_ALIGN_SIZE); @@ -424,13 +428,18 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) mb = udev->mb_addr; cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ + data_length = se_cmd->data_length; + if (se_cmd->se_cmd_flags & SCF_BIDI) { + BUG_ON(!(se_cmd->t_bidi_data_sg && se_cmd->t_bidi_data_nents)); + data_length += se_cmd->t_bidi_data_sg->length; + } if ((command_size > (udev->cmdr_size / 2)) - || tcmu_cmd->data_length > (udev->data_size - 1)) + || data_length > udev->data_size) pr_warn("TCMU: Request of size %zu/%zu may be too big for %u/%zu " - "cmd/data ring buffers\n", command_size, tcmu_cmd->data_length, + "cmd/data ring buffers\n", command_size, data_length, udev->cmdr_size, udev->data_size); - while (!is_ring_space_avail(udev, command_size, tcmu_cmd->data_length)) { + while (!is_ring_space_avail(udev, command_size, data_length)) { int ret; DEFINE_WAIT(__wait); @@ -477,6 +486,8 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) entry->hdr.kflags = 0; entry->hdr.uflags = 0; + bitmap_copy(old_bitmap, udev->data_bitmap, DATA_BLOCK_BITS); + /* * Fix up iovecs, and handle if allocation in data ring wrapped. */ @@ -495,6 +506,10 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) se_cmd->t_bidi_data_nents, &iov, &iov_cnt, false); entry->req.iov_bidi_cnt = iov_cnt; + /* cmd's data_bitmap is what changed in process */ + bitmap_xor(tcmu_cmd->data_bitmap, old_bitmap, udev->data_bitmap, + DATA_BLOCK_BITS); + /* All offsets relative to mb_addr, not start of entry! */ cdb_off = CMDR_OFF + cmd_head + base_command_size; memcpy((void *) mb + cdb_off, se_cmd->t_task_cdb, scsi_command_size(se_cmd->t_task_cdb)); @@ -547,31 +562,36 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { /* cmd has been completed already from timeout, just reclaim data ring space */ - free_data_area(udev, cmd->data_length); + free_data_area(udev, cmd); return; } if (entry->hdr.uflags & TCMU_UFLAG_UNKNOWN_OP) { - free_data_area(udev, cmd->data_length); + free_data_area(udev, cmd); pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n", cmd->se_cmd); entry->rsp.scsi_status = SAM_STAT_CHECK_CONDITION; } else if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, se_cmd->scsi_sense_length); - free_data_area(udev, cmd->data_length); + free_data_area(udev, cmd); } else if (se_cmd->se_cmd_flags & SCF_BIDI) { - /* Discard data_out buffer */ - free_data_area(udev, (size_t)se_cmd->t_data_sg->length); + DECLARE_BITMAP(bitmap, DATA_BLOCK_BITS); - /* Get Data-In buffer */ - gather_and_free_data_area(udev, + /* Get Data-In buffer before clean up */ + bitmap_copy(bitmap, cmd->data_bitmap, DATA_BLOCK_BITS); + gather_data_area(udev, bitmap, se_cmd->t_bidi_data_sg, se_cmd->t_bidi_data_nents); + free_data_area(udev, cmd); } else if (se_cmd->data_direction == DMA_FROM_DEVICE) { - gather_and_free_data_area(udev, + DECLARE_BITMAP(bitmap, DATA_BLOCK_BITS); + + bitmap_copy(bitmap, cmd->data_bitmap, DATA_BLOCK_BITS); + gather_data_area(udev, bitmap, se_cmd->t_data_sg, se_cmd->t_data_nents); + free_data_area(udev, cmd); } else if (se_cmd->data_direction == DMA_TO_DEVICE) { - free_data_area(udev, cmd->data_length); + free_data_area(udev, cmd); } else if (se_cmd->data_direction != DMA_NONE) { pr_warn("TCMU: data direction was %d!\n", se_cmd->data_direction); @@ -912,6 +932,7 @@ static int tcmu_configure_device(struct se_device *dev) WARN_ON(!PAGE_ALIGNED(udev->data_off)); WARN_ON(udev->data_size % PAGE_SIZE); + WARN_ON(udev->data_size % DATA_BLOCK_SIZE); info->version = __stringify(TCMU_MAILBOX_VERSION); -- cgit v1.2.3 From b25c786399367b9a8bd955d8496669d019409bec Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Fri, 26 Feb 2016 14:59:58 -0800 Subject: target/user: Don't free expired command when time out Which would result in NPE after when userspace connected again. Expired command would be freed either when handling command(by userspace), or when device was tearing down Reviewed-by: Andy Grover Signed-off-by: Sheng Yang Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_user.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers/target/target_core_user.c') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index b1539f357429..d0655913896b 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -560,9 +560,13 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * struct tcmu_dev *udev = cmd->tcmu_dev; if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { - /* cmd has been completed already from timeout, just reclaim data - ring space */ + /* + * cmd has been completed already from timeout, just reclaim + * data ring space and free cmd + */ free_data_area(udev, cmd); + + kmem_cache_free(tcmu_cmd_cache, cmd); return; } @@ -976,12 +980,12 @@ err_vzalloc: return ret; } -static int tcmu_check_pending_cmd(int id, void *p, void *data) +static int tcmu_check_and_free_pending_cmd(struct tcmu_cmd *cmd) { - struct tcmu_cmd *cmd = p; - - if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) + if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { + kmem_cache_free(tcmu_cmd_cache, cmd); return 0; + } return -EINVAL; } @@ -996,6 +1000,8 @@ static void tcmu_dev_call_rcu(struct rcu_head *p) static void tcmu_free_device(struct se_device *dev) { struct tcmu_dev *udev = TCMU_DEV(dev); + struct tcmu_cmd *cmd; + bool all_expired = true; int i; del_timer_sync(&udev->timeout); @@ -1004,10 +1010,13 @@ static void tcmu_free_device(struct se_device *dev) /* Upper layer should drain all requests before calling this */ spin_lock_irq(&udev->commands_lock); - i = idr_for_each(&udev->commands, tcmu_check_pending_cmd, NULL); + idr_for_each_entry(&udev->commands, cmd, i) { + if (tcmu_check_and_free_pending_cmd(cmd) != 0) + all_expired = false; + } idr_destroy(&udev->commands); spin_unlock_irq(&udev->commands_lock); - WARN_ON(i); + WARN_ON(!all_expired); /* Device was configured */ if (udev->uio_info.uio_dev) { -- cgit v1.2.3 From 0241fd39ce7bc9b82b7e57305cb0d6bb1364d45b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sat, 27 Feb 2016 18:25:22 -0800 Subject: target/user: Fix size_t format-spec build warning Fix the following printk size_t warning as per 0-day build: All warnings (new ones prefixed by >>): drivers/target/target_core_user.c: In function 'is_ring_space_avail': >> drivers/target/target_core_user.c:385:12: warning: format '%lu' >> expects argument of type 'long unsigned int', but argument 3 has type >> 'size_t {aka unsigned int}' [-Wformat=] pr_debug("no data space: only %lu available, but ask for %lu\n", ^ Reported-by: kbuild test robot Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_user.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/target/target_core_user.c') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index d0655913896b..fc4789c6067c 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -356,9 +356,8 @@ static inline size_t spc_bitmap_free(unsigned long *bitmap) static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_size, size_t data_needed) { struct tcmu_mailbox *mb = udev->mb_addr; - size_t space; + size_t space, cmd_needed; u32 cmd_head; - size_t cmd_needed; tcmu_flush_dcache_range(mb, sizeof(*mb)); @@ -382,7 +381,7 @@ static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_size, size_t d space = spc_bitmap_free(udev->data_bitmap); if (space < data_needed) { - pr_debug("no data space: only %lu available, but ask for %lu\n", + pr_debug("no data space: only %zu available, but ask for %zu\n", space, data_needed); return false; } -- cgit v1.2.3 From 32c76de3466ed2a875e36c140ac4e3800fdfab6e Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Mon, 29 Feb 2016 16:02:15 -0800 Subject: target/user: Report capability of handling out-of-order completions to userspace TCMU_MAILBOX_FLAG_CAP_OOOC was introduced, and userspace can check the flag for out-of-order completion capability support. Also update the document on how to use the feature. Signed-off-by: Sheng Yang Reviewed-by: Andy Grover Signed-off-by: Nicholas Bellinger --- Documentation/target/tcmu-design.txt | 11 ++++++++++- drivers/target/target_core_user.c | 1 + include/uapi/linux/target_core_user.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/target/target_core_user.c') diff --git a/Documentation/target/tcmu-design.txt b/Documentation/target/tcmu-design.txt index bef81e42788f..4cebc1ebf99a 100644 --- a/Documentation/target/tcmu-design.txt +++ b/Documentation/target/tcmu-design.txt @@ -117,7 +117,9 @@ userspace (respectively) to put commands on the ring, and indicate when the commands are completed. version - 1 (userspace should abort if otherwise) -flags - none yet defined. +flags: +- TCMU_MAILBOX_FLAG_CAP_OOOC: indicates out-of-order completion is + supported. See "The Command Ring" for details. cmdr_off - The offset of the start of the command ring from the start of the memory region, to account for the mailbox size. cmdr_size - The size of the command ring. This does *not* need to be a @@ -162,6 +164,13 @@ rsp.sense_buffer if necessary. Userspace then increments mailbox.cmd_tail by entry.hdr.length (mod cmdr_size) and signals the kernel via the UIO method, a 4-byte write to the file descriptor. +If TCMU_MAILBOX_FLAG_CAP_OOOC is set for mailbox->flags, kernel is +capable of handling out-of-order completions. In this case, userspace can +handle command in different order other than original. Since kernel would +still process the commands in the same order it appeared in the command +ring, userspace need to update the cmd->id when completing the +command(a.k.a steal the original command's entry). + When the opcode is PAD, userspace only updates cmd_tail as above -- it's a no-op. (The kernel inserts PAD entries to ensure each CMD entry is contiguous within the command ring.) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index fc4789c6067c..62bf4fe5704a 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -930,6 +930,7 @@ static int tcmu_configure_device(struct se_device *dev) mb = udev->mb_addr; mb->version = TCMU_MAILBOX_VERSION; + mb->flags = TCMU_MAILBOX_FLAG_CAP_OOOC; mb->cmdr_off = CMDR_OFF; mb->cmdr_size = udev->cmdr_size; diff --git a/include/uapi/linux/target_core_user.h b/include/uapi/linux/target_core_user.h index 95c6521d8a95..c506cddb8165 100644 --- a/include/uapi/linux/target_core_user.h +++ b/include/uapi/linux/target_core_user.h @@ -41,6 +41,7 @@ #define TCMU_MAILBOX_VERSION 2 #define ALIGN_SIZE 64 /* Should be enough for most CPUs */ +#define TCMU_MAILBOX_FLAG_CAP_OOOC (1 << 0) /* Out-of-order completions */ struct tcmu_mailbox { __u16 version; -- cgit v1.2.3