diff mbox series

gpio: omap: Silence lockdep "Invalid wait context"

Message ID 20241125080530.777123-1-alexander.sverdlin@siemens.com (mailing list archive)
State New
Headers show
Series gpio: omap: Silence lockdep "Invalid wait context" | expand

Commit Message

Sverdlin, Alexander Nov. 25, 2024, 8:05 a.m. UTC
From: Alexander Sverdlin <alexander.sverdlin@siemens.com>

The problem apparetly has been known since the conversion to
raw_spin_lock() (commit 4dbada2be460
("gpio: omap: use raw locks for locking")).

Symptom:

[ BUG: Invalid wait context ]
5.10.214
-----------------------------
swapper/1 is trying to lock:
(enable_lock){....}-{3:3}, at: clk_enable_lock
other info that might help us debug this:
context-{5:5}
2 locks held by swapper/1:
 #0: (&dev->mutex){....}-{4:4}, at: device_driver_attach
 #1: (&bank->lock){....}-{2:2}, at: omap_gpio_set_config
stack backtrace:
CPU: 0 PID: 1 Comm: swapper Not tainted 5.10.214
Hardware name: Generic AM33XX (Flattened Device Tree)
unwind_backtrace
show_stack
__lock_acquire
lock_acquire.part.0
_raw_spin_lock_irqsave
clk_enable_lock
clk_enable
omap_gpio_set_config
gpio_keys_setup_key
gpio_keys_probe
platform_drv_probe
really_probe
driver_probe_device
device_driver_attach
__driver_attach
bus_for_each_dev
bus_add_driver
driver_register
do_one_initcall
do_initcalls
kernel_init_freeable
kernel_init

Problematic spin_lock_irqsave(&enable_lock, ...) is being called by
clk_enable()/clk_disable() in omap2_set_gpio_debounce() and
omap_clear_gpio_debounce().

For omap2_set_gpio_debounce() it's possible to move
raw_spin_lock_irqsave(&bank->lock, ...) inside omap2_set_gpio_debounce()
so that the locks nest as follows:

  clk_enable(bank->dbck)
  raw_spin_lock_irqsave(&bank->lock, ...)
  raw_spin_unlock_irqrestore()
  clk_disable()

Two call-sites of omap_clear_gpio_debounce() are more convoluted, but one
can take the advantage of the nesting nature of clk_enable()/clk_disable(),
so that the inner clk_disable() becomes lockless:

  clk_enable(bank->dbck)		<-- only to clk_enable_lock()
  raw_spin_lock_irqsave(&bank->lock, ...)
  omap_clear_gpio_debounce()
    clk_disable()			<-- becomes lockless
  raw_spin_unlock_irqrestore()
  clk_disable()

Cc: stable@vger.kernel.org
Fixes: 4dbada2be460 ("gpio: omap: use raw locks for locking")
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
---
 drivers/gpio/gpio-omap.c | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

Comments

