Commit 6bf3fc26 authored by Frank Li's avatar Frank Li Committed by Alexandre Belloni
Browse files

i3c: master: svc: fix race condition in ibi work thread



The ibi work thread operates asynchronously with other transfers, such as
svc_i3c_master_priv_xfers(). Introduce mutex protection to ensure the
completion of the entire i3c/i2c transaction.

Fixes: dd3c5284 ("i3c: master: svc: Add Silvaco I3C master driver")
Cc:  <stable@vger.kernel.org>
Reviewed-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: default avatarFrank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20231023161658.3890811-2-Frank.Li@nxp.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 57ec42b9
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -175,6 +175,7 @@ struct svc_i3c_regs_save {
 * @ibi.slots: Available IBI slots
 * @ibi.tbq_slot: To be queued IBI slot
 * @ibi.lock: IBI lock
 * @lock: Transfer lock, protect between IBI work thread and callbacks from master
 */
struct svc_i3c_master {
	struct i3c_master_controller base;
@@ -203,6 +204,7 @@ struct svc_i3c_master {
		/* Prevent races within IBI handlers */
		spinlock_t lock;
	} ibi;
	struct mutex lock;
};

/**
@@ -384,6 +386,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
	u32 status, val;
	int ret;

	mutex_lock(&master->lock);
	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
	       SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -460,6 +463,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)

reenable_ibis:
	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
	mutex_unlock(&master->lock);
}

static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
@@ -1204,9 +1208,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
	cmd->read_len = 0;
	cmd->continued = false;

	mutex_lock(&master->lock);
	svc_i3c_master_enqueue_xfer(master, xfer);
	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
		svc_i3c_master_dequeue_xfer(master, xfer);
	mutex_unlock(&master->lock);

	ret = xfer->ret;
	kfree(buf);
@@ -1250,9 +1256,11 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
	cmd->read_len = read_len;
	cmd->continued = false;

	mutex_lock(&master->lock);
	svc_i3c_master_enqueue_xfer(master, xfer);
	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
		svc_i3c_master_dequeue_xfer(master, xfer);
	mutex_unlock(&master->lock);

	if (cmd->read_len != xfer_len)
		ccc->dests[0].payload.len = cmd->read_len;
@@ -1309,9 +1317,11 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
		cmd->continued = (i + 1) < nxfers;
	}

	mutex_lock(&master->lock);
	svc_i3c_master_enqueue_xfer(master, xfer);
	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
		svc_i3c_master_dequeue_xfer(master, xfer);
	mutex_unlock(&master->lock);

	ret = xfer->ret;
	svc_i3c_master_free_xfer(xfer);
@@ -1347,9 +1357,11 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
		cmd->continued = (i + 1 < nxfers);
	}

	mutex_lock(&master->lock);
	svc_i3c_master_enqueue_xfer(master, xfer);
	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
		svc_i3c_master_dequeue_xfer(master, xfer);
	mutex_unlock(&master->lock);

	ret = xfer->ret;
	svc_i3c_master_free_xfer(xfer);
@@ -1540,6 +1552,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)

	INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
	INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
	mutex_init(&master->lock);

	ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
			       IRQF_NO_SUSPEND, "svc-i3c-irq", master);
	if (ret)