Re: [PATCH v2] Input: uinput - fix circular locking dependency with ff-core

Dmitry Torokhov
2026-04-08T04:56:55Z

On Tue, Apr 07, 2026 at 12:50:31PM +0500, Mikhail Gavrilov wrote:
> A lockdep circular locking dependency warning can be triggered
> reproducibly when using a force-feedback gamepad with uinput (for
> example, playing ELDEN RING under Wine with a Flydigi Vader 5
> controller):
>
> ff->mutex -> udev->mutex -> input_mutex -> dev->mutex -> ff->mutex
>
> The cycle is caused by four lock acquisition paths:
>
> 1. ff upload: input_ff_upload() holds ff->mutex and calls
> uinput_dev_upload_effect() -> uinput_request_submit() ->
> uinput_request_send(), which acquires udev->mutex.
>
> 2. device create: uinput_ioctl_handler() holds udev->mutex and calls
> uinput_create_device() -> input_register_device(), which acquires
> input_mutex.
>
> 3. device register: input_register_device() holds input_mutex and
> calls kbd_connect() -> input_register_handle(), which acquires
> dev->mutex.
>
> 4. evdev release: evdev_release() calls input_flush_device() under
> dev->mutex, which calls input_ff_flush() acquiring ff->mutex.
>
> Fix this by introducing a new state_lock spinlock to protect
> udev->state and udev->dev access in uinput_request_send() instead of
> acquiring udev->mutex. The function only needs to atomically check
> device state and queue an input event into the ring buffer via
> uinput_dev_event() -- both operations are safe under a spinlock
> (ktime_get_ts64() and wake_up_interruptible() do not sleep). This
> breaks the ff->mutex -> udev->mutex link since a spinlock is a leaf in
> the lock ordering and cannot form cycles with mutexes.
>
> To keep state transitions visible to uinput_request_send(), protect
> writes to udev->state in uinput_create_device() and
> uinput_destroy_device() with the same state_lock spinlock.
>
> Additionally, move init_completion(&request->done) from
> uinput_request_send() to uinput_request_submit() before
> uinput_request_reserve_slot(). Once the slot is allocated,
> uinput_flush_requests() may call complete() on it at any time from
> the destroy path, so the completion must be initialised before the
> request becomes visible.
>
> Lock ordering after the fix:
>
> ff->mutex -> state_lock (spinlock, leaf)
> udev->mutex -> state_lock (spinlock, leaf)
> udev->mutex -> input_mutex -> dev->mutex -> ff->mutex (no back-edge)
>
> Fixes: ff462551235d ("Input: uinput - switch to the new FF interface")
> Cc: stable@vger.kernel.org
> Link: https://lore.kernel.org/all/CABXGCsMoxag+kEwHhb7KqhuyxfmGGd0P=tHZyb1uKE0pLr8Hkg@mail.gmail.com/
> Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>

Applied, thank you.

--
Dmitry