Bartosz Golaszewski Nov. 26, 2024, 8:37 p.m. UTC | #1
On Mon, Nov 25, 2024 at 9:05 AM A. Sverdlin
<alexander.sverdlin@siemens.com> wrote:
>
> From: Alexander Sverdlin <alexander.sverdlin@siemens.com>
>
> The problem apparetly has been known since the conversion to
> raw_spin_lock() (commit 4dbada2be460
> ("gpio: omap: use raw locks for locking")).
>
> Symptom:
>
> [ BUG: Invalid wait context ]
> 5.10.214
> -----------------------------
> swapper/1 is trying to lock:
> (enable_lock){....}-{3:3}, at: clk_enable_lock
> other info that might help us debug this:
> context-{5:5}
> 2 locks held by swapper/1:
>  #0: (&dev->mutex){....}-{4:4}, at: device_driver_attach
>  #1: (&bank->lock){....}-{2:2}, at: omap_gpio_set_config
> stack backtrace:
> CPU: 0 PID: 1 Comm: swapper Not tainted 5.10.214
> Hardware name: Generic AM33XX (Flattened Device Tree)
> unwind_backtrace
> show_stack
> __lock_acquire
> lock_acquire.part.0
> _raw_spin_lock_irqsave
> clk_enable_lock
> clk_enable
> omap_gpio_set_config
> gpio_keys_setup_key
> gpio_keys_probe
> platform_drv_probe
> really_probe
> driver_probe_device
> device_driver_attach
> __driver_attach
> bus_for_each_dev
> bus_add_driver
> driver_register
> do_one_initcall
> do_initcalls
> kernel_init_freeable
> kernel_init
>
> Problematic spin_lock_irqsave(&enable_lock, ...) is being called by
> clk_enable()/clk_disable() in omap2_set_gpio_debounce() and
> omap_clear_gpio_debounce().
>
> For omap2_set_gpio_debounce() it's possible to move
> raw_spin_lock_irqsave(&bank->lock, ...) inside omap2_set_gpio_debounce()
> so that the locks nest as follows:
>
>   clk_enable(bank->dbck)
>   raw_spin_lock_irqsave(&bank->lock, ...)
>   raw_spin_unlock_irqrestore()
>   clk_disable()
>
> Two call-sites of omap_clear_gpio_debounce() are more convoluted, but one
> can take the advantage of the nesting nature of clk_enable()/clk_disable(),
> so that the inner clk_disable() becomes lockless:
>
>   clk_enable(bank->dbck)                <-- only to clk_enable_lock()
>   raw_spin_lock_irqsave(&bank->lock, ...)
>   omap_clear_gpio_debounce()
>     clk_disable()                       <-- becomes lockless
>   raw_spin_unlock_irqrestore()
>   clk_disable()
>
> Cc: stable@vger.kernel.org
> Fixes: 4dbada2be460 ("gpio: omap: use raw locks for locking")
> Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
> ---
>  drivers/gpio/gpio-omap.c | 35 ++++++++++++++++++++++++++++++-----
>  1 file changed, 30 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
> index 7ad4534054962..f9e502aa57753 100644
> --- a/drivers/gpio/gpio-omap.c
> +++ b/drivers/gpio/gpio-omap.c
> @@ -181,6 +181,7 @@ static inline void omap_gpio_dbck_disable(struct gpio_bank *bank)
>  static int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
>                                    unsigned debounce)
>  {
> +       unsigned long           flags;
>         u32                     val;
>         u32                     l;
>         bool                    enable = !!debounce;
> @@ -196,13 +197,18 @@ static int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
>
>         l = BIT(offset);
>
> +       /*
> +        * Ordering is important here: clk_enable() calls spin_lock_irqsave(),
> +        * therefore it must be outside of the following raw_spin_lock_irqsave()
> +        */
>         clk_enable(bank->dbck);
> +       raw_spin_lock_irqsave(&bank->lock, flags);
> +
>         writel_relaxed(debounce, bank->base + bank->regs->debounce);
>
>         val = omap_gpio_rmw(bank->base + bank->regs->debounce_en, l, enable);
>         bank->dbck_enable_mask = val;
>
> -       clk_disable(bank->dbck);
>         /*
>          * Enable debounce clock per module.
>          * This call is mandatory because in omap_gpio_request() when
> @@ -217,6 +223,9 @@ static int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
>                 bank->context.debounce_en = val;
>         }
>
> +       raw_spin_unlock_irqrestore(&bank->lock, flags);
> +       clk_disable(bank->dbck);
> +

This part looks pretty clear to me.

>         return 0;
>  }
>
> @@ -647,6 +656,13 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
>         unsigned long flags;
>         unsigned offset = d->hwirq;
>
> +       /*
> +        * Enable the clock here so that the nested clk_disable() in the
> +        * following omap_clear_gpio_debounce() is lockless
> +        */
> +       if (bank->dbck_flag)
> +               clk_enable(bank->dbck);
> +

But this looks like a functional change. You effectively bump the
clock enable count but don't add a corresponding clk_disable() in the
affected path. Is the clock ever actually disabled then?

Am I not getting something?

Bart

>         raw_spin_lock_irqsave(&bank->lock, flags);
>         bank->irq_usage &= ~(BIT(offset));
>         omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
> @@ -656,6 +672,9 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
>                 omap_clear_gpio_debounce(bank, offset);
>         omap_disable_gpio_module(bank, offset);
>         raw_spin_unlock_irqrestore(&bank->lock, flags);
> +
> +       if (bank->dbck_flag)
> +               clk_disable(bank->dbck);
>  }
>
>  static void omap_gpio_irq_bus_lock(struct irq_data *data)
> @@ -827,6 +846,13 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
>         struct gpio_bank *bank = gpiochip_get_data(chip);
>         unsigned long flags;
>
> +       /*
> +        * Enable the clock here so that the nested clk_disable() in the
> +        * following omap_clear_gpio_debounce() is lockless
> +        */
> +       if (bank->dbck_flag)
> +               clk_enable(bank->dbck);
> +
>         raw_spin_lock_irqsave(&bank->lock, flags);
>         bank->mod_usage &= ~(BIT(offset));
>         if (!LINE_USED(bank->irq_usage, offset)) {
> @@ -836,6 +862,9 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
>         omap_disable_gpio_module(bank, offset);
>         raw_spin_unlock_irqrestore(&bank->lock, flags);
>
> +       if (bank->dbck_flag)
> +               clk_disable(bank->dbck);
> +
>         pm_runtime_put(chip->parent);
>  }
>
> @@ -913,15 +942,11 @@ static int omap_gpio_debounce(struct gpio_chip *chip, unsigned offset,
>                               unsigned debounce)
>  {
>         struct gpio_bank *bank;
> -       unsigned long flags;
>         int ret;
>
>         bank = gpiochip_get_data(chip);
>
> -       raw_spin_lock_irqsave(&bank->lock, flags);
>         ret = omap2_set_gpio_debounce(bank, offset, debounce);
> -       raw_spin_unlock_irqrestore(&bank->lock, flags);
> -
>         if (ret)
>                 dev_info(chip->parent,
>                          "Could not set line %u debounce to %u microseconds (%d)",
> --
> 2.47.0
>
Sverdlin, Alexander Nov. 26, 2024, 8:59 p.m. UTC | #2
Hi Bartosz!

On Tue, 2024-11-26 at 21:37 +0100, Bartosz Golaszewski wrote:
> > @@ -647,6 +656,13 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
> >          unsigned long flags;
> >          unsigned offset = d->hwirq;
> > 
> > +       /*
> > +        * Enable the clock here so that the nested clk_disable() in the
> > +        * following omap_clear_gpio_debounce() is lockless
> > +        */
> > +       if (bank->dbck_flag)
> > +               clk_enable(bank->dbck);
> > +
> 
> But this looks like a functional change. You effectively bump the
> clock enable count but don't add a corresponding clk_disable() in the
> affected path. Is the clock ever actually disabled then?
> 
> Am I not getting something?

Actually I though I enable and disable them in the very same function, so for the
first enable above...

> 
> >          raw_spin_lock_irqsave(&bank->lock, flags);
> >          bank->irq_usage &= ~(BIT(offset));
> >          omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
> > @@ -656,6 +672,9 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
> >                  omap_clear_gpio_debounce(bank, offset);
> >          omap_disable_gpio_module(bank, offset);
> >          raw_spin_unlock_irqrestore(&bank->lock, flags);
> > +
> > +       if (bank->dbck_flag)
> > +               clk_disable(bank->dbck);
                    ^^^^^^^^^^^^^^^^^^^^^^^^

this would be the corresponding disable.

> >   }
> > 
> >   static void omap_gpio_irq_bus_lock(struct irq_data *data)
> > @@ -827,6 +846,13 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
> >          struct gpio_bank *bank = gpiochip_get_data(chip);
> >          unsigned long flags;
> > 
> > +       /*
> > +        * Enable the clock here so that the nested clk_disable() in the
> > +        * following omap_clear_gpio_debounce() is lockless
> > +        */
> > +       if (bank->dbck_flag)
> > +               clk_enable(bank->dbck);
                    ^^^^^^^^^^^^^^^^^^^^^^
And for this second enable....

> > +
> >          raw_spin_lock_irqsave(&bank->lock, flags);
> >          bank->mod_usage &= ~(BIT(offset));
> >          if (!LINE_USED(bank->irq_usage, offset)) {
> > @@ -836,6 +862,9 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
> >          omap_disable_gpio_module(bank, offset);
> >          raw_spin_unlock_irqrestore(&bank->lock, flags);
> > 
> > +       if (bank->dbck_flag)
> > +               clk_disable(bank->dbck);
                    ^^^^^^^^^^^^^^^^^^^^^^^
... this would be the corresponding 2nd disable.

> > +
> >          pm_runtime_put(chip->parent);
> >   }
> > 

Aren't they paired from your PoV?
Bartosz Golaszewski Nov. 27, 2024, 10:04 a.m. UTC | #3
On Tue, Nov 26, 2024 at 9:59 PM Sverdlin, Alexander
<alexander.sverdlin@siemens.com> wrote:
>
> Hi Bartosz!
>
> On Tue, 2024-11-26 at 21:37 +0100, Bartosz Golaszewski wrote:
> > > @@ -647,6 +656,13 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
> > >          unsigned long flags;
> > >          unsigned offset = d->hwirq;
> > >
> > > +       /*
> > > +        * Enable the clock here so that the nested clk_disable() in the
> > > +        * following omap_clear_gpio_debounce() is lockless
> > > +        */
> > > +       if (bank->dbck_flag)
> > > +               clk_enable(bank->dbck);
> > > +
> >
> > But this looks like a functional change. You effectively bump the
> > clock enable count but don't add a corresponding clk_disable() in the
> > affected path. Is the clock ever actually disabled then?
> >
> > Am I not getting something?
>
> Actually I though I enable and disable them in the very same function, so for the
> first enable above...
>
> >
> > >          raw_spin_lock_irqsave(&bank->lock, flags);
> > >          bank->irq_usage &= ~(BIT(offset));
> > >          omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
> > > @@ -656,6 +672,9 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
> > >                  omap_clear_gpio_debounce(bank, offset);
> > >          omap_disable_gpio_module(bank, offset);
> > >          raw_spin_unlock_irqrestore(&bank->lock, flags);
> > > +
> > > +       if (bank->dbck_flag)
> > > +               clk_disable(bank->dbck);
>                     ^^^^^^^^^^^^^^^^^^^^^^^^
>
> this would be the corresponding disable.
>
> > >   }
> > >
> > >   static void omap_gpio_irq_bus_lock(struct irq_data *data)
> > > @@ -827,6 +846,13 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
> > >          struct gpio_bank *bank = gpiochip_get_data(chip);
> > >          unsigned long flags;
> > >
> > > +       /*
> > > +        * Enable the clock here so that the nested clk_disable() in the
> > > +        * following omap_clear_gpio_debounce() is lockless
> > > +        */
> > > +       if (bank->dbck_flag)
> > > +               clk_enable(bank->dbck);
>                     ^^^^^^^^^^^^^^^^^^^^^^
> And for this second enable....
>
> > > +
> > >          raw_spin_lock_irqsave(&bank->lock, flags);
> > >          bank->mod_usage &= ~(BIT(offset));
> > >          if (!LINE_USED(bank->irq_usage, offset)) {
> > > @@ -836,6 +862,9 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
> > >          omap_disable_gpio_module(bank, offset);
> > >          raw_spin_unlock_irqrestore(&bank->lock, flags);
> > >
> > > +       if (bank->dbck_flag)
> > > +               clk_disable(bank->dbck);
>                     ^^^^^^^^^^^^^^^^^^^^^^^
> ... this would be the corresponding 2nd disable.
>
> > > +
> > >          pm_runtime_put(chip->parent);
> > >   }
> > >
>
> Aren't they paired from your PoV?
>

