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

Commit 00328f10 authored by Lina Iyer's avatar Lina Iyer
Browse files

drivers: mailbox: qti-tcs: use single tasklet for response



The response object pool is a limited resource. However, when stress
testing the mailbox driver, we quickly run out of the response object in
the pool, because we cannot release the tasklet (member of the response
object) until the object is freed. When sending a response back to
mailbox, the mailbox may pick up the next element in the queue and use
the response thread to send the new request, which may fail if there are
no response objects available causing a deadlock.

To avoid this, use a single tasklet per DRV to send responses. While it
is not as efficient as having multiple tasklets, it avoids the deadlock
condition explained above.

Change-Id: I34f02f3cadb654f1c0f24d2b5f45c6f7af885363
Signed-off-by: default avatarLina Iyer <ilina@codeaurora.org>
parent 6a9b4102
Loading
Loading
Loading
Loading
+59 −22
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>

#include <asm-generic/io.h>

@@ -95,10 +94,10 @@ struct tcs_response {
	struct mbox_chan *chan;
	struct tcs_mbox_msg *msg;
	u32 m; /* m-th TCS */
	struct tasklet_struct tasklet;
	int err;
	int idx;
	bool in_use;
	struct list_head list;
};

struct tcs_response_pool {
@@ -123,15 +122,17 @@ struct tcs_mbox {
/* One per MBOX controller */
struct tcs_drv {
	const char *name;
	void *base; /* start address of the RSC's registers */
	void *reg_base; /* start address for DRV specific register */
	void __iomem *base; /* start address of the RSC's registers */
	void __iomem *reg_base; /* start address for DRV specific register */
	int drv_id;
	struct platform_device *pdev;
	struct mbox_controller mbox;
	struct tcs_mbox tcs[TCS_TYPE_NR];
	int num_assigned;
	int num_tcs;
	struct workqueue_struct *wq;
	struct tasklet_struct tasklet;
	struct list_head response_pending;
	spinlock_t drv_lock;
	struct tcs_response_pool *resp_pool;
	atomic_t tcs_in_use[MAX_POOL_SIZE];
	/* Debug info */
@@ -141,8 +142,6 @@ struct tcs_drv {
	atomic_t tcs_irq_count[MAX_POOL_SIZE];
};

static void tcs_notify_tx_done(unsigned long data);

static int tcs_response_pool_init(struct tcs_drv *drv)
{
	struct tcs_response_pool *pool;
@@ -153,11 +152,10 @@ static int tcs_response_pool_init(struct tcs_drv *drv)
		return -ENOMEM;

	for (i = 0; i < MAX_POOL_SIZE; i++) {
		tasklet_init(&pool->resp[i].tasklet, tcs_notify_tx_done,
						(unsigned long) &pool->resp[i]);
		pool->resp[i].drv = drv;
		pool->resp[i].idx = i;
		pool->resp[i].m = TCS_M_INIT;
		INIT_LIST_HEAD(&pool->resp[i].list);
	}

	spin_lock_init(&pool->lock);
@@ -188,6 +186,9 @@ static struct tcs_response *setup_response(struct tcs_drv *drv,
	}
	spin_unlock_irqrestore(&pool->lock, flags);

	if (pos == MAX_POOL_SIZE)
		pr_err("response pool is full\n");

	return resp;
}

@@ -364,7 +365,15 @@ static inline struct tcs_mbox *get_tcs_for_msg(struct tcs_drv *drv,

static inline void send_tcs_response(struct tcs_response *resp)
{
	tasklet_schedule(&resp->tasklet);
	struct tcs_drv *drv = resp->drv;
	unsigned long flags;

	spin_lock_irqsave(&drv->drv_lock, flags);
	INIT_LIST_HEAD(&resp->list);
	list_add_tail(&resp->list, &drv->response_pending);
	spin_unlock_irqrestore(&drv->drv_lock, flags);

	tasklet_schedule(&drv->tasklet);
}

static inline void enable_tcs_irq(struct tcs_drv *drv, int m, bool enable)
@@ -455,12 +464,12 @@ static irqreturn_t tcs_irq_handler(int irq, void *p)
		/* Clear the TCS IRQ status */
		write_tcs_reg(base, TCS_DRV_IRQ_CLEAR, 0, 0, BIT(m));

		/* Notify the client that this request is completed. */
		atomic_set(&drv->tcs_in_use[m], 0);

		/* Clean up response object and notify mbox in tasklet */
		if (resp)
			send_tcs_response(resp);

		/* Notify the client that this request is completed. */
		atomic_set(&drv->tcs_in_use[m], 0);
	}

	return IRQ_HANDLED;
@@ -475,19 +484,38 @@ static inline void mbox_notify_tx_done(struct mbox_chan *chan,
	mbox_chan_txdone(chan, err);
}

/**
 * tcs_notify_tx_done: TX Done for requests that do not trigger TCS
 */
static void tcs_notify_tx_done(unsigned long data)
static void respond_tx_done(struct tcs_response *resp)
{
	struct tcs_response *resp = (struct tcs_response *) data;
	struct mbox_chan *chan = resp->chan;
	struct tcs_mbox_msg *msg = resp->msg;
	int err = resp->err;
	int m = resp->m;

	mbox_notify_tx_done(chan, msg, m, err);
	free_response(resp);
	mbox_notify_tx_done(chan, msg, m, err);
}

/**
 * tcs_notify_tx_done: TX Done for requests that do not trigger TCS
 */
static void tcs_notify_tx_done(unsigned long data)
{
	struct tcs_drv *drv = (struct tcs_drv *)data;
	struct tcs_response *resp;
	unsigned long flags;

	do {
		spin_lock_irqsave(&drv->drv_lock, flags);
		if (list_empty(&drv->response_pending)) {
			spin_unlock_irqrestore(&drv->drv_lock, flags);
			break;
		}
		resp = list_first_entry(&drv->response_pending,
					struct tcs_response, list);
		list_del(&resp->list);
		spin_unlock_irqrestore(&drv->drv_lock, flags);
		respond_tx_done(resp);
	} while (1);
}

static void __tcs_buffer_write(struct tcs_drv *drv, int d, int m, int n,
@@ -673,8 +701,11 @@ static int tcs_mbox_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg,
	if (IS_ERR(tcs))
		return PTR_ERR(tcs);

	if (trigger)
	if (trigger) {
		resp = setup_response(drv, msg, chan, TCS_M_INIT, 0);
		if (IS_ERR_OR_NULL(resp))
			return -EBUSY;
	}

	/* Identify the sequential slots that we can write to */
	spin_lock_irqsave(&tcs->tcs_lock, flags);
@@ -690,6 +721,8 @@ static int tcs_mbox_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg,
		ret = check_for_req_inflight(drv, tcs, msg);
		if (ret) {
			spin_unlock_irqrestore(&tcs->tcs_lock, flags);
			if (resp)
				free_response(resp);
			return ret;
		}
	}
@@ -840,7 +873,7 @@ static int chan_tcs_write(struct mbox_chan *chan, void *data)
		if (ret != -EBUSY)
			break;
		udelay(100);
	} while (++count < 10);
	} while (++count < 100);

tx_fail:
	/* If there was an error in the request, schedule a response */
@@ -849,6 +882,7 @@ static int chan_tcs_write(struct mbox_chan *chan, void *data)
				drv, msg, chan, TCS_M_INIT, ret);

		dev_err(dev, "Error sending RPMH message %d\n", ret);
		if (resp)
			send_tcs_response(resp);
		ret = 0;
	}
@@ -1108,6 +1142,9 @@ static int tcs_drv_probe(struct platform_device *pdev)
	drv->mbox.is_idle = tcs_drv_is_idle;
	drv->num_tcs = st;
	drv->pdev = pdev;
	INIT_LIST_HEAD(&drv->response_pending);
	spin_lock_init(&drv->drv_lock);
	tasklet_init(&drv->tasklet, tcs_notify_tx_done, (unsigned long)drv);

	drv->name = of_get_property(pdev->dev.of_node, "label", NULL);
	if (!drv->name)