msm: mhi_dev: Add UCI support if client req > TRE length

Consider the following issue scenario:
1. Received client write request with size greater than single TRE
element length.
2. mhi_dev_write() is called from UCI layer to handle the request.
3. In mhi_dev_write(), packet is split into multiple packets of TRE
length and is expected to send as multiple packets of TRE length in
loop.
4. The first transfer with TRE length is initiated and MHI received a
completion call back as well.
5. As part of call back, UCI completion callback is called and the
client buffer is cleared.
6. As remaining transfers are not completed, seeing NULL pointer
dereference error while trying to process next transfers in the same
write request.

To handle this scenario from UCI layer, added the support to split the
packets into TRE length if the request size is greater than TRE length.
For this, saving and passing the minimum TRE size to UCI layer as part
of channel doorbell processing.

Change-Id: Id7468967e59effab690dacab6eca02d0f3f8ca2c
Signed-off-by: Sai Chaitanya Kaveti <quic_skaveti@quicinc.com>
This commit is contained in:
Sai Chaitanya Kaveti 2024-01-23 16:50:41 +05:30
commit 52f01d5092
2 changed files with 98 additions and 31 deletions

View file

@ -2278,6 +2278,12 @@ static int mhi_dev_process_tre_ring(struct mhi_dev *mhi,
ch = &mhi->ch[ring->id - mhi->ch_ring_start];
reason.ch_id = ch->ch_id;
reason.reason = MHI_DEV_TRE_AVAILABLE;
/*
* Save lowest value of tre_len to split packets in UCI layer
* for write request of size more than tre_len.
*/
if (!ch->tre_size || ch->tre_size > el->tre.len)
ch->tre_size = el->tre.len;
/* Invoke a callback to let the client know its data is ready.
* Copy this event to the clients context so that it can be
@ -3466,7 +3472,7 @@ int mhi_dev_read_channel(struct mhi_req *mreq)
uint64_t read_from_loc;
ssize_t bytes_read = 0;
size_t write_to_loc = 0;
uint32_t usr_buf_remaining;
uint32_t usr_buf_remaining, tre_size;
int td_done = 0, rc = 0;
struct mhi_dev_client *handle_client;
@ -3509,10 +3515,9 @@ int mhi_dev_read_channel(struct mhi_req *mreq)
}
el = &ring->ring_cache[ring->rd_offset];
mhi_log(MHI_MSG_VERBOSE, "evtptr : 0x%llx\n",
el->tre.data_buf_ptr);
mhi_log(MHI_MSG_VERBOSE, "evntlen : 0x%x, offset:%lu\n",
el->tre.len, ring->rd_offset);
mhi_log(MHI_MSG_VERBOSE,
"TRE.PTR: 0x%llx, TRE.LEN: 0x%x, rd offset: %lu\n",
el->tre.data_buf_ptr, el->tre.len, ring->rd_offset);
if (ch->tre_loc) {
bytes_to_read = min(usr_buf_remaining,
@ -3531,17 +3536,15 @@ int mhi_dev_read_channel(struct mhi_req *mreq)
ch->tre_loc = el->tre.data_buf_ptr;
ch->tre_size = el->tre.len;
ch->tre_bytes_left = ch->tre_size;
mhi_log(MHI_MSG_VERBOSE,
"user_buf_remaining %d, ch->tre_size %d\n",
usr_buf_remaining, ch->tre_size);
bytes_to_read = min(usr_buf_remaining, ch->tre_size);
tre_size = el->tre.len;
ch->tre_bytes_left = el->tre.len;
mhi_log(MHI_MSG_VERBOSE, "user_buf_remaining %d, tre_size %d\n",
usr_buf_remaining, el->tre.len);
bytes_to_read = min(usr_buf_remaining, tre_size);
}
bytes_read += bytes_to_read;
addr_offset = ch->tre_size - ch->tre_bytes_left;
addr_offset = el->tre.len - ch->tre_bytes_left;
read_from_loc = ch->tre_loc + addr_offset;
write_to_loc = (size_t) mreq->buf +
(mreq->len - usr_buf_remaining);

View file

@ -391,6 +391,7 @@ struct uci_client {
int (*read)(struct uci_client *h, int *bytes);
unsigned int tiocm;
unsigned int at_ctrl_mask;
int tre_len;
};
struct mhi_uci_ctxt_t {
@ -1466,8 +1467,9 @@ static ssize_t mhi_uci_client_write(struct file *file,
{
struct uci_client *uci_handle = NULL;
void *data_loc;
const char __user *cur_buf;
unsigned long memcpy_result;
int rc;
int rc = 0, tre_len, cur_rc = 0, count_left, cur_txfr_len;
if (!file || !buf || !count || !file->private_data) {
uci_log(UCI_DBG_DBG, "Invalid access to write\n");
@ -1475,6 +1477,8 @@ static ssize_t mhi_uci_client_write(struct file *file,
}
uci_handle = file->private_data;
tre_len = uci_handle->tre_len;
if (!uci_handle->send || !uci_handle->out_handle) {
uci_log(UCI_DBG_DBG, "Invalid handle or send\n");
return -EINVAL;
@ -1497,17 +1501,47 @@ static ssize_t mhi_uci_client_write(struct file *file,
count, uci_handle->out_chan_attr->max_packet_size);
}
data_loc = kmalloc(count, GFP_KERNEL);
if (!data_loc)
return -ENOMEM;
cur_txfr_len = count;
memcpy_result = copy_from_user(data_loc, buf, count);
if (memcpy_result) {
rc = -EFAULT;
goto error_memcpy;
if (!tre_len)
uci_log(UCI_DBG_ERROR, "tre_len is 0, not updated yet\n");
else if (count > tre_len) {
uci_log(UCI_DBG_DBG, "Write req size (%d) > tre_len (%d)\n", count, tre_len);
cur_txfr_len = tre_len;
}
rc = mhi_uci_send_packet(uci_handle, data_loc, count);
count_left = count;
cur_buf = buf;
do {
data_loc = kmalloc(cur_txfr_len, GFP_KERNEL);
if (!data_loc) {
uci_log(UCI_DBG_ERROR, "Memory allocation failed\n");
return -ENOMEM;
}
memcpy_result = copy_from_user(data_loc, cur_buf, cur_txfr_len);
if (memcpy_result) {
uci_log(UCI_DBG_ERROR, "Mem copy failed\n");
rc = -EFAULT;
goto error_memcpy;
}
cur_rc = mhi_uci_send_packet(uci_handle, data_loc, cur_txfr_len);
if (cur_rc != cur_txfr_len) {
uci_log(UCI_DBG_ERROR,
"Send failed with error %d, after sending %d data\n",
cur_rc, rc);
rc = cur_rc;
goto error_memcpy;
}
rc += cur_rc;
cur_buf += cur_txfr_len;
count_left -= cur_txfr_len;
if (count_left < tre_len)
cur_txfr_len = count_left;
} while (count_left);
if (rc == count)
return rc;
@ -1523,7 +1557,7 @@ static ssize_t mhi_uci_client_write_iter(struct kiocb *iocb,
struct uci_client *uci_handle = NULL;
void *data_loc;
unsigned long memcpy_result;
int rc;
int rc = 0, tre_len, cur_rc = 0, count_left, cur_txfr_len;
struct file *file = iocb->ki_filp;
ssize_t count = iov_iter_count(buf);
@ -1533,6 +1567,8 @@ static ssize_t mhi_uci_client_write_iter(struct kiocb *iocb,
}
uci_handle = file->private_data;
tre_len = uci_handle->tre_len;
if (!uci_handle->send || !uci_handle->out_handle) {
uci_log(UCI_DBG_DBG, "Invalid handle or send\n");
return -EINVAL;
@ -1555,17 +1591,44 @@ static ssize_t mhi_uci_client_write_iter(struct kiocb *iocb,
count, uci_handle->out_chan_attr->max_packet_size);
}
data_loc = kmalloc(count, GFP_KERNEL);
if (!data_loc)
return -ENOMEM;
cur_txfr_len = count;
memcpy_result = copy_from_iter_full(data_loc, count, buf);
if (!memcpy_result) {
rc = -EFAULT;
goto error_memcpy;
if (!tre_len)
uci_log(UCI_DBG_ERROR, "tre_len is 0, not updated yet\n");
else if (count > tre_len) {
uci_log(UCI_DBG_DBG, "Write req size (%d) > tre_len (%d)\n", count, tre_len);
cur_txfr_len = tre_len;
}
rc = mhi_uci_send_packet(uci_handle, data_loc, count);
count_left = count;
do {
data_loc = kmalloc(cur_txfr_len, GFP_KERNEL);
if (!data_loc) {
uci_log(UCI_DBG_ERROR, "Memory allocation failed\n");
return -ENOMEM;
}
memcpy_result = copy_from_iter_full(data_loc, cur_txfr_len, buf);
if (!memcpy_result) {
uci_log(UCI_DBG_ERROR, "Mem copy failed\n");
rc = -EFAULT;
goto error_memcpy;
}
cur_rc = mhi_uci_send_packet(uci_handle, data_loc, cur_txfr_len);
if (cur_rc != cur_txfr_len) {
uci_log(UCI_DBG_ERROR,
"Send failed with error %d, after sending %d data\n",
cur_rc, rc);
rc = cur_rc;
goto error_memcpy;
}
rc += cur_rc;
count_left -= cur_txfr_len;
if (count_left < tre_len)
cur_txfr_len = count_left;
} while (count_left);
if (rc == count)
return rc;
@ -1683,6 +1746,7 @@ static void uci_event_notifier(struct mhi_dev_client_cb_reason *reason)
uci_handle->in_chan);
if (reason->ch_id % 2) {
atomic_set(&uci_handle->write_data_ready, 1);
uci_handle->tre_len = uci_handle->out_handle->channel->tre_size;
wake_up(&uci_handle->write_wq);
} else {
atomic_set(&uci_handle->read_data_ready, 1);