Skip to content

Commit 566c05f

Browse files
digetxthierryreding
authored andcommitted
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: Dmitry Osipenko <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent 26ca88a commit 566c05f

File tree

1 file changed

+15
-12
lines changed

1 file changed

+15
-12
lines changed

drivers/i2c/busses/i2c-tegra.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -996,14 +996,13 @@ tegra_i2c_poll_completion_timeout(struct tegra_i2c_dev *i2c_dev,
996996
do {
997997
u32 status = i2c_readl(i2c_dev, I2C_INT_STATUS);
998998

999-
if (status) {
999+
if (status)
10001000
tegra_i2c_isr(i2c_dev->irq, i2c_dev);
10011001

1002-
if (completion_done(complete)) {
1003-
s64 delta = ktime_ms_delta(ktimeout, ktime);
1002+
if (completion_done(complete)) {
1003+
s64 delta = ktime_ms_delta(ktimeout, ktime);
10041004

1005-
return msecs_to_jiffies(delta) ?: 1;
1006-
}
1005+
return msecs_to_jiffies(delta) ?: 1;
10071006
}
10081007

10091008
ktime = ktime_get();
@@ -1030,14 +1029,18 @@ tegra_i2c_wait_completion_timeout(struct tegra_i2c_dev *i2c_dev,
10301029
disable_irq(i2c_dev->irq);
10311030

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

10431046
return ret;

0 commit comments

Comments
 (0)