Ok, I needed to take a long look at this driver.

IIUC what happens now:

In omap_gpio_irq_shutdown() and omap_gpio_free() you now enable the
clock (really just bumping its enable count) before entering
omap_clear_gpio_debounce() so that its internal call do clk_disable()
only decreases the reference count without actually disabling it and
the clock is really disabled one level up the stack.

If that's correct, isn't it unnecessarily convoluted?
omap_clear_gpio_debounce() is only called from these two functions so
why not just move the clk_disable() out of it and into them?

Bartosz
Sverdlin, Alexander Nov. 27, 2024, 10:42 a.m. UTC | #4
Thanks Bartosz for looking into it!

On Wed, 2024-11-27 at 11:04 +0100, Bartosz Golaszewski wrote:
> > > > @@ -827,6 +846,13 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
> > > >           struct gpio_bank *bank = gpiochip_get_data(chip);
> > > >           unsigned long flags;
> > > > 
> > > > +       /*
> > > > +        * Enable the clock here so that the nested clk_disable() in the
> > > > +        * following omap_clear_gpio_debounce() is lockless
> > > > +        */
> > > > +       if (bank->dbck_flag)
> > > > +               clk_enable(bank->dbck);
> >                      ^^^^^^^^^^^^^^^^^^^^^^
> > And for this second enable....
> > 
> > > > +
> > > >           raw_spin_lock_irqsave(&bank->lock, flags);
> > > >           bank->mod_usage &= ~(BIT(offset));
> > > >           if (!LINE_USED(bank->irq_usage, offset)) {
> > > > @@ -836,6 +862,9 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
> > > >           omap_disable_gpio_module(bank, offset);
> > > >           raw_spin_unlock_irqrestore(&bank->lock, flags);
> > > > 
> > > > +       if (bank->dbck_flag)
> > > > +               clk_disable(bank->dbck);
> >                      ^^^^^^^^^^^^^^^^^^^^^^^
> > ... this would be the corresponding 2nd disable.
> > 
> > > > +
> > > >           pm_runtime_put(chip->parent);
> > > >    }
> > > > 
> > 
> > Aren't they paired from your PoV?
> > 
> 
> Ok, I needed to take a long look at this driver.
> 
> IIUC what happens now:
> 
> In omap_gpio_irq_shutdown() and omap_gpio_free() you now enable the
> clock (really just bumping its enable count) before entering
> omap_clear_gpio_debounce() so that its internal call do clk_disable()
> only decreases the reference count without actually disabling it and
> the clock is really disabled one level up the stack.
> 
> If that's correct, isn't it unnecessarily convoluted?
> omap_clear_gpio_debounce() is only called from these two functions so
> why not just move the clk_disable() out of it and into them?

