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

Commit 271d8476 authored by Siva Kumar Akkireddi's avatar Siva Kumar Akkireddi Committed by Gerrit - the friendly Code Review server
Browse files

msm: mhi_dev: Avoid race in event ring updates



Event ring can be shared by multiple channels. Synchronize
completion event updates to the event ring to avoid race
condition when the same ring is updated in the read and
write path.

Change-Id: Ib626bb45206766f34e7ae5224148b8dea962fc11
Signed-off-by: default avatarSiva Kumar Akkireddi <sivaa@codeaurora.org>
parent c5ac9792
Loading
Loading
Loading
Loading
+44 −10
Original line number Diff line number Diff line
@@ -323,6 +323,7 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
		return -EINVAL;
	}

	mutex_lock(&ring->event_lock);
	mhi_log(MHI_MSG_VERBOSE, "Flushing %d cmpl events of ch %d\n",
			ereq->num_events, ch->ch_id);
	/* add the events */
@@ -331,6 +332,7 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
	rc = mhi_dev_add_element(ring, ereq->tr_events, ereq, evt_len);
	if (rc) {
		pr_err("%s(): error in adding element rc %d\n", __func__, rc);
		mutex_unlock(&ring->event_lock);
		return rc;
	}
	ring->ring_ctx_shadow->ev.rp = (ring->rd_offset *
@@ -366,6 +368,7 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
	ereq->client_cb = mhi_dev_event_rd_offset_completion_cb;
	ereq->event_ring = evnt_ring;
	mhi_ctx->write_to_host(mhi, &transfer_addr, ereq, MHI_DEV_DMA_ASYNC);
	mutex_unlock(&ring->event_lock);
	return rc;
}

@@ -1393,6 +1396,37 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring,
	return ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec);
}

static int mhi_dev_send_completion_event_async(struct mhi_dev_channel *ch,
			size_t rd_ofst, uint32_t len,
			enum mhi_dev_cmd_completion_code code,
			struct mhi_req *mreq)
{
	int rc;
	struct mhi_dev *mhi = ch->ring->mhi_dev;

	mhi_log(MHI_MSG_VERBOSE, "Ch %d\n", ch->ch_id);

	/* Queue the completion event for the current transfer */
	mreq->snd_cmpl = 1;
	rc = mhi_dev_queue_transfer_completion(mreq, NULL);
	if (rc) {
		mhi_log(MHI_MSG_ERROR,
			"Failed to queue completion for ch %d, rc %d\n",
			ch->ch_id, rc);
		return rc;
	}

	mhi_log(MHI_MSG_VERBOSE, "Calling flush for ch %d\n", ch->ch_id);
	rc = mhi_dev_flush_transfer_completion_events(mhi, ch);
	if (rc) {
		mhi_log(MHI_MSG_ERROR,
			"Failed to flush read completions to host\n");
		return rc;
	}

	return 0;
}

static int mhi_dev_send_completion_event(struct mhi_dev_channel *ch,
			size_t rd_ofst, uint32_t len,
			enum mhi_dev_cmd_completion_code code)
@@ -2699,7 +2733,7 @@ EXPORT_SYMBOL(mhi_dev_close_channel);

static int mhi_dev_check_tre_bytes_left(struct mhi_dev_channel *ch,
		struct mhi_dev_ring *ring, union mhi_dev_ring_element_type *el,
		uint32_t *chain)
		struct mhi_req *mreq)
{
	uint32_t td_done = 0;

@@ -2710,17 +2744,17 @@ static int mhi_dev_check_tre_bytes_left(struct mhi_dev_channel *ch,
	if (ch->tre_bytes_left == 0) {
		if (el->tre.chain) {
			if (el->tre.ieob)
				mhi_dev_send_completion_event(ch,
				mhi_dev_send_completion_event_async(ch,
				ring->rd_offset, el->tre.len,
					MHI_CMD_COMPL_CODE_EOB);
			*chain = 1;
				MHI_CMD_COMPL_CODE_EOB, mreq);
			mreq->chain = 1;
		} else {
			if (el->tre.ieot)
				mhi_dev_send_completion_event(
				mhi_dev_send_completion_event_async(
				ch, ring->rd_offset, el->tre.len,
					MHI_CMD_COMPL_CODE_EOT);
				MHI_CMD_COMPL_CODE_EOT, mreq);
			td_done = 1;
			*chain = 0;
			mreq->chain = 0;
		}
		mhi_dev_ring_inc_index(ring, ring->rd_offset);
		ch->tre_bytes_left = 0;
@@ -2831,7 +2865,7 @@ int mhi_dev_read_channel(struct mhi_req *mreq)
			goto exit;
		} else {
			td_done = mhi_dev_check_tre_bytes_left(ch, ring,
					el, &mreq->chain);
					el, mreq);
		}
	} while (usr_buf_remaining  && !td_done);
	if (td_done && ch->state == MHI_DEV_CH_PENDING_STOP) {
+5 −1
Original line number Diff line number Diff line
@@ -382,7 +382,11 @@ struct mhi_dev_ring {

	enum mhi_dev_ring_type			type;
	enum mhi_dev_ring_state			state;

	/*
	 * Lock to prevent race in updating event ring
	 * which is shared by multiple channels
	 */
	struct mutex	event_lock;
	/* device virtual address location of the cached host ring ctx data */
	union mhi_dev_ring_element_type		*ring_cache;
	/* Physical address of the cached ring copy on the device side */
+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -453,6 +453,7 @@ void mhi_ring_init(struct mhi_dev_ring *ring, enum mhi_dev_ring_type type,
	ring->state = RING_STATE_UINT;
	ring->ring_cb = NULL;
	ring->type = type;
	mutex_init(&ring->event_lock);
}
EXPORT_SYMBOL(mhi_ring_init);