I tried to hunt a bit the cause of the problem in kernel code to see how far I
could go.
So, according to the kernel Oopses, the crash occurs in nouveau_fence_sync():
```text
RIP: 0010:nouveau_fence_sync+0x12c/0x1b0 [nouveau]
```
The top of the stack trace being:
```text
nouveau_fence_sync+0x12c/0x1b0 [nouveau]
nouveau_gem_ioctl_pushbuf+0x678/0x1390 [nouveau]
? __pfx_nouveau_gem_ioctl_pushbuf+0x10/0x10 [nouveau]
drm_ioctl_kernel+0xae/0x100 [drm]
drm_ioctl+0x2a8/0x550 [drm]
...
```
nouveau_fence_sync() is implemented in drivers/gpu/drm/nouveau/nouveau_fence.c
as follow:
```c
int
nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
bool exclusive, bool intr)
{
struct nouveau_fence_chan *fctx = chan->fence;
struct dma_resv *resv = nvbo->bo.base.resv;
int i, ret;
ret = dma_resv_reserve_fences(resv, 1);
if (ret)
return ret;
/* Waiting for the writes first causes performance regressions
* under some circumstances. So manually wait for the reads first.
*/
for (i = 0; i < 2; ++i) {
struct dma_resv_iter cursor;
struct dma_fence *fence;
dma_resv_for_each_fence(&cursor, resv,
dma_resv_usage_rw(exclusive),
fence) {
enum dma_resv_usage usage;
struct nouveau_fence *f;
usage = dma_resv_iter_usage(&cursor);
if (i == 0 && usage == DMA_RESV_USAGE_WRITE)
continue;
f = nouveau_local_fence(fence, chan->cli->drm);
if (f) {
struct nouveau_channel *prev;
bool must_wait = true;
bool local;
rcu_read_lock();
prev = rcu_dereference(f->channel);
local = prev && prev->cli->drm ==
chan->cli->drm;
if (local && (prev == chan ||
fctx->sync(f, prev, chan) == 0))
must_wait = false;
rcu_read_unlock();
if (!must_wait)
continue;
}
ret = dma_fence_wait(fence, intr);
if (ret)
return ret;
}
}
return 0;
}
```
Looking at the current executed code, we have:
```text
RIP: 0010:nouveau_fence_sync+0x12c/0x1b0 [nouveau]
Code: 74 08 48 3d 20 dd f2 c0 75 aa e8 bf 94 d1 df 49 8b 76 50 48 85 f6 74 97
48 8b 85 f0 00 00 00 48 8b 96 f0 00 00 00 48 8b 40 40 <48> 39 42 40 0f
85 7b ff ff ff 48 39 f5 74 17 49 8b 47 30 48 89 ea
```
Which can be decompiled (using `rasm2 -d "740848..."`) as:
```asm
je 0xa
cmp rax, 0xffffffffc0f2dd20
jne 0xffffffffffffffb4
call 0xffffffffdfd194ce
mov rsi, qword [r14 + 0x50]
test rsi, rsi
je 0xffffffffffffffaf
mov rax, qword [rbp + 0xf0]
mov rdx, qword [rsi + 0xf0]
mov rax, qword [rax + 0x40]
cmp qword [rdx + 0x40], rax
jne 0xffffffffffffffaf
cmp rbp, rsi
je 0x50
mov rax, qword [r15 + 0x30]
mov rdx, rbp
```
More precisely, if we try to locate the exact position in the code at crash
time:
```asm
mov rsi, [r14+0x50] ; rsi = rcu_dereference(f->channel) → prev
test rsi, rsi
je ... ; if (prev == NULL) skip
mov rax, [rbp+0xf0] ; rax = chan->cli (rbp holds chan)
mov rdx, [rsi+0xf0] ; rdx = prev->cli
mov rax, [rax+0x40] ; rax = chan->cli->drm
cmp [rdx+0x40], rax ; <-- faults here: dereferences rdx+0x40
```
Which leads to the following lines of code:
```c
rcu_read_lock();
prev = rcu_dereference(f->channel);
local = prev && prev->cli->drm == chan->cli->drm;
if (local && (prev == chan ||
fctx->sync(f, prev, chan) == 0))
must_wait = false;
rcu_read_unlock();
```
So, the problem occurs on:
```c
local = prev && prev->cli->drm == chan->cli->drm;
```
`prev` is checked to be not NULL, but when the comparison occurs, we try to
dereference `rdx+0x40` and `rdx` is obviously set to NULL:
```text
RSP: 0018:ffffd1e710f6ba78 EFLAGS: 00010286
RAX: ffff89c7c0e28000 RBX: ffffd1e710f6ba90 RCX: ffff89c7c89f3180
RDX: 0000000000000000 RSI: ffff89c7e22d2000 RDI: ffffd1e710f6ba90
RBP: ffff89c86495b400 R08: ffff89c925b3b400 R09: 0000000000000000
R10: 0000000000000193 R11: ffff89c86d286e50 R12: 0000000000000001
R13: 0000000000000000 R14: ffff89c8b1108b40 R15: ffff89c853dc5100
```
Which means that `prev->cli` is NULL (and shouldn't be).
For now, I have no idea why this could happen, nor the meaning of it...
Does it ring a bell to somebody that has more knowledge about this ?
Regards
--
Emmanuel Fleury
Univ. de Bordeaux, LaBRI, | Associate Professor,
351, Cours de la Libération | Email: [email protected]
33405 Talence Cedex, France | Web: http://www.labri.fr/~fleury