From 17aec3c9ff4e78a16e54204af235ab78335d9ed6 Mon Sep 17 00:00:00 2001 From: Sujeev Dias Date: Thu, 29 Jun 2017 21:16:30 -0700 Subject: [PATCH] mhi: uci: Fix a bug related to split transfer packets When transfer buffer is larger than available space, uci driver will split the transfer into multiple transactions. Driver incorrectly calculated the transfer length and caused infinite transfer. Simplify uci write method to avoid such bugs. CRs-Fixed: 2083693 Change-Id: Ic7169cefda6a4637511ecfa3ce5ddde6f3d55f8c Signed-off-by: Sujeev Dias --- drivers/platform/msm/mhi_uci/mhi_uci.c | 113 ++++++++----------------- 1 file changed, 35 insertions(+), 78 deletions(-) diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c index 4563810d7e5b..5b50666d30a2 100644 --- a/drivers/platform/msm/mhi_uci/mhi_uci.c +++ b/drivers/platform/msm/mhi_uci/mhi_uci.c @@ -326,73 +326,6 @@ static int mhi_init_inbound(struct uci_client *client_handle) return ret_val; } -static int mhi_uci_send_packet(struct mhi_client_handle **client_handle, - void *buf, - u32 size) -{ - u32 nr_avail_trbs = 0; - u32 i = 0; - void *data_loc = NULL; - unsigned long memcpy_result = 0; - int data_left_to_insert = 0; - size_t data_to_insert_now = 0; - u32 data_inserted_so_far = 0; - int ret_val = 0; - struct uci_client *uci_handle; - struct uci_buf *uci_buf; - - uci_handle = container_of(client_handle, struct uci_client, - out_attr.mhi_handle); - - nr_avail_trbs = atomic_read(&uci_handle->out_attr.avail_pkts); - data_left_to_insert = size; - - for (i = 0; i < nr_avail_trbs; ++i) { - data_to_insert_now = min_t(size_t, data_left_to_insert, - uci_handle->out_attr.max_packet_size); - data_loc = kmalloc(data_to_insert_now + sizeof(*uci_buf), - GFP_KERNEL); - if (!data_loc) { - uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE, - "Failed to allocate memory 0x%zx\n", - data_to_insert_now); - return -ENOMEM; - } - uci_buf = data_loc + data_to_insert_now; - uci_buf->data = data_loc; - uci_buf->pkt_id = uci_handle->out_attr.pkt_count++; - memcpy_result = copy_from_user(uci_buf->data, - buf + data_inserted_so_far, - data_to_insert_now); - if (memcpy_result) - goto error_xfer; - - uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE, - "At trb i = %d/%d, size = %lu, id %llu chan %d\n", - i, nr_avail_trbs, data_to_insert_now, uci_buf->pkt_id, - uci_handle->out_attr.chan_id); - ret_val = mhi_queue_xfer(*client_handle, uci_buf->data, - data_to_insert_now, MHI_EOT); - if (ret_val) { - goto error_xfer; - } else { - data_left_to_insert -= data_to_insert_now; - data_inserted_so_far += data_to_insert_now; - atomic_inc(&uci_handle->out_pkt_pend_ack); - atomic_dec(&uci_handle->out_attr.avail_pkts); - list_add_tail(&uci_buf->node, - &uci_handle->out_attr.buf_head); - } - if (!data_left_to_insert) - break; - } - return data_inserted_so_far; - -error_xfer: - kfree(uci_buf->data); - return data_inserted_so_far; -} - static int mhi_uci_send_status_cmd(struct uci_client *client) { void *buf = NULL; @@ -963,18 +896,11 @@ static ssize_t mhi_uci_client_write(struct file *file, goto sys_interrupt; } - while (bytes_transferrd != count) { - ret_val = mhi_uci_send_packet(&chan_attr->mhi_handle, - (void *)buf, count); - if (ret_val < 0) - goto sys_interrupt; + while (count) { + size_t xfer_size; + void *data_loc = NULL; + struct uci_buf *uci_buf; - bytes_transferrd += ret_val; - if (bytes_transferrd == count) - break; - uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE, - "No descriptors available, did we poll, chan %d?\n", - chan); mutex_unlock(&chan_attr->chan_lock); ret_val = wait_event_interruptible(chan_attr->wq, (atomic_read(&chan_attr->avail_pkts) || @@ -991,6 +917,37 @@ static ssize_t mhi_uci_client_write(struct file *file, ret_val = -ERESTARTSYS; goto sys_interrupt; } + + xfer_size = min_t(size_t, count, chan_attr->max_packet_size); + data_loc = kmalloc(xfer_size + sizeof(*uci_buf), GFP_KERNEL); + if (!data_loc) { + uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE, + "Failed to allocate memory %lu\n", xfer_size); + ret_val = -ENOMEM; + goto sys_interrupt; + } + + uci_buf = data_loc + xfer_size; + uci_buf->data = data_loc; + uci_buf->pkt_id = uci_handle->out_attr.pkt_count++; + ret_val = copy_from_user(uci_buf->data, buf, xfer_size); + if (unlikely(ret_val)) { + kfree(uci_buf->data); + goto sys_interrupt; + } + ret_val = mhi_queue_xfer(chan_attr->mhi_handle, uci_buf->data, + xfer_size, MHI_EOT); + if (unlikely(ret_val)) { + kfree(uci_buf->data); + goto sys_interrupt; + } + + bytes_transferrd += xfer_size; + count -= xfer_size; + buf += xfer_size; + atomic_inc(&uci_handle->out_pkt_pend_ack); + atomic_dec(&uci_handle->out_attr.avail_pkts); + list_add_tail(&uci_buf->node, &uci_handle->out_attr.buf_head); } mutex_unlock(&chan_attr->chan_lock);