Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf613871 authored by Marc Zyngier's avatar Marc Zyngier Committed by Jason Cooper
Browse files

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: default avatarMarc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/1425659870-11832-6-git-send-email-marc.zyngier@arm.com


Signed-off-by: default avatarJason Cooper <jason@lakedaemon.net>
parent 3e39e8f5
Loading
Loading
Loading
Loading
+12 −8
Original line number Original line Diff line number Diff line
@@ -154,23 +154,25 @@ static inline unsigned int gic_irq(struct irq_data *d)
static void gic_mask_irq(struct irq_data *d)
static void gic_mask_irq(struct irq_data *d)
{
{
	u32 mask = 1 << (gic_irq(d) % 32);
	u32 mask = 1 << (gic_irq(d) % 32);
	unsigned long flags;


	raw_spin_lock(&irq_controller_lock);
	raw_spin_lock_irqsave(&irq_controller_lock, flags);
	writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);
	writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);
	if (gic_arch_extn.irq_mask)
	if (gic_arch_extn.irq_mask)
		gic_arch_extn.irq_mask(d);
		gic_arch_extn.irq_mask(d);
	raw_spin_unlock(&irq_controller_lock);
	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
}


static void gic_unmask_irq(struct irq_data *d)
static void gic_unmask_irq(struct irq_data *d)
{
{
	u32 mask = 1 << (gic_irq(d) % 32);
	u32 mask = 1 << (gic_irq(d) % 32);
	unsigned long flags;


	raw_spin_lock(&irq_controller_lock);
	raw_spin_lock_irqsave(&irq_controller_lock, flags);
	if (gic_arch_extn.irq_unmask)
	if (gic_arch_extn.irq_unmask)
		gic_arch_extn.irq_unmask(d);
		gic_arch_extn.irq_unmask(d);
	writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
	writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
	raw_spin_unlock(&irq_controller_lock);
	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
}


static void gic_eoi_irq(struct irq_data *d)
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)
{
{
	void __iomem *base = gic_dist_base(d);
	void __iomem *base = gic_dist_base(d);
	unsigned int gicirq = gic_irq(d);
	unsigned int gicirq = gic_irq(d);
	unsigned long flags;
	int ret;
	int ret;


	/* Interrupt configuration for SGIs can't be changed */
	/* Interrupt configuration for SGIs can't be changed */
@@ -199,14 +202,14 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
			    type != IRQ_TYPE_EDGE_RISING)
			    type != IRQ_TYPE_EDGE_RISING)
		return -EINVAL;
		return -EINVAL;


	raw_spin_lock(&irq_controller_lock);
	raw_spin_lock_irqsave(&irq_controller_lock, flags);


	if (gic_arch_extn.irq_set_type)
	if (gic_arch_extn.irq_set_type)
		gic_arch_extn.irq_set_type(d, type);
		gic_arch_extn.irq_set_type(d, type);


	ret = gic_configure_irq(gicirq, type, base, NULL);
	ret = gic_configure_irq(gicirq, type, base, NULL);


	raw_spin_unlock(&irq_controller_lock);
	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);


	return ret;
	return ret;
}
}
@@ -227,6 +230,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
	unsigned int cpu, shift = (gic_irq(d) % 4) * 8;
	unsigned int cpu, shift = (gic_irq(d) % 4) * 8;
	u32 val, mask, bit;
	u32 val, mask, bit;
	unsigned long flags;


	if (!force)
	if (!force)
		cpu = cpumask_any_and(mask_val, cpu_online_mask);
		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,
	if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
	if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
		return -EINVAL;
		return -EINVAL;


	raw_spin_lock(&irq_controller_lock);
	raw_spin_lock_irqsave(&irq_controller_lock, flags);
	mask = 0xff << shift;
	mask = 0xff << shift;
	bit = gic_cpu_map[cpu] << shift;
	bit = gic_cpu_map[cpu] << shift;
	val = readl_relaxed(reg) & ~mask;
	val = readl_relaxed(reg) & ~mask;
	writel_relaxed(val | bit, reg);
	writel_relaxed(val | bit, reg);
	raw_spin_unlock(&irq_controller_lock);
	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);


	return IRQ_SET_MASK_OK;
	return IRQ_SET_MASK_OK;
}
}