Re: [PATCH v2 1/2] gpio: shared-proxy: always serialize with a sleeping mutex

From: Bartosz Golaszewski

Date: Mon Jun 29 2026 - 13:07:28 EST


On Thu, 25 Jun 2026 13:57:17 +0200, Viacheslav Bocharov <v@xxxxxxxxxxx> said:
> 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.
>
> 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@xxxxxxxxxxx>
> Closes: https://lore.kernel.org/all/00107523-7737-4b92-a785-14ce4e93b8cb@xxxxxxxxxxx/
> Signed-off-by: Viacheslav Bocharov <v@xxxxxxxxxxx>
> ---
> v1 -> v2: open-code the descriptor mutex; drop the gpio_shared_desc_lock
> guard and the gpio_shared_lockdep_assert() helper, use
> guard(mutex) and lockdep_assert_held() directly; move the
> mutex rationale from the header to the can_sleep assignment in
> probe.
>
> v1: https://lore.kernel.org/linux-gpio/20260610153329.937833-2-v@xxxxxxxxxxx/
>
> drivers/gpio/gpio-shared-proxy.c | 66 +++++++++++++-------------------
> drivers/gpio/gpiolib-shared.c | 9 +----
> drivers/gpio/gpiolib-shared.h | 28 +-------------
> 3 files changed, 29 insertions(+), 74 deletions(-)
>
> diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c
> index 6941e4be6cf1..0cd52015b731 100644
> --- a/drivers/gpio/gpio-shared-proxy.c
> +++ b/drivers/gpio/gpio-shared-proxy.c
> @@ -9,8 +9,10 @@
> #include <linux/err.h>
> #include <linux/gpio/consumer.h>
> #include <linux/gpio/driver.h>
> +#include <linux/lockdep.h>
> #include <linux/mod_devicetable.h>
> #include <linux/module.h>
> +#include <linux/mutex.h>
> #include <linux/string_choices.h>
> #include <linux/types.h>
>
> @@ -32,7 +34,7 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy,

I was about to apply it but then realized that it can be simplified further.
The set_func() argument in gpio_shared_proxy_set_unlocked() is no longer
needed and can be replaced with a direct call to gpiod_set_value_cansleep().

Would you mind sending a v3 with that included?

Thanks,
Bartosz