Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52f01d50 authored by Sai Chaitanya Kaveti's avatar Sai Chaitanya Kaveti
Browse files

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: default avatarSai Chaitanya Kaveti <quic_skaveti@quicinc.com>
parent d44f01f4
Loading
Loading
Loading
Loading
+16 −13
Original line number Original line Diff line number Diff line
@@ -2278,6 +2278,12 @@ static int mhi_dev_process_tre_ring(struct mhi_dev *mhi,
	ch = &mhi->ch[ring->id - mhi->ch_ring_start];
	ch = &mhi->ch[ring->id - mhi->ch_ring_start];
	reason.ch_id = ch->ch_id;
	reason.ch_id = ch->ch_id;
	reason.reason = MHI_DEV_TRE_AVAILABLE;
	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.
	/* Invoke a callback to let the client know its data is ready.
	 * Copy this event to the clients context so that it can be
	 * 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;
	uint64_t read_from_loc;
	ssize_t bytes_read = 0;
	ssize_t bytes_read = 0;
	size_t write_to_loc = 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;
	int td_done = 0, rc = 0;
	struct mhi_dev_client *handle_client;
	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];
		el = &ring->ring_cache[ring->rd_offset];
		mhi_log(MHI_MSG_VERBOSE, "evtptr : 0x%llx\n",
		mhi_log(MHI_MSG_VERBOSE,
						el->tre.data_buf_ptr);
				"TRE.PTR: 0x%llx, TRE.LEN: 0x%x, rd offset: %lu\n",
		mhi_log(MHI_MSG_VERBOSE, "evntlen : 0x%x, offset:%lu\n",
				el->tre.data_buf_ptr, el->tre.len, ring->rd_offset);
						el->tre.len, ring->rd_offset);


		if (ch->tre_loc) {
		if (ch->tre_loc) {
			bytes_to_read = min(usr_buf_remaining,
			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_loc = el->tre.data_buf_ptr;
			ch->tre_size = el->tre.len;
			tre_size = el->tre.len;
			ch->tre_bytes_left = ch->tre_size;
			ch->tre_bytes_left = el->tre.len;

			mhi_log(MHI_MSG_VERBOSE, "user_buf_remaining %d, tre_size %d\n",
			mhi_log(MHI_MSG_VERBOSE,
					usr_buf_remaining, el->tre.len);
			"user_buf_remaining %d, ch->tre_size %d\n",
			bytes_to_read = min(usr_buf_remaining, tre_size);
			usr_buf_remaining, ch->tre_size);
			bytes_to_read = min(usr_buf_remaining, ch->tre_size);
		}
		}


		bytes_read += bytes_to_read;
		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;
		read_from_loc = ch->tre_loc + addr_offset;
		write_to_loc = (size_t) mreq->buf +
		write_to_loc = (size_t) mreq->buf +
			(mreq->len - usr_buf_remaining);
			(mreq->len - usr_buf_remaining);
+82 −18
Original line number Original line Diff line number Diff line
@@ -391,6 +391,7 @@ struct uci_client {
	int (*read)(struct uci_client *h, int *bytes);
	int (*read)(struct uci_client *h, int *bytes);
	unsigned int tiocm;
	unsigned int tiocm;
	unsigned int at_ctrl_mask;
	unsigned int at_ctrl_mask;
	int tre_len;
};
};


struct mhi_uci_ctxt_t {
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;
	struct uci_client *uci_handle = NULL;
	void *data_loc;
	void *data_loc;
	const char __user *cur_buf;
	unsigned long memcpy_result;
	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) {
	if (!file || !buf || !count || !file->private_data) {
		uci_log(UCI_DBG_DBG, "Invalid access to write\n");
		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;
	uci_handle = file->private_data;
	tre_len = uci_handle->tre_len;

	if (!uci_handle->send || !uci_handle->out_handle) {
	if (!uci_handle->send || !uci_handle->out_handle) {
		uci_log(UCI_DBG_DBG, "Invalid handle or send\n");
		uci_log(UCI_DBG_DBG, "Invalid handle or send\n");
		return -EINVAL;
		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);
			count, uci_handle->out_chan_attr->max_packet_size);
	}
	}


	data_loc = kmalloc(count, GFP_KERNEL);
	cur_txfr_len = count;
	if (!data_loc)

	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;
	}

	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;
			return -ENOMEM;
		}


	memcpy_result = copy_from_user(data_loc, buf, count);
		memcpy_result = copy_from_user(data_loc, cur_buf, cur_txfr_len);
		if (memcpy_result) {
		if (memcpy_result) {
			uci_log(UCI_DBG_ERROR, "Mem copy failed\n");
			rc = -EFAULT;
			rc = -EFAULT;
			goto error_memcpy;
			goto error_memcpy;
		}
		}


	rc = mhi_uci_send_packet(uci_handle, data_loc, count);
		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)
	if (rc == count)
		return rc;
		return rc;


@@ -1523,7 +1557,7 @@ static ssize_t mhi_uci_client_write_iter(struct kiocb *iocb,
	struct uci_client *uci_handle = NULL;
	struct uci_client *uci_handle = NULL;
	void *data_loc;
	void *data_loc;
	unsigned long memcpy_result;
	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;
	struct file *file = iocb->ki_filp;
	ssize_t count = iov_iter_count(buf);
	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;
	uci_handle = file->private_data;
	tre_len = uci_handle->tre_len;

	if (!uci_handle->send || !uci_handle->out_handle) {
	if (!uci_handle->send || !uci_handle->out_handle) {
		uci_log(UCI_DBG_DBG, "Invalid handle or send\n");
		uci_log(UCI_DBG_DBG, "Invalid handle or send\n");
		return -EINVAL;
		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);
			count, uci_handle->out_chan_attr->max_packet_size);
	}
	}


	data_loc = kmalloc(count, GFP_KERNEL);
	cur_txfr_len = count;
	if (!data_loc)

	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;
	}

	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;
			return -ENOMEM;
		}


	memcpy_result = copy_from_iter_full(data_loc, count, buf);
		memcpy_result = copy_from_iter_full(data_loc, cur_txfr_len, buf);
		if (!memcpy_result) {
		if (!memcpy_result) {
			uci_log(UCI_DBG_ERROR, "Mem copy failed\n");
			rc = -EFAULT;
			rc = -EFAULT;
			goto error_memcpy;
			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);


	rc = mhi_uci_send_packet(uci_handle, data_loc, count);
	if (rc == count)
	if (rc == count)
		return rc;
		return rc;


@@ -1683,6 +1746,7 @@ static void uci_event_notifier(struct mhi_dev_client_cb_reason *reason)
			uci_handle->in_chan);
			uci_handle->in_chan);
		if (reason->ch_id % 2) {
		if (reason->ch_id % 2) {
			atomic_set(&uci_handle->write_data_ready, 1);
			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);
			wake_up(&uci_handle->write_wq);
		} else {
		} else {
			atomic_set(&uci_handle->read_data_ready, 1);
			atomic_set(&uci_handle->read_data_ready, 1);