Seems that I didn't notice the elephant in the room, so let me rework it ;-)
diff mbox series

Patch

diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 7ad4534054962..f9e502aa57753 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -181,6 +181,7 @@  static inline void omap_gpio_dbck_disable(struct gpio_bank *bank)
 static int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
 				   unsigned debounce)
 {
+	unsigned long		flags;
 	u32			val;
 	u32			l;
 	bool			enable = !!debounce;
@@ -196,13 +197,18 @@  static int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
 
 	l = BIT(offset);
 
+	/*
+	 * Ordering is important here: clk_enable() calls spin_lock_irqsave(),
+	 * therefore it must be outside of the following raw_spin_lock_irqsave()
+	 */
 	clk_enable(bank->dbck);
+	raw_spin_lock_irqsave(&bank->lock, flags);
+
 	writel_relaxed(debounce, bank->base + bank->regs->debounce);
 
 	val = omap_gpio_rmw(bank->base + bank->regs->debounce_en, l, enable);
 	bank->dbck_enable_mask = val;
 
-	clk_disable(bank->dbck);
 	/*
 	 * Enable debounce clock per module.
 	 * This call is mandatory because in omap_gpio_request() when
@@ -217,6 +223,9 @@  static int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
 		bank->context.debounce_en = val;
 	}
 
+	raw_spin_unlock_irqrestore(&bank->lock, flags);
+	clk_disable(bank->dbck);
+
 	return 0;
 }
 
@@ -647,6 +656,13 @@  static void omap_gpio_irq_shutdown(struct irq_data *d)
 	unsigned long flags;
 	unsigned offset = d->hwirq;
 
+	/*
+	 * Enable the clock here so that the nested clk_disable() in the
+	 * following omap_clear_gpio_debounce() is lockless
+	 */
+	if (bank->dbck_flag)
+		clk_enable(bank->dbck);
+
 	raw_spin_lock_irqsave(&bank->lock, flags);
 	bank->irq_usage &= ~(BIT(offset));
 	omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
@@ -656,6 +672,9 @@  static void omap_gpio_irq_shutdown(struct irq_data *d)
 		omap_clear_gpio_debounce(bank, offset);
 	omap_disable_gpio_module(bank, offset);
 	raw_spin_unlock_irqrestore(&bank->lock, flags);
+
+	if (bank->dbck_flag)
+		clk_disable(bank->dbck);
 }
 
 static void omap_gpio_irq_bus_lock(struct irq_data *data)
