[linux-yocto] [PATCH 31/52] arch/arm/mach-axxia: fixed hang in preempt kernel

Daniel Dragomir daniel.dragomir at windriver.com
Wed Jan 28 09:18:45 PST 2015


From: John Jacques <john.jacques at lsi.com>

Fixed the hang in the affinity code. The selection of a cpu on a
clear affinity did not have the right mask, causing the affinity
to be moved to a cpu that was not powered up.

Signed-off-by: John Jacques <john.jacques at lsi.com>
---
 arch/arm/mach-axxia/axxia-gic.c | 242 +++++++++++++++++++++-------------------
 1 file changed, 130 insertions(+), 112 deletions(-)

diff --git a/arch/arm/mach-axxia/axxia-gic.c b/arch/arm/mach-axxia/axxia-gic.c
index 6345a99..34764b0 100644
--- a/arch/arm/mach-axxia/axxia-gic.c
+++ b/arch/arm/mach-axxia/axxia-gic.c
@@ -228,22 +228,23 @@ static void axxia_gic_flush_affinity_queue(struct work_struct *dummy)
 	void *qdata;
 	struct gic_rpc_data *rpc_data;
 
-
 	while (axxia_get_item(&axxia_circ_q, &qdata) != -1) {
+
 		rpc_data = (struct gic_rpc_data *) qdata;
 		if (rpc_data->func_mask == SET_AFFINITY) {
-			smp_call_function_single(rpc_data->cpu,
-					gic_set_affinity_remote,
-					qdata, 1);
-
+			if (cpu_online(rpc_data->cpu)) {
+				smp_call_function_single(rpc_data->cpu, gic_set_affinity_remote,
+						qdata, 1);
+			}
 		} else if (rpc_data->func_mask == CLR_AFFINITY) {
-
-			smp_call_function_single(rpc_data->cpu,
-					gic_clr_affinity_remote,
-					qdata, 1);
+			if (cpu_online(rpc_data->cpu)) {
+				smp_call_function_single(rpc_data->cpu, gic_clr_affinity_remote,
+						qdata, 1);
+			}
 		}
 		kfree(qdata);
 	}
