diff mbox

[2/7] gpio: brcmstb: release the bgpio lock during irq handlers

Message ID 20170930034057.15166-3-opendmb@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Doug Berger Sept. 30, 2017, 3:40 a.m. UTC
The basic memory-mapped GPIO controller lock must be released
before calling the registered GPIO interrupt handlers to allow
the interrupt handlers to access the hardware.  Otherwise, the
hardware accesses will deadlock when they attempt to grab the
lock.

Since the lock is only needed to protect the calculation of
unmasked pending interrupts create a dedicated function to
perform this and hide the complexity.

Fixes: 19a7b6940b78 ("gpio: brcmstb: Add interrupt and wakeup source support")
Signed-off-by: Doug Berger <opendmb@gmail.com>
---
 drivers/gpio/gpio-brcmstb.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

Comments

Gregory Fong Oct. 4, 2017, 1:55 a.m. UTC | #1
Hi Doug,

On Fri, Sep 29, 2017 at 8:40 PM, Doug Berger <opendmb@gmail.com> wrote:
> The basic memory-mapped GPIO controller lock must be released
> before calling the registered GPIO interrupt handlers to allow
> the interrupt handlers to access the hardware.  Otherwise, the
> hardware accesses will deadlock when they attempt to grab the
> lock.

I was having some trouble understanding exactly what the problem was
here, but I think I see it now.  Since this locks the entire bank,
where some GPIOs might be set as inputs and some as inputs (and
interrupt sources), then an interrupt on a GPIO that is supposed to
set another GPIO in the bank would result in deadlock.  Is that
correct?  If so, please update the commit message to make that clear,
and nice fix.  If not that, it would be nice to know what scenario can
cause a problem.

Thanks,
Gregory
Doug Berger Oct. 4, 2017, 2:09 a.m. UTC | #2
On 10/03/2017 06:55 PM, Gregory Fong wrote:
> Hi Doug,
> 
> On Fri, Sep 29, 2017 at 8:40 PM, Doug Berger <opendmb@gmail.com> wrote:
>> The basic memory-mapped GPIO controller lock must be released
>> before calling the registered GPIO interrupt handlers to allow
>> the interrupt handlers to access the hardware.  Otherwise, the
>> hardware accesses will deadlock when they attempt to grab the
>> lock.
> 
> I was having some trouble understanding exactly what the problem was
> here, but I think I see it now.  Since this locks the entire bank,
> where some GPIOs might be set as inputs and some as inputs (and
> interrupt sources), then an interrupt on a GPIO that is supposed to
> set another GPIO in the bank would result in deadlock.  Is that
> correct?  If so, please update the commit message to make that clear,
> and nice fix.  If not that, it would be nice to know what scenario can
> cause a problem.

That is an example, but there are really many possibilities.

Basically, if a registered interrupt handler wants to access its GPIO
you are likely to run into trouble.  Another example might be an
interrupt that is configured to trigger on either edge transition and
the handler wants to know whether the input is currently high or low.

I can submit a V2 with a change in the description if you would like,
but I'm not sure what the clearest example would be.

> 
> Thanks,
> Gregory
> 

Thanks for the feedback,
    Doug
Gregory Fong Oct. 4, 2017, 3:07 a.m. UTC | #3
On Tue, Oct 3, 2017 at 7:09 PM, Doug Berger <opendmb@gmail.com> wrote:
> On 10/03/2017 06:55 PM, Gregory Fong wrote:
>> Hi Doug,
>>
>> On Fri, Sep 29, 2017 at 8:40 PM, Doug Berger <opendmb@gmail.com> wrote:
>>> The basic memory-mapped GPIO controller lock must be released
>>> before calling the registered GPIO interrupt handlers to allow
>>> the interrupt handlers to access the hardware.  Otherwise, the
>>> hardware accesses will deadlock when they attempt to grab the
>>> lock.
>>
>> I was having some trouble understanding exactly what the problem was
>> here, but I think I see it now.  Since this locks the entire bank,
>> where some GPIOs might be set as inputs and some as inputs (and
>> interrupt sources), then an interrupt on a GPIO that is supposed to
>> set another GPIO in the bank would result in deadlock.  Is that
>> correct?  If so, please update the commit message to make that clear,
>> and nice fix.  If not that, it would be nice to know what scenario can
>> cause a problem.
>
> That is an example, but there are really many possibilities.
>
> Basically, if a registered interrupt handler wants to access its GPIO
> you are likely to run into trouble.  Another example might be an
> interrupt that is configured to trigger on either edge transition and
> the handler wants to know whether the input is currently high or low.
>
> I can submit a V2 with a change in the description if you would like,
> but I'm not sure what the clearest example would be.

If you could just mention both of these possible cases, that would be
great! With that change,

Acked-by: Gregory Fong <gregory.0xf0@gmail.com>
diff mbox

Patch

diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index 7f39b160a4d5..8945861876f9 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -62,6 +62,21 @@  brcmstb_gpio_gc_to_priv(struct gpio_chip *gc)
 	return bank->parent_priv;
 }
 
+static unsigned long
+brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank)
+{
+	void __iomem *reg_base = bank->parent_priv->reg_base;
+	unsigned long status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
+	status = bank->gc.read_reg(reg_base + GIO_STAT(bank->id)) &
+		 bank->gc.read_reg(reg_base + GIO_MASK(bank->id));
+	spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
+
+	return status;
+}
+
 static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
 		unsigned int offset, bool enable)
 {
@@ -204,11 +219,8 @@  static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank)
 	struct irq_domain *irq_domain = bank->gc.irqdomain;
 	void __iomem *reg_base = priv->reg_base;
 	unsigned long status;
-	unsigned long flags;
 
-	spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
-	while ((status = bank->gc.read_reg(reg_base + GIO_STAT(bank->id)) &
-			 bank->gc.read_reg(reg_base + GIO_MASK(bank->id)))) {
+	while ((status = brcmstb_gpio_get_active_irqs(bank))) {
 		int bit;
 
 		for_each_set_bit(bit, &status, 32) {
@@ -223,7 +235,6 @@  static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank)
 			generic_handle_irq(irq_find_mapping(irq_domain, bit));
 		}
 	}
-	spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
 }
 
 /* Each UPG GIO block has one IRQ for all banks */