@@ -827,6 +846,13 @@  static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
 	struct gpio_bank *bank = gpiochip_get_data(chip);
 	unsigned long flags;
 
+	/*
+	 * Enable the clock here so that the nested clk_disable() in the
+	 * following omap_clear_gpio_debounce() is lockless
+	 */
+	if (bank->dbck_flag)
+		clk_enable(bank->dbck);
+
 	raw_spin_lock_irqsave(&bank->lock, flags);
 	bank->mod_usage &= ~(BIT(offset));
 	if (!LINE_USED(bank->irq_usage, offset)) {
@@ -836,6 +862,9 @@  static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
 	omap_disable_gpio_module(bank, offset);
 	raw_spin_unlock_irqrestore(&bank->lock, flags);
 
+	if (bank->dbck_flag)
+		clk_disable(bank->dbck);
+
 	pm_runtime_put(chip->parent);
 }
 
@@ -913,15 +942,11 @@  static int omap_gpio_debounce(struct gpio_chip *chip, unsigned offset,
 			      unsigned debounce)
 {
 	struct gpio_bank *bank;
-	unsigned long flags;
 	int ret;
 
 	bank = gpiochip_get_data(chip);
 
-	raw_spin_lock_irqsave(&bank->lock, flags);
 	ret = omap2_set_gpio_debounce(bank, offset, debounce);
-	raw_spin_unlock_irqrestore(&bank->lock, flags);
-
 	if (ret)
 		dev_info(chip->parent,
 			 "Could not set line %u debounce to %u microseconds (%d)",