+
 }
 
 /*
@@ -482,35 +483,45 @@ static int gic_retrigger(struct irq_data *d)
 	return -ENXIO;
 }
 
-static int _gic_clear_affinity(struct irq_data *d, u32 cpu, bool update_enable)
+static void gic_set_irq_target(void __iomem *dist_base,
+		u32 irqid, u32 cpu, bool set)
 {
-
-	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
-	unsigned int shift = (gic_irq(d) % 4) * 8;
+	void __iomem *reg;
+	unsigned int shift;
 	u32 val;
 	u32 mask = 0;
-	u32 enable_mask, enable_offset;
+	u32 bit;
 
+	reg =  dist_base + GIC_DIST_TARGET + (irqid & ~3);
+	shift = (irqid % 4) * 8;
 	mask = 0xff << shift;
 
-	enable_mask = 1 << (gic_irq(d) % 32);
-	enable_offset = 4 * (gic_irq(d) / 32);
+	val = readl_relaxed(reg) & ~mask;
+
+	if (!set)
+		/* Clear affinity, mask IRQ. */
+		writel_relaxed(val, reg);
+	else {
+		bit = 1 << ((cpu_logical_map(cpu) % CORES_PER_CLUSTER) + shift);
+		writel_relaxed(val | bit, reg);
+	}
+
+}
+
+static int _gic_clear_affinity(struct irq_data *d, u32 cpu, bool update_enable)
+{
+
+	u32 enable_mask, enable_offset;
 
 	raw_spin_lock(&irq_controller_lock);
 
-	val = readl_relaxed(reg) & ~mask;
-	/* Clear affinity, mask IRQ. */
-	writel_relaxed(val, reg);
+	gic_set_irq_target(gic_dist_base(d), gic_irq(d), cpu, false);
 
 	if (update_enable) {
-
-		writel_relaxed(enable_mask,
-				gic_data_dist_base(&gic_data) + GIC_DIST_PENDING_CLEAR + enable_offset);
-		writel_relaxed(enable_mask,
-				gic_data_dist_base(&gic_data) + GIC_DIST_ACTIVE_CLEAR + enable_offset);
+		enable_mask = 1 << (gic_irq(d) % 32);
+		enable_offset = 4 * (gic_irq(d) / 32);
 		writel_relaxed(enable_mask,
 				gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_CLEAR + enable_offset);
-
 	}
 
 	raw_spin_unlock(&irq_controller_lock);
@@ -523,33 +534,17 @@ static int _gic_set_affinity(struct irq_data *d,
 			     u32 cpu,
 			     bool update_enable)
 {
-	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
-	unsigned int shift = (gic_irq(d) % 4) * 8;
-	u32 val;
-	u32 mask = 0;
-	u32 bit;
 	u32 enable_mask, enable_offset;
 
-	/*
-	 * Normalize the cpu number as seen by Linux (0-15) to a
-	 * number as seen by a cluster (0-3).
-	 */
-	bit = 1 << ((cpu_logical_map(cpu) % CORES_PER_CLUSTER) + shift);
-	mask = 0xff << shift;
-
-	enable_mask = 1 << (gic_irq(d) % 32);
-	enable_offset = 4 * (gic_irq(d) / 32);
-
 	raw_spin_lock(&irq_controller_lock);
 
-	val = readl_relaxed(reg) & ~mask;
-	/* Set affinity, mask IRQ. */
-	writel_relaxed(val | bit, reg);
+	gic_set_irq_target(gic_dist_base(d), gic_irq(d), cpu, true);
 
 	if (update_enable) {
+		enable_mask = 1 << (gic_irq(d) % 32);
+		enable_offset = 4 * (gic_irq(d) / 32);
 		writel_relaxed(enable_mask,
-			gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_SET
-					+ enable_offset);
+				gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_SET + enable_offset);
 	}
 
 	raw_spin_unlock(&irq_controller_lock);
@@ -580,11 +575,11 @@ static int gic_set_affinity(struct irq_data *d,
 			    const struct cpumask *mask_val,
 			    bool force)
 {
-	u32 pcpu = cpu_logical_map(smp_processor_id());
-	unsigned int irqid = gic_irq(d);
-	struct cpumask *affinity_mask = (struct cpumask *)mask_val;
+	u32 pcpu;
+	unsigned int irqid;
+	struct cpumask *affinity_mask;
 	u32 mask;
-	u32 oldcpu = irq_cpuid[irqid];
+	u32 oldcpu;
 	struct gic_rpc_data *gic_rpc_ptr;
 	int rval;
 	bool new_same_core = false;
@@ -597,6 +592,12 @@ static int gic_set_affinity(struct irq_data *d,
 
 	BUG_ON(!irqs_disabled());
 
+
+	pcpu = cpu_logical_map(smp_processor_id());
+	irqid = gic_irq(d);
+	affinity_mask = (struct cpumask *)mask_val;
+	oldcpu = irq_cpuid[irqid];
+
 	if (irqid >= MAX_GIC_INTERRUPTS)
 		return -EINVAL;
 
@@ -604,7 +605,6 @@ static int gic_set_affinity(struct irq_data *d,
 	if ((irqid >= IPI0_CPU0) && (irqid < MAX_AXM_IPI_NUM))
 		return IRQ_SET_MASK_OK;
 
-
 	if (force)
 		add_cpu = cpumask_any(cpu_online_mask);
 	else
@@ -650,25 +650,21 @@ static int gic_set_affinity(struct irq_data *d,
 		}
 	}
 
-	mutex_lock(&affinity_lock);
-
-	/* Update Axxia IRQ affinity table with the new physical CPU number. */
-	irq_cpuid[irqid] = cpu_logical_map(add_cpu);
 
 	/*
 	 * We clear first to make sure the affinity mask always has a bit set,
 	 * especially when the two cpus are in the same cluster.
 	 */
 	if (irqid != IRQ_PMU) {
-
 		if (clear_needed == AFFINITY_CLEAR_LOCAL) {
 
 			_gic_clear_affinity(d, del_cpu, update_enable);
 
 		} else if (clear_needed == AFFINITY_CLEAR_OTHER_CLUSTER) {
 
-			mask = 0xf << (oldcpu / CORES_PER_CLUSTER);
-			del_cpu = cpumask_any_and((struct cpumask *)&mask, cpu_online_mask);
+			mask = 0xf << ((oldcpu / CORES_PER_CLUSTER) * 4);
+			del_cpu = cpumask_any_and((struct cpumask *)&mask,
+					cpu_online_mask);
 
 			if (del_cpu < nr_cpu_ids) {
 
@@ -685,19 +681,23 @@ static int gic_set_affinity(struct irq_data *d,
 				gic_rpc_ptr->oldcpu = oldcpu;
 				gic_rpc_ptr->d = d;
 				gic_rpc_ptr->update_enable = update_enable;
+				get_cpu();
 				rval = axxia_put_item(&axxia_circ_q, (void *) gic_rpc_ptr);
+				put_cpu();
 				if (rval) {
 					pr_err(
 							"ERROR: failed to add CLR_AFFINITY request for cpu: %d\n",
 							del_cpu);
 					kfree((void *) gic_rpc_ptr);
+					mutex_unlock(&affinity_lock);
+					return rval;
 				}
 				schedule_work_on(0, &axxia_gic_affinity_work);
-			}
+			} else
+				pr_err("ERROR: no CPUs left\n");
 		}
 	}
 
-
 	if (set_needed == AFFINITY_SET_LOCAL) {
 
 		_gic_set_affinity(d, add_cpu, update_enable);
@@ -716,19 +716,22 @@ static int gic_set_affinity(struct irq_data *d,
 		gic_rpc_ptr->cpu = add_cpu;
 		gic_rpc_ptr->update_enable = update_enable;
 		gic_rpc_ptr->d = d;
+		get_cpu();
 		rval = axxia_put_item(&axxia_circ_q, (void *) gic_rpc_ptr);
+		put_cpu();
 		if (rval) {
 			pr_err("ERROR: failed to add SET_AFFINITY request for cpu: %d\n",
 					add_cpu);
 			kfree((void *) gic_rpc_ptr);
+			mutex_unlock(&affinity_lock);
+			return rval;
 		}
 		schedule_work_on(0, &axxia_gic_affinity_work);
 
 	}
 
-
-	mutex_unlock(&affinity_lock);
-
+	/* Update Axxia IRQ affinity table with the new physical CPU number. */
+	irq_cpuid[irqid] = cpu_logical_map(add_cpu);
 
 	return IRQ_SET_MASK_OK;
 }
@@ -1206,10 +1209,9 @@ static void __init gic_axxia_init(struct gic_chip_data *gic)
 		writel_relaxed(cpumask, ipi_mask_reg_base + 0x40 + i * 4);
 }
 
-static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
+static void  gic_dist_init(struct gic_chip_data *gic)
 {
 	unsigned int i;
-	u32 cpumask;
 	unsigned int gic_irqs = gic->gic_irqs;
 	void __iomem *base = gic_data_dist_base(gic);
 	u32 cpu = cpu_logical_map(smp_processor_id());
@@ -1220,17 +1222,17 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
 	u32 enableoff;
 	u32 val;
 	u32 this_cluster = get_cluster_id();
+	u32 powered_on;
+	u32 ccpu;
 
 	/* Initialize the distributor interface once per CPU cluster */
 	if ((test_and_set_bit(get_cluster_id(), &gic->dist_init_done)) && (!cluster_power_up[this_cluster]))
 		return;
 
-	cpumask = 1 << cpu;
-	cpumask |= cpumask << 8;
-	cpumask |= cpumask << 16;
-
 	writel_relaxed(0, base + GIC_DIST_CTRL);
 
+	/*################################# CONFIG IRQS ####################################*/
+
 	/*
 	 * Set all global interrupts to be level triggered, active low.
 	 */
@@ -1238,13 +1240,23 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
 		writel_relaxed(0, base + GIC_DIST_CONFIG + i * 4 / 16);
 
 	/*
-	 * Set all global interrupts to this CPU only.
-	 * (Only do this for the first core on cluster 0).
+	 * Set Axxia IPI interrupts to be edge triggered.
 	 */
-	if (cpu == 0)
-		for (i = 32; i < gic_irqs; i += 4)
-			writel_relaxed(cpumask,
-				       base + GIC_DIST_TARGET + i * 4 / 4);
+	for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
+		confmask = 0x2 << ((i % 16) * 2);
+		confoff = (i / 16) * 4;
+		val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
+		val |= confmask;
+		writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
+	}
+
+	/*################################# PRIORITY  ####################################*/
+	/*
+	 * Set priority on PPI and SGI interrupts
+	 */
+	for (i = 0; i < 32; i += 4)
+		writel_relaxed(0xa0a0a0a0,
+				base + GIC_DIST_PRI + i * 4 / 4);
 
 	/*
 	 * Set priority on all global interrupts.
@@ -1252,52 +1264,43 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
 	for (i = 32; i < gic_irqs; i += 4)
 		writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
 
+
+	/*################################# TARGET ####################################*/
 	/*
-	 * Disable all interrupts.  Leave the PPI and SGIs alone
-	 * as these enables are banked registers.
+	 * Set all global interrupts to this CPU only.
+	 * (Only do this for the first core on cluster 0).
 	 */
-	for (i = 32; i < gic_irqs; i += 32) {
-		writel_relaxed(0xffffffff,
-			       base + GIC_DIST_ACTIVE_CLEAR + i * 4 / 32);
-		writel_relaxed(0xffffffff,
-			       base + GIC_DIST_PENDING_CLEAR + i * 4 / 32);
-		writel_relaxed(0xffffffff,
-			       base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
-	}
+	if (cpu == 0)
+		for (i = 32; i < gic_irqs; i += 4)
+			writel_relaxed(0x01010101, base + GIC_DIST_TARGET + i * 4 / 4);
 
 	/*
 	 * Set Axxia IPI interrupts for all CPUs in this cluster.
 	 */
+	powered_on = (~pm_cpu_powered_down) & 0xFFFF;
 	for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
 		cpumask_8 = 1 << ((i - IPI0_CPU0) % 4);
-		writeb_relaxed(cpumask_8, base + GIC_DIST_TARGET + i);
-	}
-
-	/*
-	 * Set the PMU IRQ to the first cpu in this cluster.
-	 */
-	writeb_relaxed(0x01, base + GIC_DIST_TARGET + IRQ_PMU);
-
-	/*
-	 * Set Axxia IPI interrupts to be edge triggered.
-	 */
-	for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
-		confmask = 0x2 << ((i % 16) * 2);
-		confoff = (i / 16) * 4;
-		val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
-		val |= confmask;
-		writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
+		ccpu = (this_cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+		if ((1 << ccpu) & powered_on)
+			writeb_relaxed(cpumask_8, base + GIC_DIST_TARGET + i);
+		else
+			writeb_relaxed(0x00, base + GIC_DIST_TARGET + i);
 	}
 
+	/*################################# ENABLE IRQS ####################################*/
 	/*
 	 * Do the initial enable of the Axxia IPI interrupts here.
 	 * NOTE: Writing a 0 to this register has no effect, so
 	 * no need to read and OR in bits, just writing is OK.
 	 */
+
+	powered_on = (~pm_cpu_powered_down) & 0xFFFF;
 	for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
 		enablemask = 1 << (i % 32);
 		enableoff = (i / 32) * 4;
-		writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
+		ccpu = (this_cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+		if ((1 << ccpu) & powered_on)
+			writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
 	}
 
 	/*
@@ -1307,32 +1310,47 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
 	enableoff = (IRQ_PMU / 32) * 4;
 	writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
 
+
 	writel_relaxed(1, base + GIC_DIST_CTRL);
+
 }
 
-static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
+static void  gic_cpu_init(struct gic_chip_data *gic)
 {
 	void __iomem *dist_base = gic_data_dist_base(gic);
 	void __iomem *base = gic_data_cpu_base(gic);
 	int i;
-
+	u32 enablemask;
+	u32 enableoff;
+	u32 ccpu;
+	u32 cpu = smp_processor_id();
+	u32 cluster = cpu / CORES_PER_CLUSTER;
+	u32 cpumask_8;
 
 	/*
 	 * Deal with the banked PPI and SGI interrupts - disable all
 	 * PPI interrupts, and also all SGI interrupts (we don't use
 	 * SGIs in the Axxia).
 	 */
-
 	writel_relaxed(0xffffffff, dist_base + GIC_DIST_ENABLE_CLEAR);
 
-	/*
-	 * Set priority on PPI and SGI interrupts
-	 */
-	for (i = 0; i < 32; i += 4)
-		writel_relaxed(0xa0a0a0a0,
-			       dist_base + GIC_DIST_PRI + i * 4 / 4);
+	if (!cluster_power_up[cluster]) {
+		writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+		for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
+			cpumask_8 = 1 << ((i - IPI0_CPU0) % 4);
+			enablemask = 1 << (i % 32);
+			enableoff = (i / 32) * 4;
+			ccpu = (cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+			if (ccpu == cpu) {
+				writeb_relaxed(cpumask_8, dist_base + GIC_DIST_TARGET + i);
+				writel_relaxed(enablemask, dist_base + GIC_DIST_ENABLE_SET + enableoff);
+			}
+		}
+		writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+	}
 
 	writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
+
 	writel_relaxed(1, base + GIC_CPU_CTRL);
 }
 
@@ -1526,7 +1544,7 @@ void __init axxia_gic_init_bases(int irq_start,
 
 }
 
-void __cpuinit axxia_gic_secondary_init(void)
+void  axxia_gic_secondary_init(void)
 {
 	struct gic_chip_data *gic = &gic_data;
 
-- 
1.8.1.4



More information about the linux-yocto mailing list