diff options
author | Alexandre Courbot <acourbot@nvidia.com> | 2015-08-17 04:23:52 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2015-08-17 04:56:24 -0400 |
commit | 7fa25937542358bfa01ef5c5a1e9a00bd164c000 (patch) | |
tree | d4edc6026912b6b8ad9450aa84f26f06d4b23794 /drivers/gpio/gpio-grgpio.c | |
parent | ac49fbd1f9d437cca7234473850aef4165779383 (diff) |
gpio/grgpio: fix deadlock in grgpio_irq_unmap()
As reported by Alexey Khoroshilov:
grgpio_irq_unmap() code looks quite suspicious regarding usage of
priv->bgc.lock spinlock.
It locks the spinlock in line 310:
spin_lock_irqsave(&priv->bgc.lock, flags);
and then it can call grgpio_set_imask() in line 317:
grgpio_set_imask(priv, i, 0);
But grgpio_set_imask() unconditionally locks the spinlock by itself.
Fix this by moving the spinlock acquisition outside of
grgpio_set_imask().
Found by Linux Driver Verification project (linuxtesting.org).
Reported-by: Alexey Khoroshilov <khoroshilov@ispras.ru>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Cc: Alexey Khoroshilov <khoroshilov@ispras.ru>
Cc: Andreas Larsson <andreas@gaisler.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio/gpio-grgpio.c')
-rw-r--r-- | drivers/gpio/gpio-grgpio.c | 15 |
1 files changed, 10 insertions, 5 deletions
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 77053d61466e..801423fe8143 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c | |||
@@ -104,17 +104,12 @@ static void grgpio_set_imask(struct grgpio_priv *priv, unsigned int offset, | |||
104 | { | 104 | { |
105 | struct bgpio_chip *bgc = &priv->bgc; | 105 | struct bgpio_chip *bgc = &priv->bgc; |
106 | unsigned long mask = bgc->pin2mask(bgc, offset); | 106 | unsigned long mask = bgc->pin2mask(bgc, offset); |
107 | unsigned long flags; | ||
108 | |||
109 | spin_lock_irqsave(&bgc->lock, flags); | ||
110 | 107 | ||
111 | if (val) | 108 | if (val) |
112 | priv->imask |= mask; | 109 | priv->imask |= mask; |
113 | else | 110 | else |
114 | priv->imask &= ~mask; | 111 | priv->imask &= ~mask; |
115 | bgc->write_reg(priv->regs + GRGPIO_IMASK, priv->imask); | 112 | bgc->write_reg(priv->regs + GRGPIO_IMASK, priv->imask); |
116 | |||
117 | spin_unlock_irqrestore(&bgc->lock, flags); | ||
118 | } | 113 | } |
119 | 114 | ||
120 | static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset) | 115 | static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset) |
@@ -180,16 +175,26 @@ static void grgpio_irq_mask(struct irq_data *d) | |||
180 | { | 175 | { |
181 | struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); | 176 | struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); |
182 | int offset = d->hwirq; | 177 | int offset = d->hwirq; |
178 | unsigned long flags; | ||
179 | |||
180 | spin_lock_irqsave(&priv->bgc.lock, flags); | ||
183 | 181 | ||
184 | grgpio_set_imask(priv, offset, 0); | 182 | grgpio_set_imask(priv, offset, 0); |
183 | |||
184 | spin_unlock_irqrestore(&priv->bgc.lock, flags); | ||
185 | } | 185 | } |
186 | 186 | ||
187 | static void grgpio_irq_unmask(struct irq_data *d) | 187 | static void grgpio_irq_unmask(struct irq_data *d) |
188 | { | 188 | { |
189 | struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); | 189 | struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); |
190 | int offset = d->hwirq; | 190 | int offset = d->hwirq; |
191 | unsigned long flags; | ||
192 | |||
193 | spin_lock_irqsave(&priv->bgc.lock, flags); | ||
191 | 194 | ||
192 | grgpio_set_imask(priv, offset, 1); | 195 | grgpio_set_imask(priv, offset, 1); |
196 | |||
197 | spin_unlock_irqrestore(&priv->bgc.lock, flags); | ||
193 | } | 198 | } |
194 | 199 | ||
195 | static struct irq_chip grgpio_irq_chip = { | 200 | static struct irq_chip grgpio_irq_chip = { |