* [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping @ 2026-06-10 15:32 ` Viacheslav Bocharov 2026-06-10 15:32 ` [PATCH 1/2] gpio: shared-proxy: always serialize with a sleeping mutex Viacheslav Bocharov ` (3 more replies) 0 siblings, 4 replies; 8+ messages in thread From: Viacheslav Bocharov @ 2026-06-10 15:32 UTC (permalink / raw) To: Linus Walleij, Bartosz Golaszewski Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Marek Szyprowski, Robin Murphy, Diederik de Haas, linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel gpio-shared-proxy chooses its descriptor lock (mutex vs spinlock) from the underlying chip's can_sleep, but under that lock it calls config and direction ops that reach sleeping pinctrl paths. On a controller with non-sleeping MMIO value ops the lock is a spinlock, so a sleeping call runs from atomic context: BUG: sleeping function called from invalid context ... pinctrl_gpio_set_config <- gpiochip_generic_config <- gpio_shared_proxy_set_config (voting spinlock held) <- ... <- mmc_pwrseq_simple_probe This was reported on Khadas VIM3 and worked around for Amlogic by commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping"), which marked the whole meson controller sleeping. That workaround broke atomic value-path consumers: w1-gpio (1-Wire bitbang) no longer detects devices, because its IRQ-disabled read slot calls the non-cansleep gpiod_*_value() and now hits WARN_ON(can_sleep) per bit. Patch 1 fixes the proxy locking generically (always a sleeping mutex). Patch 2 then restores meson can_sleep=false, fixing 1-Wire. Patch 1 has a trade-off: a proxied GPIO becomes sleeping, so consumers gating on gpiod_cansleep() change behaviour. No current device needs atomic (non-cansleep) value access on a shared GPIO -- every report (Khadas VIM3, ODROID-M1, my test on JetHub D1+) is a shared reset line (eMMC/SDIO pwrseq or PCIe reset) driven through the cansleep accessors, which is what the proxy exists to vote on. An alternative that keeps atomic value access (split locking) is possible but adds a second lock and new race windows. I went with the simpler, verified approach and would appreciate guidance on whether the atomic value path must be preserved. The two are a unit: patch 2 must not be applied without patch 1, otherwise the original VIM3 splat returns on boards that share a meson GPIO -- please keep the order. I have not Cc'd stable; I will request stable backports separately once both patches have landed. Viacheslav Bocharov (2): gpio: shared-proxy: always serialize with a sleeping mutex pinctrl: meson: restore non-sleeping GPIO access drivers/gpio/gpio-shared-proxy.c | 43 ++++++--------------------- drivers/gpio/gpiolib-shared.c | 9 ++---- drivers/gpio/gpiolib-shared.h | 31 ++++++++----------- drivers/pinctrl/meson/pinctrl-meson.c | 2 +- 4 files changed, 24 insertions(+), 61 deletions(-) -- 2.54.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/2] gpio: shared-proxy: always serialize with a sleeping mutex 2026-06-10 15:32 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Viacheslav Bocharov @ 2026-06-10 15:32 ` Viacheslav Bocharov 2026-06-16 12:53 ` Bartosz Golaszewski 2026-06-10 15:32 ` [PATCH 2/2] pinctrl: meson: restore non-sleeping GPIO access Viacheslav Bocharov ` (2 subsequent siblings) 3 siblings, 1 reply; 8+ messages in thread From: Viacheslav Bocharov @ 2026-06-10 15:32 UTC (permalink / raw) To: Linus Walleij, Bartosz Golaszewski Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Marek Szyprowski, Robin Murphy, Diederik de Haas, linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel 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@samsung.com> Closes: https://lore.kernel.org/all/00107523-7737-4b92-a785-14ce4e93b8cb@samsung.com/ Signed-off-by: Viacheslav Bocharov <v@baodeep.com> --- drivers/gpio/gpio-shared-proxy.c | 43 +++++++------------------------- drivers/gpio/gpiolib-shared.c | 9 ++----- drivers/gpio/gpiolib-shared.h | 31 +++++++++-------------- 3 files changed, 23 insertions(+), 60 deletions(-) diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c index 6941e4be6cf1..856e5b9d6163 100644 --- a/drivers/gpio/gpio-shared-proxy.c +++ b/drivers/gpio/gpio-shared-proxy.c @@ -109,7 +109,7 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset) if (proxy->voted_high) { ret = gpio_shared_proxy_set_unlocked(proxy, - shared_desc->can_sleep ? gpiod_set_value_cansleep : gpiod_set_value, 0); + gpiod_set_value_cansleep, 0); if (ret) dev_err(proxy->dev, "Failed to unset the shared GPIO value on release: %d\n", ret); @@ -222,13 +222,6 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value); } -static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset) -{ - struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - - return gpiod_get_value(proxy->shared_desc->desc); -} - static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, unsigned int offset) { @@ -237,29 +230,15 @@ static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, return gpiod_get_value_cansleep(proxy->shared_desc->desc); } -static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy, - int (*set_func)(struct gpio_desc *desc, int value), - int value) -{ - guard(gpio_shared_desc_lock)(proxy->shared_desc); - - return gpio_shared_proxy_set_unlocked(proxy, set_func, value); -} - -static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset, - int value) -{ - struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - - return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value); -} - static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); - return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value); + guard(gpio_shared_desc_lock)(proxy->shared_desc); + + return gpio_shared_proxy_set_unlocked(proxy, gpiod_set_value_cansleep, + value); } static int gpio_shared_proxy_get_direction(struct gpio_chip *gc, @@ -302,20 +281,16 @@ static int gpio_shared_proxy_probe(struct auxiliary_device *adev, gc->label = dev_name(dev); gc->parent = dev; gc->owner = THIS_MODULE; - gc->can_sleep = shared_desc->can_sleep; + /* Always a sleeping gpiochip: see the lock comment in gpiolib-shared.h. */ + gc->can_sleep = true; gc->request = gpio_shared_proxy_request; gc->free = gpio_shared_proxy_free; gc->set_config = gpio_shared_proxy_set_config; gc->direction_input = gpio_shared_proxy_direction_input; gc->direction_output = gpio_shared_proxy_direction_output; - if (gc->can_sleep) { - gc->set = gpio_shared_proxy_set_cansleep; - gc->get = gpio_shared_proxy_get_cansleep; - } else { - gc->set = gpio_shared_proxy_set; - gc->get = gpio_shared_proxy_get; - } + gc->set = gpio_shared_proxy_set_cansleep; + gc->get = gpio_shared_proxy_get_cansleep; gc->get_direction = gpio_shared_proxy_get_direction; gc->to_irq = gpio_shared_proxy_to_irq; diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index de72776fb154..495bd3d0ddf0 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -627,8 +627,7 @@ static void gpio_shared_release(struct kref *kref) shared_desc = entry->shared_desc; gpio_device_put(shared_desc->desc->gdev); - if (shared_desc->can_sleep) - mutex_destroy(&shared_desc->mutex); + mutex_destroy(&shared_desc->mutex); kfree(shared_desc); entry->shared_desc = NULL; } @@ -659,11 +658,7 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry) } shared_desc->desc = &gdev->descs[entry->offset]; - shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc); - if (shared_desc->can_sleep) - mutex_init(&shared_desc->mutex); - else - spin_lock_init(&shared_desc->spinlock); + mutex_init(&shared_desc->mutex); return shared_desc; } diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index 15e72a8dcdb1..5c725118b1af 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -6,7 +6,6 @@ #include <linux/cleanup.h> #include <linux/lockdep.h> #include <linux/mutex.h> -#include <linux/spinlock.h> struct gpio_device; struct gpio_desc; @@ -42,35 +41,29 @@ static inline int gpio_shared_add_proxy_lookup(struct device *consumer, struct gpio_shared_desc { struct gpio_desc *desc; - bool can_sleep; unsigned long cfg; unsigned int usecnt; unsigned int highcnt; - union { - struct mutex mutex; - spinlock_t spinlock; - }; + struct mutex mutex; /* serializes all proxy operations on this descriptor */ }; struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev); +/* + * Under this lock the proxy may call gpiod_set_config()/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 the + * underlying chip's can_sleep. A spinlock would run that sleeping call from + * atomic context, so the descriptor lock must be a mutex and the proxy + * gpiochip is therefore sleeping (can_sleep=true). + */ DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc, - if (_T->lock->can_sleep) - mutex_lock(&_T->lock->mutex); - else - spin_lock_irqsave(&_T->lock->spinlock, _T->flags), - if (_T->lock->can_sleep) - mutex_unlock(&_T->lock->mutex); - else - spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags), - unsigned long flags) + mutex_lock(&_T->lock->mutex), + mutex_unlock(&_T->lock->mutex)) static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc) { - if (shared_desc->can_sleep) - lockdep_assert_held(&shared_desc->mutex); - else - lockdep_assert_held(&shared_desc->spinlock); + lockdep_assert_held(&shared_desc->mutex); } #endif /* __LINUX_GPIO_SHARED_H */ -- 2.54.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] gpio: shared-proxy: always serialize with a sleeping mutex 2026-06-10 15:32 ` [PATCH 1/2] gpio: shared-proxy: always serialize with a sleeping mutex Viacheslav Bocharov @ 2026-06-16 12:53 ` Bartosz Golaszewski 0 siblings, 0 replies; 8+ messages in thread From: Bartosz Golaszewski @ 2026-06-16 12:53 UTC (permalink / raw) To: Viacheslav Bocharov Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Marek Szyprowski, Robin Murphy, Diederik de Haas, linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel, Linus Walleij, Bartosz Golaszewski On Wed, 10 Jun 2026 17:32:10 +0200, Viacheslav Bocharov <v@baodeep.com> 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@samsung.com> > Closes: https://lore.kernel.org/all/00107523-7737-4b92-a785-14ce4e93b8cb@samsung.com/ > Signed-off-by: Viacheslav Bocharov <v@baodeep.com> > --- Sigh... Ok let's try this. This limits users of shared pins to using them from process context exclusively but maybe there is in fact no need to do it from atomic context. After all: bit-banging can't work if we're using the voting mechanism. > > drivers/gpio/gpio-shared-proxy.c | 43 +++++++------------------------- > drivers/gpio/gpiolib-shared.c | 9 ++----- > drivers/gpio/gpiolib-shared.h | 31 +++++++++-------------- > 3 files changed, 23 insertions(+), 60 deletions(-) > > diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c > index 6941e4be6cf1..856e5b9d6163 100644 > --- a/drivers/gpio/gpio-shared-proxy.c > +++ b/drivers/gpio/gpio-shared-proxy.c > @@ -109,7 +109,7 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset) > > if (proxy->voted_high) { > ret = gpio_shared_proxy_set_unlocked(proxy, > - shared_desc->can_sleep ? gpiod_set_value_cansleep : gpiod_set_value, 0); > + gpiod_set_value_cansleep, 0); > if (ret) > dev_err(proxy->dev, > "Failed to unset the shared GPIO value on release: %d\n", ret); > @@ -222,13 +222,6 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc, > return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value); > } > > -static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset) > -{ > - struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); > - > - return gpiod_get_value(proxy->shared_desc->desc); > -} > - > static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, > unsigned int offset) > { > @@ -237,29 +230,15 @@ static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc, > return gpiod_get_value_cansleep(proxy->shared_desc->desc); > } > > -static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy, > - int (*set_func)(struct gpio_desc *desc, int value), > - int value) > -{ > - guard(gpio_shared_desc_lock)(proxy->shared_desc); > - > - return gpio_shared_proxy_set_unlocked(proxy, set_func, value); > -} > - > -static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset, > - int value) > -{ > - struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); > - > - return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value); > -} > - > static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc, > unsigned int offset, int value) > { > struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc); > > - return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value); > + guard(gpio_shared_desc_lock)(proxy->shared_desc); > + > + return gpio_shared_proxy_set_unlocked(proxy, gpiod_set_value_cansleep, > + value); > } > > static int gpio_shared_proxy_get_direction(struct gpio_chip *gc, > @@ -302,20 +281,16 @@ static int gpio_shared_proxy_probe(struct auxiliary_device *adev, > gc->label = dev_name(dev); > gc->parent = dev; > gc->owner = THIS_MODULE; > - gc->can_sleep = shared_desc->can_sleep; > + /* Always a sleeping gpiochip: see the lock comment in gpiolib-shared.h. */ > + gc->can_sleep = true; > > gc->request = gpio_shared_proxy_request; > gc->free = gpio_shared_proxy_free; > gc->set_config = gpio_shared_proxy_set_config; > gc->direction_input = gpio_shared_proxy_direction_input; > gc->direction_output = gpio_shared_proxy_direction_output; > - if (gc->can_sleep) { > - gc->set = gpio_shared_proxy_set_cansleep; > - gc->get = gpio_shared_proxy_get_cansleep; > - } else { > - gc->set = gpio_shared_proxy_set; > - gc->get = gpio_shared_proxy_get; > - } > + gc->set = gpio_shared_proxy_set_cansleep; > + gc->get = gpio_shared_proxy_get_cansleep; > gc->get_direction = gpio_shared_proxy_get_direction; > gc->to_irq = gpio_shared_proxy_to_irq; > > diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c > index de72776fb154..495bd3d0ddf0 100644 > --- a/drivers/gpio/gpiolib-shared.c > +++ b/drivers/gpio/gpiolib-shared.c > @@ -627,8 +627,7 @@ static void gpio_shared_release(struct kref *kref) > > shared_desc = entry->shared_desc; > gpio_device_put(shared_desc->desc->gdev); > - if (shared_desc->can_sleep) > - mutex_destroy(&shared_desc->mutex); > + mutex_destroy(&shared_desc->mutex); > kfree(shared_desc); > entry->shared_desc = NULL; > } > @@ -659,11 +658,7 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry) > } > > shared_desc->desc = &gdev->descs[entry->offset]; > - shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc); > - if (shared_desc->can_sleep) > - mutex_init(&shared_desc->mutex); > - else > - spin_lock_init(&shared_desc->spinlock); > + mutex_init(&shared_desc->mutex); > > return shared_desc; > } > diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h > index 15e72a8dcdb1..5c725118b1af 100644 > --- a/drivers/gpio/gpiolib-shared.h > +++ b/drivers/gpio/gpiolib-shared.h > @@ -6,7 +6,6 @@ > #include <linux/cleanup.h> > #include <linux/lockdep.h> > #include <linux/mutex.h> > -#include <linux/spinlock.h> > > struct gpio_device; > struct gpio_desc; > @@ -42,35 +41,29 @@ static inline int gpio_shared_add_proxy_lookup(struct device *consumer, > > struct gpio_shared_desc { > struct gpio_desc *desc; > - bool can_sleep; > unsigned long cfg; > unsigned int usecnt; > unsigned int highcnt; > - union { > - struct mutex mutex; > - spinlock_t spinlock; > - }; > + struct mutex mutex; /* serializes all proxy operations on this descriptor */ > }; > > struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev); > > +/* > + * Under this lock the proxy may call gpiod_set_config()/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 the > + * underlying chip's can_sleep. A spinlock would run that sleeping call from > + * atomic context, so the descriptor lock must be a mutex and the proxy > + * gpiochip is therefore sleeping (can_sleep=true). > + */ > DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc, > - if (_T->lock->can_sleep) > - mutex_lock(&_T->lock->mutex); > - else > - spin_lock_irqsave(&_T->lock->spinlock, _T->flags), > - if (_T->lock->can_sleep) > - mutex_unlock(&_T->lock->mutex); > - else > - spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags), > - unsigned long flags) > + mutex_lock(&_T->lock->mutex), > + mutex_unlock(&_T->lock->mutex)) Just drop these wrappers altogether then, let's open-code mutex locking in the proxy. > > static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc) > { > - if (shared_desc->can_sleep) > - lockdep_assert_held(&shared_desc->mutex); > - else > - lockdep_assert_held(&shared_desc->spinlock); > + lockdep_assert_held(&shared_desc->mutex); Same here, move it to the proxy. > } > > #endif /* __LINUX_GPIO_SHARED_H */ > -- > 2.54.0 > > > Bart ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] pinctrl: meson: restore non-sleeping GPIO access 2026-06-10 15:32 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Viacheslav Bocharov 2026-06-10 15:32 ` [PATCH 1/2] gpio: shared-proxy: always serialize with a sleeping mutex Viacheslav Bocharov @ 2026-06-10 15:32 ` Viacheslav Bocharov 2026-06-11 8:26 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Marek Szyprowski 2026-06-11 9:53 ` Bartosz Golaszewski 3 siblings, 0 replies; 8+ messages in thread From: Viacheslav Bocharov @ 2026-06-10 15:32 UTC (permalink / raw) To: Linus Walleij, Bartosz Golaszewski Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Marek Szyprowski, Robin Murphy, Diederik de Haas, linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel Commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") set gpio_chip.can_sleep = true to work around gpio-shared-proxy holding a spinlock across a sleeping pinctrl config path. That locking bug is now fixed in the shared-proxy itself ("gpio: shared-proxy: always serialize with a sleeping mutex"), so the controller-wide workaround is no longer needed; the meson GPIO controller does not sleep. meson_gpio_get/set/direction_* access MMIO through regmap. The regmap_mmio bus uses fast I/O (spinlock) locking, so these value callbacks do not contain sleeping operations. Since gpio_chip.can_sleep describes the get/set value path, restore can_sleep = false. Marking the controller sleeping also broke atomic value consumers such as w1-gpio (1-Wire bitbang): w1_io.c runs its read time slot under local_irq_save() and uses the non-cansleep gpiod_set_value() / gpiod_get_value(), which with can_sleep=true trigger WARN_ON(can_sleep) in gpiolib on every transferred bit (from w1_gpio_write_bit() / w1_gpio_read_bit() via w1_reset_bus() and w1_search()). The printk and stack dump inside the IRQs-off, microsecond-scale time slot destroy the bit timing, so reset/presence detection and ROM search fail: the bus master registers but w1_master_slave_count stays at 0 and no devices are found. Verified on an Amlogic A113X board (DS18B20 on GPIOA_14): with can_sleep restored to false the warnings are gone and the sensor is detected and read again. This must not be applied or backported without the shared-proxy locking fix above; otherwise the original Khadas VIM3 splat returns on boards that genuinely share a meson GPIO. Fixes: 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") Link: https://lore.kernel.org/all/20260105150509.56537-1-bartosz.golaszewski@oss.qualcomm.com/ Signed-off-by: Viacheslav Bocharov <v@baodeep.com> --- drivers/pinctrl/meson/pinctrl-meson.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 4507dc8b5563..18295b15ecd9 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -619,7 +619,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc) pc->chip.set = meson_gpio_set; pc->chip.base = -1; pc->chip.ngpio = pc->data->num_pins; - pc->chip.can_sleep = true; + pc->chip.can_sleep = false; ret = gpiochip_add_data(&pc->chip, pc); if (ret) { -- 2.54.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping 2026-06-10 15:32 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Viacheslav Bocharov 2026-06-10 15:32 ` [PATCH 1/2] gpio: shared-proxy: always serialize with a sleeping mutex Viacheslav Bocharov 2026-06-10 15:32 ` [PATCH 2/2] pinctrl: meson: restore non-sleeping GPIO access Viacheslav Bocharov @ 2026-06-11 8:26 ` Marek Szyprowski 2026-06-11 9:27 ` Viacheslav 2026-06-23 15:16 ` Robin Murphy 2026-06-11 9:53 ` Bartosz Golaszewski 3 siblings, 2 replies; 8+ messages in thread From: Marek Szyprowski @ 2026-06-11 8:26 UTC (permalink / raw) To: Viacheslav Bocharov, Linus Walleij, Bartosz Golaszewski Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Robin Murphy, Diederik de Haas, linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel, linux-rockchip, Heiko Stuebner Hi Viachesla, On 10.06.2026 17:32, Viacheslav Bocharov wrote: > gpio-shared-proxy chooses its descriptor lock (mutex vs spinlock) from > the underlying chip's can_sleep, but under that lock it calls config and > direction ops that reach sleeping pinctrl paths. On a controller with > non-sleeping MMIO value ops the lock is a spinlock, so a sleeping call > runs from atomic context: > > BUG: sleeping function called from invalid context > ... pinctrl_gpio_set_config <- gpiochip_generic_config > <- gpio_shared_proxy_set_config (voting spinlock held) > <- ... <- mmc_pwrseq_simple_probe > > This was reported on Khadas VIM3 and worked around for Amlogic by > commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as > sleeping"), which marked the whole meson controller sleeping. That > workaround broke atomic value-path consumers: w1-gpio (1-Wire bitbang) > no longer detects devices, because its IRQ-disabled read slot calls the > non-cansleep gpiod_*_value() and now hits WARN_ON(can_sleep) per bit. > > Patch 1 fixes the proxy locking generically (always a sleeping mutex). > Patch 2 then restores meson can_sleep=false, fixing 1-Wire. > > Patch 1 has a trade-off: a proxied GPIO becomes sleeping, so consumers > gating on gpiod_cansleep() change behaviour. No current device needs > atomic (non-cansleep) value access on a shared GPIO -- every report > (Khadas VIM3, ODROID-M1, my test on JetHub D1+) is a shared reset line > (eMMC/SDIO pwrseq or PCIe reset) driven through the cansleep accessors, > which is what the proxy exists to vote on. An alternative that keeps > atomic value access (split locking) is possible but adds a second lock > and new race windows. I went with the simpler, verified approach and > would appreciate guidance on whether the atomic value path must be > preserved. > > The two are a unit: patch 2 must not be applied without patch 1, > otherwise the original VIM3 splat returns on boards that share a meson > GPIO -- please keep the order. I have not Cc'd stable; I will request > stable backports separately once both patches have landed. > > Viacheslav Bocharov (2): > gpio: shared-proxy: always serialize with a sleeping mutex > pinctrl: meson: restore non-sleeping GPIO access Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> This probably also affects the similar changes in Rockchip GPIO driver done by the following commits: 20cf2aed89ac ("gpio: rockchip: mark the GPIO controller as sleeping") 7ca497be0016 ("gpio: rockchip: Stop calling pinctrl for set_direction") I've checked this patchset with these two reverted and no warning was reported. Best regards -- Marek Szyprowski, PhD Samsung R&D Institute Poland ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping 2026-06-11 8:26 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Marek Szyprowski @ 2026-06-11 9:27 ` Viacheslav 2026-06-23 15:16 ` Robin Murphy 1 sibling, 0 replies; 8+ messages in thread From: Viacheslav @ 2026-06-11 9:27 UTC (permalink / raw) To: Marek Szyprowski, Linus Walleij, Bartosz Golaszewski Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Robin Murphy, Diederik de Haas, linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel, linux-rockchip, Heiko Stuebner Hi, Marek! 11.06.2026 11:26, Marek Szyprowski wrote: > Hi Viachesla, > > On 10.06.2026 17:32, Viacheslav Bocharov wrote: > > Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> > Thanks for testing. > This probably also affects the similar changes in Rockchip GPIO driver done > by the following commits: > 20cf2aed89ac ("gpio: rockchip: mark the GPIO controller as sleeping") > 7ca497be0016 ("gpio: rockchip: Stop calling pinctrl for set_direction") > Yeah, we also need to revert can_sleep=true on rockchip and amlogic-a4 (d6df4abe95a4). Best regards -- Viacheslav Bocharov Shenzhen JetHome Technology Co., Ltd. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping 2026-06-11 8:26 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Marek Szyprowski 2026-06-11 9:27 ` Viacheslav @ 2026-06-23 15:16 ` Robin Murphy 1 sibling, 0 replies; 8+ messages in thread From: Robin Murphy @ 2026-06-23 15:16 UTC (permalink / raw) To: Marek Szyprowski, Viacheslav Bocharov, Linus Walleij, Bartosz Golaszewski Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Diederik de Haas, linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel, linux-rockchip, Heiko Stuebner On 11/06/2026 9:26 am, Marek Szyprowski wrote: > Hi Viachesla, > > On 10.06.2026 17:32, Viacheslav Bocharov wrote: >> gpio-shared-proxy chooses its descriptor lock (mutex vs spinlock) from >> the underlying chip's can_sleep, but under that lock it calls config and >> direction ops that reach sleeping pinctrl paths. On a controller with >> non-sleeping MMIO value ops the lock is a spinlock, so a sleeping call >> runs from atomic context: >> >> BUG: sleeping function called from invalid context >> ... pinctrl_gpio_set_config <- gpiochip_generic_config >> <- gpio_shared_proxy_set_config (voting spinlock held) >> <- ... <- mmc_pwrseq_simple_probe >> >> This was reported on Khadas VIM3 and worked around for Amlogic by >> commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as >> sleeping"), which marked the whole meson controller sleeping. That >> workaround broke atomic value-path consumers: w1-gpio (1-Wire bitbang) >> no longer detects devices, because its IRQ-disabled read slot calls the >> non-cansleep gpiod_*_value() and now hits WARN_ON(can_sleep) per bit. >> >> Patch 1 fixes the proxy locking generically (always a sleeping mutex). >> Patch 2 then restores meson can_sleep=false, fixing 1-Wire. >> >> Patch 1 has a trade-off: a proxied GPIO becomes sleeping, so consumers >> gating on gpiod_cansleep() change behaviour. No current device needs >> atomic (non-cansleep) value access on a shared GPIO -- every report >> (Khadas VIM3, ODROID-M1, my test on JetHub D1+) is a shared reset line >> (eMMC/SDIO pwrseq or PCIe reset) driven through the cansleep accessors, >> which is what the proxy exists to vote on. An alternative that keeps >> atomic value access (split locking) is possible but adds a second lock >> and new race windows. I went with the simpler, verified approach and >> would appreciate guidance on whether the atomic value path must be >> preserved. >> >> The two are a unit: patch 2 must not be applied without patch 1, >> otherwise the original VIM3 splat returns on boards that share a meson >> GPIO -- please keep the order. I have not Cc'd stable; I will request >> stable backports separately once both patches have landed. >> >> Viacheslav Bocharov (2): >> gpio: shared-proxy: always serialize with a sleeping mutex >> pinctrl: meson: restore non-sleeping GPIO access > > Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> > > This probably also affects the similar changes in Rockchip GPIO driver done > by the following commits: > 20cf2aed89ac ("gpio: rockchip: mark the GPIO controller as sleeping") > 7ca497be0016 ("gpio: rockchip: Stop calling pinctrl for set_direction") > > I've checked this patchset with these two reverted and no warning was reported. If it hadn't already been fixed, then indeed I guess this might make 20cf2aed89ac redundant. However, 7ca497be0016 is still an objective improvement either way, since that driver never needed to call pinctrl at all (it was seemingly just an artefact of how the GPIO code was originally implemented within the pinctrl driver itself). Thanks, Robin. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping 2026-06-10 15:32 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Viacheslav Bocharov ` (2 preceding siblings ...) 2026-06-11 8:26 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Marek Szyprowski @ 2026-06-11 9:53 ` Bartosz Golaszewski 3 siblings, 0 replies; 8+ messages in thread From: Bartosz Golaszewski @ 2026-06-11 9:53 UTC (permalink / raw) To: Viacheslav Bocharov Cc: Linus Walleij, Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Marek Szyprowski, Robin Murphy, Diederik de Haas, linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel On Wed, Jun 10, 2026 at 5:41 PM Viacheslav Bocharov <v@baodeep.com> wrote: > > gpio-shared-proxy chooses its descriptor lock (mutex vs spinlock) from > the underlying chip's can_sleep, but under that lock it calls config and > direction ops that reach sleeping pinctrl paths. On a controller with > non-sleeping MMIO value ops the lock is a spinlock, so a sleeping call > runs from atomic context: > Why am I seeing three versions of this in my inbox? Which one should I review? Bartosz ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-23 15:17 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CGME20260610153425eucas1p29d20a835114a28b15cb12ea00534e074@eucas1p2.samsung.com>
2026-06-10 15:32 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Viacheslav Bocharov
2026-06-10 15:32 ` [PATCH 1/2] gpio: shared-proxy: always serialize with a sleeping mutex Viacheslav Bocharov
2026-06-16 12:53 ` Bartosz Golaszewski
2026-06-10 15:32 ` [PATCH 2/2] pinctrl: meson: restore non-sleeping GPIO access Viacheslav Bocharov
2026-06-11 8:26 ` [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping Marek Szyprowski
2026-06-11 9:27 ` Viacheslav
2026-06-23 15:16 ` Robin Murphy
2026-06-11 9:53 ` Bartosz Golaszewski
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox