Commit a900aeac authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Wolfram Sang
Browse files

i2c: tegra: Better handle case where CPU0 is busy for a long time



Boot CPU0 always handle I2C interrupt and under some rare circumstances
(like running KASAN + NFS root) it may stuck in uninterruptible state for
a significant time. In this case we will get timeout if I2C transfer is
running on a sibling CPU, despite of IRQ being raised. In order to handle
this rare condition, the IRQ status needs to be checked after completion
timeout.

Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 3c1d1613
Loading
Loading
Loading
Loading
+15 −12
Original line number Diff line number Diff line
@@ -996,7 +996,7 @@ tegra_i2c_poll_completion_timeout(struct tegra_i2c_dev *i2c_dev,
	do {
		u32 status = i2c_readl(i2c_dev, I2C_INT_STATUS);

		if (status) {
		if (status)
			tegra_i2c_isr(i2c_dev->irq, i2c_dev);

		if (completion_done(complete)) {
@@ -1004,7 +1004,6 @@ tegra_i2c_poll_completion_timeout(struct tegra_i2c_dev *i2c_dev,

			return msecs_to_jiffies(delta) ?: 1;
		}
		}

		ktime = ktime_get();

@@ -1030,14 +1029,18 @@ tegra_i2c_wait_completion_timeout(struct tegra_i2c_dev *i2c_dev,
		disable_irq(i2c_dev->irq);

		/*
		 * There is a chance that completion may happen after IRQ
		 * synchronization, which is done by disable_irq().
		 * Under some rare circumstances (like running KASAN +
		 * NFS root) CPU, which handles interrupt, may stuck in
		 * uninterruptible state for a significant time.  In this
		 * case we will get timeout if I2C transfer is running on
		 * a sibling CPU, despite of IRQ being raised.
		 *
		 * In order to handle this rare condition, the IRQ status
		 * needs to be checked after timeout.
		 */
		if (ret == 0 && completion_done(complete)) {
			dev_warn(i2c_dev->dev,
				 "completion done after timeout\n");
			ret = 1;
		}
		if (ret == 0)
			ret = tegra_i2c_poll_completion_timeout(i2c_dev,
								complete, 0);
	}

	return ret;