[PATCH 5/5] powerpc/spufs: fix mmap_lock/state_mutex lock inversion

From: Junrui Luo via B4 Relay

Date: Wed Aug 12 2026 - 01:51:11 EST


From: Junrui Luo <moonafterrain@xxxxxxxxxxx>

spufs_ps_fault() is called by the VM with mmap_lock held for read and
takes ctx->state_mutex via spu_acquire(). When ctx->state is
SPU_STATE_SAVED it drops mmap_lock, waits in spufs_wait() for the
context to become runnable, and then re-takes mmap_lock. spufs_wait()
returns with state_mutex re-acquired, and spu_release() only runs after
the branch, so mmap_read_lock() is called while state_mutex is held.
Every other spufs fault path takes the locks in the opposite order:
spufs_mem_mmap_fault() and spufs_ps_fault() are entered with mmap_lock
already held and only then take state_mutex.

Three threads sharing an mm can close the cycle: one holds state_mutex
and blocks in mmap_read_lock(), another holds mmap_lock for read and
blocks in spu_acquire(), and a queued writer in mmap_write_lock()
prevents the first down_read() from succeeding. This can result in a
deadlock, and since spusched_tick() takes the same state_mutex, one
wedged context also stalls SPU scheduling for every other context on
the node.

Fix by calling spu_release() before re-taking mmap_lock and jumping to
the existing refault path, which drops the reference taken earlier in
the function and returns VM_FAULT_NOPAGE as before.

Fixes: 33bfd7a73861 ("[POWERPC] spufs: block fault handlers in spu_acquire_runnable")
Reported-by: Yuhao Jiang <danisjiang@xxxxxxxxx>
Assisted-by: Claude:claude-opus-5
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Junrui Luo <moonafterrain@xxxxxxxxxxx>
---
Found by inspection; I have no Cell/PS3 hardware, so this is
compile-tested only.
---
arch/powerpc/platforms/cell/spufs/file.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index 07b1755ddc3d..d479c956506d 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -349,7 +349,10 @@ static vm_fault_t spufs_ps_fault(struct vm_fault *vmf,
spu_context_nospu_trace(spufs_ps_fault__sleep, ctx);
err = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE);
spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu);
+ if (!err)
+ spu_release(ctx);
mmap_read_lock(current->mm);
+ goto refault;
} else {
area = ctx->spu->problem_phys + ps_offs;
ret = vmf_insert_pfn(vmf->vma, vmf->address,

--
2.51.2