diff options
| author | Marc Zyngier <marc.zyngier@arm.com> | 2015-03-06 11:37:44 -0500 |
|---|---|---|
| committer | Jason Cooper <jason@lakedaemon.net> | 2015-03-08 00:33:06 -0500 |
| commit | cf613871946230c5dd8178d07bcdc2984f4545cd (patch) | |
| tree | a74f34ffbd961798a4e0432335f57b933b7401e2 /drivers | |
| parent | 3e39e8f56c1c67cdd1e8f06da0d6b7c831818c76 (diff) | |
irqchip: gic: Fix unsafe locking reported by lockdep
When compiled with CONFIG_LOCKDEP, the kernel shouts badly, saying
that the locking in the GIC code is unsafe. I'm afraid the kernel
is right:
CPU0
----
lock(irq_controller_lock);
<Interrupt>
lock(irq_controller_lock);
*** DEADLOCK ***
This can happen while enabling, disabling, setting the type
or the affinity of an interrupt.
The fix is to take the interrupt_controller_lock with interrupts
disabled in these cases.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/1425659870-11832-6-git-send-email-marc.zyngier@arm.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/irqchip/irq-gic.c | 20 |
1 files changed, 12 insertions, 8 deletions
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4634cf7d0ec3..471e1cdc1933 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c | |||
| @@ -154,23 +154,25 @@ static inline unsigned int gic_irq(struct irq_data *d) | |||
| 154 | static void gic_mask_irq(struct irq_data *d) | 154 | static void gic_mask_irq(struct irq_data *d) |
| 155 | { | 155 | { |
| 156 | u32 mask = 1 << (gic_irq(d) % 32); | 156 | u32 mask = 1 << (gic_irq(d) % 32); |
| 157 | unsigned long flags; | ||
| 157 | 158 | ||
| 158 | raw_spin_lock(&irq_controller_lock); | 159 | raw_spin_lock_irqsave(&irq_controller_lock, flags); |
| 159 | writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); | 160 | writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); |
| 160 | if (gic_arch_extn.irq_mask) | 161 | if (gic_arch_extn.irq_mask) |
| 161 | gic_arch_extn.irq_mask(d); | 162 | gic_arch_extn.irq_mask(d); |
| 162 | raw_spin_unlock(&irq_controller_lock); | 163 | raw_spin_unlock_irqrestore(&irq_controller_lock, flags); |
| 163 | } | 164 | } |
| 164 | 165 | ||
| 165 | static void gic_unmask_irq(struct irq_data *d) | 166 | static void gic_unmask_irq(struct irq_data *d) |
| 166 | { | 167 | { |
| 167 | u32 mask = 1 << (gic_irq(d) % 32); | 168 | u32 mask = 1 << (gic_irq(d) % 32); |
| 169 | unsigned long flags; | ||
| 168 | 170 | ||
| 169 | raw_spin_lock(&irq_controller_lock); | 171 | raw_spin_lock_irqsave(&irq_controller_lock, flags); |
| 170 | if (gic_arch_extn.irq_unmask) | 172 | if (gic_arch_extn.irq_unmask) |
| 171 | gic_arch_extn.irq_unmask(d); | 173 | gic_arch_extn.irq_unmask(d); |
| 172 | writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); | 174 | writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); |
| 173 | raw_spin_unlock(&irq_controller_lock); | 175 | raw_spin_unlock_irqrestore(&irq_controller_lock, flags); |
| 174 | } | 176 | } |
| 175 | 177 | ||
| 176 | static void gic_eoi_irq(struct irq_data *d) | 178 | static void gic_eoi_irq(struct irq_data *d) |
| @@ -188,6 +190,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type) | |||
| 188 | { | 190 | { |
| 189 | void __iomem *base = gic_dist_base(d); | 191 | void __iomem *base = gic_dist_base(d); |
| 190 | unsigned int gicirq = gic_irq(d); | 192 | unsigned int gicirq = gic_irq(d); |
| 193 | unsigned long flags; | ||
| 191 | int ret; | 194 | int ret; |
| 192 | 195 | ||
| 193 | /* Interrupt configuration for SGIs can't be changed */ | 196 | /* Interrupt configuration for SGIs can't be changed */ |
| @@ -199,14 +202,14 @@ static int gic_set_type(struct irq_data *d, unsigned int type) | |||
| 199 | type != IRQ_TYPE_EDGE_RISING) | 202 | type != IRQ_TYPE_EDGE_RISING) |
| 200 | return -EINVAL; | 203 | return -EINVAL; |
| 201 | 204 | ||
| 202 | raw_spin_lock(&irq_controller_lock); | 205 | raw_spin_lock_irqsave(&irq_controller_lock, flags); |
| 203 | 206 | ||
| 204 | if (gic_arch_extn.irq_set_type) | 207 | if (gic_arch_extn.irq_set_type) |
| 205 | gic_arch_extn.irq_set_type(d, type); | 208 | gic_arch_extn.irq_set_type(d, type); |
| 206 | 209 | ||
| 207 | ret = gic_configure_irq(gicirq, type, base, NULL); | 210 | ret = gic_configure_irq(gicirq, type, base, NULL); |
| 208 | 211 | ||
| 209 | raw_spin_unlock(&irq_controller_lock); | 212 | raw_spin_unlock_irqrestore(&irq_controller_lock, flags); |
| 210 | 213 | ||
| 211 | return ret; | 214 | return ret; |
| 212 | } | 215 | } |
| @@ -227,6 +230,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, | |||
| 227 | void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); | 230 | void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); |
| 228 | unsigned int cpu, shift = (gic_irq(d) % 4) * 8; | 231 | unsigned int cpu, shift = (gic_irq(d) % 4) * 8; |
| 229 | u32 val, mask, bit; | 232 | u32 val, mask, bit; |
| 233 | unsigned long flags; | ||
| 230 | 234 | ||
| 231 | if (!force) | 235 | if (!force) |
| 232 | cpu = cpumask_any_and(mask_val, cpu_online_mask); | 236 | cpu = cpumask_any_and(mask_val, cpu_online_mask); |
| @@ -236,12 +240,12 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, | |||
| 236 | if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids) | 240 | if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids) |
| 237 | return -EINVAL; | 241 | return -EINVAL; |
| 238 | 242 | ||
| 239 | raw_spin_lock(&irq_controller_lock); | 243 | raw_spin_lock_irqsave(&irq_controller_lock, flags); |
| 240 | mask = 0xff << shift; | 244 | mask = 0xff << shift; |
| 241 | bit = gic_cpu_map[cpu] << shift; | 245 | bit = gic_cpu_map[cpu] << shift; |
| 242 | val = readl_relaxed(reg) & ~mask; | 246 | val = readl_relaxed(reg) & ~mask; |
| 243 | writel_relaxed(val | bit, reg); | 247 | writel_relaxed(val | bit, reg); |
| 244 | raw_spin_unlock(&irq_controller_lock); | 248 | raw_spin_unlock_irqrestore(&irq_controller_lock, flags); |
| 245 | 249 | ||
| 246 | return IRQ_SET_MASK_OK; | 250 | return IRQ_SET_MASK_OK; |
| 247 | } | 251 | } |
