Skip to content

Commit c358ebf

Browse files
LyudeBen Skeggs
authored andcommitted
drm/nouveau: Don't retry infinitely when receiving no data on i2c over AUX
While I had thought I had fixed this issue in: commit 342406e ("drm/nouveau/i2c: Disable i2c bus access after ->fini()") It turns out that while I did fix the error messages I was seeing on my P50 when trying to access i2c busses with the GPU in runtime suspend, I accidentally had missed one important detail that was mentioned on the bug report this commit was supposed to fix: that the CPU would only lock up when trying to access i2c busses _on connected devices_ _while the GPU is not in runtime suspend_. Whoops. That definitely explains why I was not able to get my machine to hang with i2c bus interactions until now, as plugging my P50 into it's dock with an HDMI monitor connected allowed me to finally reproduce this locally. Now that I have managed to reproduce this issue properly, it looks like the problem is much simpler then it looks. It turns out that some connected devices, such as MST laptop docks, will actually ACK i2c reads even if no data was actually read: [ 275.063043] nouveau 0000:01:00.0: i2c: aux 000a: 1: 0000004c 1 [ 275.063447] nouveau 0000:01:00.0: i2c: aux 000a: 00 01101000 10040000 [ 275.063759] nouveau 0000:01:00.0: i2c: aux 000a: rd 00000001 [ 275.064024] nouveau 0000:01:00.0: i2c: aux 000a: rd 00000000 [ 275.064285] nouveau 0000:01:00.0: i2c: aux 000a: rd 00000000 [ 275.064594] nouveau 0000:01:00.0: i2c: aux 000a: rd 00000000 Because we don't handle the situation of i2c ack without any data, we end up entering an infinite loop in nvkm_i2c_aux_i2c_xfer() since the value of cnt always remains at 0. This finally properly explains how this could result in a CPU hang like the ones observed in the aforementioned commit. So, fix this by retrying transactions if no data is written or received, and give up and fail the transaction if we continue to not write or receive any data after 32 retries. Signed-off-by: Lyude Paul <[email protected]> Cc: [email protected] Signed-off-by: Ben Skeggs <[email protected]>
1 parent 4d352db commit c358ebf

File tree

1 file changed

+17
-7
lines changed
  • drivers/gpu/drm/nouveau/nvkm/subdev/i2c

1 file changed

+17
-7
lines changed

drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
4040
u8 *ptr = msg->buf;
4141

4242
while (remaining) {
43-
u8 cnt = (remaining > 16) ? 16 : remaining;
44-
u8 cmd;
43+
u8 cnt, retries, cmd;
4544

4645
if (msg->flags & I2C_M_RD)
4746
cmd = 1;
@@ -51,10 +50,19 @@ nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
5150
if (mcnt || remaining > 16)
5251
cmd |= 4; /* MOT */
5352

54-
ret = aux->func->xfer(aux, true, cmd, msg->addr, ptr, &cnt);
55-
if (ret < 0) {
56-
nvkm_i2c_aux_release(aux);
57-
return ret;
53+
for (retries = 0, cnt = 0;
54+
retries < 32 && !cnt;
55+
retries++) {
56+
cnt = min_t(u8, remaining, 16);
57+
ret = aux->func->xfer(aux, true, cmd,
58+
msg->addr, ptr, &cnt);
59+
if (ret < 0)
60+
goto out;
61+
}
62+
if (!cnt) {
63+
AUX_TRACE(aux, "no data after 32 retries");
64+
ret = -EIO;
65+
goto out;
5866
}
5967

6068
ptr += cnt;
@@ -64,8 +72,10 @@ nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
6472
msg++;
6573
}
6674

75+
ret = num;
76+
out:
6777
nvkm_i2c_aux_release(aux);
68-
return num;
78+
return ret;
6979
}
7080

7181
static u32

0 commit comments

Comments
 (0)