]> git.ipfire.org Git - thirdparty/kernel/linux.git/commit
gpio: shared-proxy: always serialize with a sleeping mutex
authorViacheslav Bocharov <v@baodeep.com>
Tue, 30 Jun 2026 10:15:44 +0000 (13:15 +0300)
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tue, 30 Jun 2026 14:01:28 +0000 (16:01 +0200)
commitefecde8a254d1f207b75c5ebcfba2c51f4c771d9
treef6b93b46d68da652424cff7bc32eee6a79b14826
parent9a6c0b6ea12746d50cf53d59a7e05fd83f974bda
gpio: shared-proxy: always serialize with a sleeping mutex

The shared GPIO descriptor used either a mutex or a spinlock, chosen at
runtime from the underlying chip's can_sleep:

shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
... if (can_sleep) mutex_lock(); else spin_lock_irqsave();

can_sleep describes only the value path (->get/->set). Under the same
lock, however, the proxy may call gpiod_set_config() and
gpiod_direction_*(), which can reach pinctrl paths that take a mutex
(e.g. gpiod_set_config() -> gpiochip_generic_config() ->
pinctrl_gpio_set_config()), independent of can_sleep. On a controller
with non-sleeping MMIO value ops the descriptor lock was a spinlock, so
the sleeping pinctrl call ran from atomic context. Reproduced on an
Amlogic A113X board with the workaround from commit 28f240683871
("pinctrl: meson: mark the GPIO controller as sleeping") reverted; the
original Khadas VIM3 report hit the same path:

BUG: sleeping function called from invalid context
  __mutex_lock
  pinctrl_get_device_gpio_range
  pinctrl_gpio_set_config
  gpiochip_generic_config
  gpiod_set_config
  gpio_shared_proxy_set_config   <- voting spinlock held
  ...
  mmc_pwrseq_simple_probe

The spinlock existed to take the value vote from atomic context, but the
vote and the (possibly sleeping) control operations share the same state
and lock, so this scheme cannot serialize config under a mutex and still
offer atomic value access. Always serialize the shared descriptor with a
mutex instead and mark the proxy a sleeping gpiochip, driving the
underlying GPIO through the cansleep value accessors: those are valid
for both sleeping and non-sleeping chips, so value access keeps working
on fast controllers, at the cost of no longer being atomic.

With every vote edge now driven through the cansleep value setter,
gpio_shared_proxy_set_unlocked() no longer needs a per-call setter: drop
its set_func callback and call gpiod_set_value_cansleep() directly. The
shared direction_output path reaches it only once the line is already an
output, so driving the value there is equivalent to re-issuing
gpiod_direction_output(), without the redundant per-edge re-assertion of
drive config and bias.

This is observable: consumers gating on gpiod_cansleep() take their
sleeping branch on a proxied GPIO (mmc-pwrseq-emmc skips its
emergency-restart reset handler; its normal reset is unaffected), and
consumers that reject sleeping GPIOs (pwm-gpio, ps2-gpio, ...) would
fail to probe. Such atomic users do not share a pin through the proxy,
whose purpose is voting on shared reset/enable lines. The same narrowing
already applies on Amlogic since that workaround, and rockchip
addressed the identical splat per-driver in commit 7ca497be0016 ("gpio:
rockchip: Stop calling pinctrl for set_direction"); fixing the proxy
addresses the locking error once, for every controller.

The lock type was added by commit a060b8c511ab ("gpiolib: implement
low-level, shared GPIO support"); the sleeping call under it arrived with
the proxy driver.

Fixes: e992d54c6f97 ("gpio: shared-proxy: implement the shared GPIO proxy driver")
Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
Closes: https://lore.kernel.org/all/00107523-7737-4b92-a785-14ce4e93b8cb@samsung.com/
Signed-off-by: Viacheslav Bocharov <v@baodeep.com>
Link: https://patch.msgid.link/20260630101545.800625-2-v@baodeep.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
drivers/gpio/gpio-shared-proxy.c
drivers/gpio/gpiolib-shared.c
drivers/gpio/gpiolib-shared.h