Skip to contentSkip to conversation

xics and ipi cleanups for 2.6.28

PATCH In review Milton Miller 5 participants 25 messages Oct 10, 2008 – Nov 11, 2008

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover xics and ipi cleanups for 2.6.28

The first two patches in this series implememnt a couple of bug fixes for the xics interrupt controller accessed as found in pseries hardware. The next several patches implement some code orginization and cleanups, followed by some changes to reduce code or flag the behavior to generic code.

The last 5 patces are independent of the xics changes, although the first one affects xics. They are designed to reduce interrupt overhead for interprocessor interupts.

While I would have liked to do more testing where I have access to the hardware (which is only xics), I am sending them out now for review and consdieration for the merge window. All have been compile tested, and I feel they are split into reviewable pieces.

milton

#1 lore raw

PATCH 1/16 powerpc pseries: eoi unmapped xics irqs after disable

When reciving an irq vector that does not have a linux mapping, the kernel
prints a message and calls RTAS to disable the irq source. Previously
the kernel did not EOI the interrupt, causing the source to think it is still being processed by software. While this does add an additional layer of protection against interrupt storms had RTAS failed to disable the source, it also prevents the interrupt from working when a driver later enables it. (We could alternatively send an EOI on startup, but that strategy would likely fail on an emulated xics.)

All interrupts should be disabled when the kernel starts, but this can be observed if a driver does not shutdown an interrupt in its reboot hook before starting a new kernel with kexec.

Michael reports this can be reproduced trivially by banging the keyboard while kexec'ing on a P5 LPAR: even though the hvc_console driver request's the console irq later in boot, the console is non-functional because we're receiving no console interrupts.

Reported-By Michael Ellerman

---
This version is for 2.6.28 based on benh's next branch. For prior kernels, s/irq_radix_revmap_lookup/irq_radix_revmap/ before applying.

arch/powerpc/platforms/pseries/xics.c +41 -12
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -332,32 +332,61 @@ static void xics_eoi_lpar(unsigned int v
 	lpar_xirr_info_set((0xff << 24) | irq);
 }
 
-static inline unsigned int xics_remap_irq(unsigned int vec)
+static inline unsigned int xics_xirr_vector(unsigned int xirr)
 {
-	unsigned int irq;
+	/*
+	 * The top byte is the old cppr, to be restored on EOI.
+	 * The remaining 24 bits are the vector.
+	 */
+	return xirr & 0x00ffffff;
+}
 
-	vec &= 0x00ffffff;
+static void xics_mask_unknown_vec(unsigned int vec)
+{
+	printk(KERN_ERR "Interrupt %u (real) is invalid, disabling it.\n", vec);
+	xics_mask_real_irq(vec);
+}
+
+static unsigned int xics_get_irq_direct(void)
+{
+	unsigned int xirr = direct_xirr_info_get();
+	unsigned int vec = xics_xirr_vector(xirr);
+	unsigned int irq;
 
 	if (vec == XICS_IRQ_SPURIOUS)
 		return NO_IRQ;
+
 	irq = irq_radix_revmap_lookup(xics_host, vec);
 	if (likely(irq != NO_IRQ))
 		return irq;
 
-	printk(KERN_ERR "Interrupt %u (real) is invalid,"
-	       " disabling it.\n", vec);
-	xics_mask_real_irq(vec);
-	return NO_IRQ;
-}
+	/* We don't have a linux mapping, so have rtas mask it. */
+	xics_mask_unknown_vec(vec);
 
-static unsigned int xics_get_irq_direct(void)
-{
-	return xics_remap_irq(direct_xirr_info_get());
+	/* We might learn about it later, so EOI it */
+	direct_xirr_info_set(xirr);
+	return NO_IRQ;
 }
 
 static unsigned int xics_get_irq_lpar(void)
 {
-	return xics_remap_irq(lpar_xirr_info_get());
+	unsigned int xirr = lpar_xirr_info_get();
+	unsigned int vec = xics_xirr_vector(xirr);
+	unsigned int irq;
+
+	if (vec == XICS_IRQ_SPURIOUS)
+		return NO_IRQ;
+
+	irq = irq_radix_revmap_lookup(xics_host, vec);
+	if (likely(irq != NO_IRQ))
+		return irq;
+
+	/* We don't have a linux mapping, so have RTAS mask it. */
+	xics_mask_unknown_vec(vec);
+
+	/* We might learn about it later, so EOI it */
+	lpar_xirr_info_set(xirr);
+	return NO_IRQ;
 }
 
 #ifdef CONFIG_SMP
#2 lore raw

PATCH 2/16 xics: update default_server during migrate_irqs_away

In reply to Milton Miller

Currently, every time we determine which irq server to use, we check if default_server, which is the id of the bootcpu, is still online. But default_server is a hardware cpu, not the logical cpu id needed to index cpu_online_map.

Since the default server can only go offline during a cpu hotplug event, explicitly check the default server and choose the new one when we move irqs away from the cpu being offlined.

This has the added benefit of only needing the boot_cpuid to be updated and not relying on the cpu being marked offline during migrate_irqs_away.

Also, since xics_update_irq_servers only reads device tree information, we can call it before xics_init_host in xics_init_IRQ and then default_server will always be valid when we can reach get_irq_server via the host ops.

---
I realized the hard vs soft cpu id as I was writing the patch description. get_irq_server is called from unmask and set affinity.

The effects of this bug would be the increased pathlength to walk the device tree in the unmask and set affinity methods, and possible failure to migrate irqs during cpu hotplug removal (not likely to be seen on until a sequence of remove and adds is performed).

This check dates to 2.6.25 via de0723dcca6e593a12a259798a54eb0e82628fb8.

arch/powerpc/platforms/pseries/xics.c +5 -4
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -208,9 +208,6 @@ static int get_irq_server(unsigned int v
 	cpumask_t cpumask = irq_desc[virq].affinity;
 	cpumask_t tmp = CPU_MASK_NONE;
 
-	if (! cpu_isset(default_server, cpu_online_map))
-		xics_update_irq_servers();
-
 	if (!distribute_irqs)
 		return default_server;
 
@@ -685,8 +682,8 @@ void __init xics_init_IRQ(void)
 	if (found == 0)
 		return;
 
-	xics_init_host();
 	xics_update_irq_servers();
+	xics_init_host();
 
 	if (firmware_has_feature(FW_FEATURE_LPAR))
 		ppc_md.get_irq = xics_get_irq_lpar;
@@ -779,6 +776,10 @@ void xics_migrate_irqs_away(void)
 	int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id();
 	unsigned int irq, virq;
 
+	/* If we used to be the default server, move to the new "boot_cpuid" */
+	if (hw_cpu == default_server)
+		xics_update_irq_servers();
+
 	/* Reject any interrupt that was queued to us... */
 	xics_set_cpu_priority(0);
 
Acked-by on v1 2/16
#3 lore raw

Re: [PATCH 2/16] xics: update default_server during migrate_irqs_away

Quoted 9 lines · Milton Miller

Currently, every time we determine which irq server to use, we check if default_server, which is the id of the bootcpu, is still online. But default_server is a hardware cpu, not the logical cpu id needed to index cpu_online_map.

Since the default server can only go offline during a cpu hotplug event, explicitly check the default server and choose the new one when we move irqs away from the cpu being offlined.

Quoted 8 lines

This has the added benefit of only needing the boot_cpuid to be updated and not relying on the cpu being marked offline during migrate_irqs_away.

Also, since xics_update_irq_servers only reads device tree information, we can call it before xics_init_host in xics_init_IRQ and then default_server will always be valid when we can reach get_irq_server via the host ops.

Quoted 46 lines

---
I realized the hard vs soft cpu id as I was writing the patch description. get_irq_server is called from unmask and set affinity.

The effects of this bug would be the increased pathlength to walk the device tree in the unmask and set affinity methods, and possible failure to migrate irqs during cpu hotplug removal (not likely to be seen on until a sequence of remove and adds is performed).

This check dates to 2.6.25 via de0723dcca6e593a12a259798a54eb0e82628fb8.

Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================

arch/powerpc/platforms/pseries/xics.c +5 -4
@@ -208,9 +208,6 @@ static int get_irq_server(unsigned int v
 	cpumask_t cpumask = irq_desc[virq].affinity;
 	cpumask_t tmp = CPU_MASK_NONE;
 
-	if (! cpu_isset(default_server, cpu_online_map))
-		xics_update_irq_servers();
-
 	if (!distribute_irqs)
 		return default_server;
 
@@ -685,8 +682,8 @@ void __init xics_init_IRQ(void)
 	if (found == 0)
 		return;
 
-	xics_init_host();
 	xics_update_irq_servers();
+	xics_init_host();
 
 	if (firmware_has_feature(FW_FEATURE_LPAR))
 		ppc_md.get_irq = xics_get_irq_lpar;
@@ -779,6 +776,10 @@ void xics_migrate_irqs_away(void)
 	int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id();
 	unsigned int irq, virq;
 
+	/* If we used to be the default server, move to the new "boot_cpuid" */
+	if (hw_cpu == default_server)
+		xics_update_irq_servers();
+
 	/* Reject any interrupt that was queued to us... */
 	xics_set_cpu_priority(0);
 
#4 lore raw

PATCH 3/16 xics: consolidate ipi message encode and decode

In reply to Milton Miller

xics supports only one ipi per cpu, and expects software to use some queue to know why the interrupt was sent. In Linux, we use a an array of bitmaps indexed by cpu to identify the message. Currently the bits are set in smp.c and decoded in xics.c, with the data structure in a
header file. Consolidate the code in xics.c similar to mpic and other
interrupt controllers.

Also, while making the the array static, the message word doesn't need to be volatile as set_bit and test_clear_bit take care of it for us, and put it under ifdef smp.

---
iseries and powermac psurge have similar code due to only one ipi, but they don't spread out the lines by cachelines for scalability like pseries. Since that is one other platform per arch head, I left the code replicated.

I kept smp_xics_cpu_setup in smp.c as the vpa init is unrelated.

arch/powerpc/platforms/pseries/smp.c +1 -31
Index: next.git/arch/powerpc/platforms/pseries/smp.c
===================================================================
@@ -37,7 +37,6 @@
 #include <asm/paca.h>
 #include <asm/time.h>
 #include <asm/machdep.h>
-#include "xics.h"
 #include <asm/cputable.h>
 #include <asm/firmware.h>
 #include <asm/system.h>
@@ -49,6 +48,7 @@
 
 #include "plpar_wrappers.h"
 #include "pseries.h"
+#include "xics.h"
 
 
 /*
@@ -105,36 +105,6 @@ static inline int __devinit smp_startup_
 }
 
 #ifdef CONFIG_XICS
-static inline void smp_xics_do_message(int cpu, int msg)
-{
-	set_bit(msg, &xics_ipi_message[cpu].value);
-	mb();
-	xics_cause_IPI(cpu);
-}
-
-static void smp_xics_message_pass(int target, int msg)
-{
-	unsigned int i;
-
-	if (target < NR_CPUS) {
-		smp_xics_do_message(target, msg);
-	} else {
-		for_each_online_cpu(i) {
-			if (target == MSG_ALL_BUT_SELF
-			    && i == smp_processor_id())
-				continue;
-			smp_xics_do_message(i, msg);
-		}
-	}
-}
-
-static int __init smp_xics_probe(void)
-{
-	xics_request_IPIs();
-
-	return cpus_weight(cpu_possible_map);
-}
-
 static void __devinit smp_xics_setup_cpu(int cpu)
 {
 	if (cpu != boot_cpuid)
arch/powerpc/platforms/pseries/xics.c +45 -16

103 lines not on this page · raw message

arch/powerpc/platforms/pseries/xics.h +2 -10
Index: next.git/arch/powerpc/platforms/pseries/xics.h
===================================================================
@@ -12,20 +12,12 @@
 #ifndef _POWERPC_KERNEL_XICS_H
 #define _POWERPC_KERNEL_XICS_H
 
-#include <linux/cache.h>
-
 extern void xics_init_IRQ(void);
 extern void xics_setup_cpu(void);
 extern void xics_teardown_cpu(void);
 extern void xics_kexec_teardown_cpu(int secondary);
-extern void xics_cause_IPI(int cpu);
-extern  void xics_request_IPIs(void);
 extern void xics_migrate_irqs_away(void);
-
-struct xics_ipi_struct {
-	volatile unsigned long value;
-} ____cacheline_aligned;
-
-extern struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned;
+extern int smp_xics_probe(void);
+extern void smp_xics_message_pass(int target, int msg);
 
 #endif /* _POWERPC_KERNEL_XICS_H */
#5 lore raw

PATCH 4/16 xics: rearrange file to group code by function

In reply to Milton Miller

Now that xics_update_irq_servers is called only from init and hotplug code, it becomes possible to clean up the ordering of functions in the file, grouping them but the interfaces they implement.

---
This patch is pure function reordering and comments.

This patch applies to -next. For prior kernels, there are 6 more lines in xics_startup() that affect patch application.

arch/powerpc/platforms/pseries/xics.c +217 -215
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -9,7 +9,6 @@
  *  2 of the License, or (at your option) any later version.
  */
 
-
 #include <linux/types.h>
 #include <linux/threads.h>
 #include <linux/kernel.h>
@@ -35,6 +34,8 @@
 #include "xics.h"
 #include "plpar_wrappers.h"
 
+static struct irq_host *xics_host;
+
 #define XICS_IPI		2
 #define XICS_IRQ_SPURIOUS	0
 
@@ -47,6 +48,20 @@
  */
 #define IPI_PRIORITY		4
 
+static unsigned int default_server = 0xFF;
+static unsigned int default_distrib_server = 0;
+static unsigned int interrupt_server_size = 8;
+
+/* RTAS service tokens */
+static int ibm_get_xive;
+static int ibm_set_xive;
+static int ibm_int_on;
+static int ibm_int_off;
+
+
+/* Direct hardware low level accessors */
+
+/* The part of the interrupt presentation layer that we care about */
 struct xics_ipl {
 	union {
 		u32 word;
@@ -65,22 +80,6 @@ struct xics_ipl {
 
 static struct xics_ipl __iomem *xics_per_cpu[NR_CPUS];
 
-static unsigned int default_server = 0xFF;
-static unsigned int default_distrib_server = 0;
-static unsigned int interrupt_server_size = 8;
-
-static struct irq_host *xics_host;
-
-/* RTAS service tokens */
-static int ibm_get_xive;
-static int ibm_set_xive;
-static int ibm_int_on;
-static int ibm_int_off;
-
-
-/* Direct HW low level accessors */
-
-
 static inline unsigned int direct_xirr_info_get(void)
 {
 	int cpu = smp_processor_id();
@@ -110,7 +109,6 @@ static inline void direct_qirr_info(int 
 
 /* LPAR low level accessors */
 
-
 static inline unsigned int lpar_xirr_info_get(void)
 {
 	unsigned long lpar_rc;
@@ -152,59 +150,9 @@ static inline void lpar_qirr_info(int n_
 }
 
 
-/* High level handlers and init code */
-
-static void xics_update_irq_servers(void)
-{
-	int i, j;
-	struct device_node *np;
-	u32 ilen;
-	const u32 *ireg, *isize;
-	u32 hcpuid;
-
-	/* Find the server numbers for the boot cpu. */
-	np = of_get_cpu_node(boot_cpuid, NULL);
-	BUG_ON(!np);
-
-	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
-	if (!ireg) {
-		of_node_put(np);
-		return;
-	}
-
-	i = ilen / sizeof(int);
-	hcpuid = get_hard_smp_processor_id(boot_cpuid);
-
-	/* Global interrupt distribution server is specified in the last
-	 * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last
-	 * entry fom this property for current boot cpu id and use it as
-	 * default distribution server
-	 */
-	for (j = 0; j < i; j += 2) {
-		if (ireg[j] == hcpuid) {
-			default_server = hcpuid;
-			default_distrib_server = ireg[j+1];
-
-			isize = of_get_property(np,
-					"ibm,interrupt-server#-size", NULL);
-			if (isize)
-				interrupt_server_size = *isize;
-		}
-	}
-
-	of_node_put(np);
-}
+/* Interface to generic irq subsystem */
 
 #ifdef CONFIG_SMP
-/*
- * XICS only has a single IPI, so encode the messages per CPU
- */
-struct xics_ipi_struct {
-        unsigned long value;
-	} ____cacheline_aligned;
-
-static struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned;
-
 static int get_irq_server(unsigned int virq, unsigned int strict_check)
 {
 	int server;
@@ -239,7 +187,6 @@ static int get_irq_server(unsigned int v
 }
 #endif
 
-
 static void xics_unmask_irq(unsigned int virq)
 {
 	unsigned int irq;
@@ -273,6 +220,13 @@ static void xics_unmask_irq(unsigned int
 	}
 }
 
+static unsigned int xics_startup(unsigned int virq)
+{
+	/* unmask it */
+	xics_unmask_irq(virq);
+	return 0;
+}
+
 static void xics_mask_real_irq(unsigned int irq)
 {
 	int call_status;
@@ -309,28 +263,10 @@ static void xics_mask_irq(unsigned int v
 	xics_mask_real_irq(irq);
 }
 
-static unsigned int xics_startup(unsigned int virq)
-{
-	/* unmask it */
-	xics_unmask_irq(virq);
-	return 0;
-}
-
-static void xics_eoi_direct(unsigned int virq)
-{
-	unsigned int irq = (unsigned int)irq_map[virq].hwirq;
-
-	iosync();
-	direct_xirr_info_set((0xff << 24) | irq);
-}
-
-
-static void xics_eoi_lpar(unsigned int virq)
+static void xics_mask_unknown_vec(unsigned int vec)
 {
-	unsigned int irq = (unsigned int)irq_map[virq].hwirq;
-
-	iosync();
-	lpar_xirr_info_set((0xff << 24) | irq);
+	printk(KERN_ERR "Interrupt %u (real) is invalid, disabling it.\n", vec);
+	xics_mask_real_irq(vec);
 }
 
 static inline unsigned int xics_xirr_vector(unsigned int xirr)
@@ -342,12 +278,6 @@ static inline unsigned int xics_xirr_vec
 	return xirr & 0x00ffffff;
 }
 
-static void xics_mask_unknown_vec(unsigned int vec)
-{
-	printk(KERN_ERR "Interrupt %u (real) is invalid, disabling it.\n", vec);
-	xics_mask_real_irq(vec);
-}
-
 static unsigned int xics_get_irq_direct(void)
 {
 	unsigned int xirr = direct_xirr_info_get();
@@ -390,91 +320,20 @@ static unsigned int xics_get_irq_lpar(vo
 	return NO_IRQ;
 }
 
-#ifdef CONFIG_SMP
-static irqreturn_t xics_ipi_dispatch(int cpu)
-{
-	WARN_ON(cpu_is_offline(cpu));
-
-	while (xics_ipi_message[cpu].value) {
-		if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION,
-				       &xics_ipi_message[cpu].value)) {
-			mb();
-			smp_message_recv(PPC_MSG_CALL_FUNCTION);
-		}
-		if (test_and_clear_bit(PPC_MSG_RESCHEDULE,
-				       &xics_ipi_message[cpu].value)) {
-			mb();
-			smp_message_recv(PPC_MSG_RESCHEDULE);
-		}
-		if (test_and_clear_bit(PPC_MSG_CALL_FUNC_SINGLE,
-				       &xics_ipi_message[cpu].value)) {
-			mb();
-			smp_message_recv(PPC_MSG_CALL_FUNC_SINGLE);
-		}
-#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
-		if (test_and_clear_bit(PPC_MSG_DEBUGGER_BREAK,
-				       &xics_ipi_message[cpu].value)) {
-			mb();
-			smp_message_recv(PPC_MSG_DEBUGGER_BREAK);
-		}
-#endif
-	}
-	return IRQ_HANDLED;
-}
-
-static inline void smp_xics_do_message(int cpu, int msg)
-{
-	set_bit(msg, &xics_ipi_message[cpu].value);
-	mb();
-	if (firmware_has_feature(FW_FEATURE_LPAR))
-		lpar_qirr_info(cpu, IPI_PRIORITY);
-	else
-		direct_qirr_info(cpu, IPI_PRIORITY);
-}
-
-void smp_xics_message_pass(int target, int msg)
-{
-	unsigned int i;
-
-	if (target < NR_CPUS) {
-		smp_xics_do_message(target, msg);
-	} else {
-		for_each_online_cpu(i) {
-			if (target == MSG_ALL_BUT_SELF
-			    && i == smp_processor_id())
-				continue;
-			smp_xics_do_message(i, msg);
-		}
-	}
-}
-
-
-static irqreturn_t xics_ipi_action_direct(int irq, void *dev_id)
+static void xics_eoi_direct(unsigned int virq)
 {
-	int cpu = smp_processor_id();
-
-	direct_qirr_info(cpu, 0xff);
+	unsigned int irq = (unsigned int)irq_map[virq].hwirq;
 
-	return xics_ipi_dispatch(cpu);
+	iosync();
+	direct_xirr_info_set((0xff << 24) | irq);
 }
 
-static irqreturn_t xics_ipi_action_lpar(int irq, void *dev_id)
+static void xics_eoi_lpar(unsigned int virq)
 {
-	int cpu = smp_processor_id();
-
-	lpar_qirr_info(cpu, 0xff);
-
-	return xics_ipi_dispatch(cpu);
-}
-#endif /* CONFIG_SMP */
+	unsigned int irq = (unsigned int)irq_map[virq].hwirq;
 
-static void xics_set_cpu_priority(unsigned char cppr)
-{
-	if (firmware_has_feature(FW_FEATURE_LPAR))
-		lpar_cppr_info(cppr);
-	else
-		direct_cppr_info(cppr);
 	iosync();
+	lpar_xirr_info_set((0xff << 24) | irq);
 }
 
 static void xics_set_affinity(unsigned int virq, cpumask_t cpumask)
@@ -519,22 +378,6 @@ static void xics_set_affinity(unsigned i
 	}
 }
 
-void xics_setup_cpu(void)
-{
-	xics_set_cpu_priority(0xff);
-
-	/*
-	 * Put the calling processor into the GIQ.  This is really only
-	 * necessary from a secondary thread as the OF start-cpu interface
-	 * performs this function for us on primary threads.
-	 *
-	 * XXX: undo of teardown on kexec needs this too, as may hotplug
-	 */
-	rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
-		(1UL << interrupt_server_size) - 1 - default_distrib_server, 1);
-}
-
-
 static struct irq_chip xics_pic_direct = {
 	.typename = " XICS     ",
 	.startup = xics_startup,
@@ -544,7 +387,6 @@ static struct irq_chip xics_pic_direct =
 	.set_affinity = xics_set_affinity
 };
 
-
 static struct irq_chip xics_pic_lpar = {
 	.typename = " XICS     ",
 	.startup = xics_startup,
@@ -554,6 +396,9 @@ static struct irq_chip xics_pic_lpar = {
 	.set_affinity = xics_set_affinity
 };
 
+
+/* Interface to arch irq controller subsystem layer */
+
 /* Points to the irq_chip we're actually using */
 static struct irq_chip *xics_irq_chip;
 
@@ -613,6 +458,169 @@ static void __init xics_init_host(void)
 	irq_set_default_host(xics_host);
 }
 
+
+/* Inter-processor interrupt support */
+
+#ifdef CONFIG_SMP
+/*
+ * XICS only has a single IPI, so encode the messages per CPU
+ */
+struct xics_ipi_struct {
+        unsigned long value;
+	} ____cacheline_aligned;
+
+static struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned;
+
+static inline void smp_xics_do_message(int cpu, int msg)
+{
+	set_bit(msg, &xics_ipi_message[cpu].value);
+	mb();
+	if (firmware_has_feature(FW_FEATURE_LPAR))
+		lpar_qirr_info(cpu, IPI_PRIORITY);
+	else
+		direct_qirr_info(cpu, IPI_PRIORITY);
+}
+
+void smp_xics_message_pass(int target, int msg)
+{
+	unsigned int i;
+
+	if (target < NR_CPUS) {
+		smp_xics_do_message(target, msg);
+	} else {
+		for_each_online_cpu(i) {
+			if (target == MSG_ALL_BUT_SELF
+			    && i == smp_processor_id())
+				continue;
+			smp_xics_do_message(i, msg);
+		}
+	}
+}
+
+static irqreturn_t xics_ipi_dispatch(int cpu)
+{
+	WARN_ON(cpu_is_offline(cpu));
+
+	while (xics_ipi_message[cpu].value) {
+		if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION,
+				       &xics_ipi_message[cpu].value)) {
+			mb();
+			smp_message_recv(PPC_MSG_CALL_FUNCTION);
+		}
+		if (test_and_clear_bit(PPC_MSG_RESCHEDULE,
+				       &xics_ipi_message[cpu].value)) {
+			mb();
+			smp_message_recv(PPC_MSG_RESCHEDULE);
+		}
+		if (test_and_clear_bit(PPC_MSG_CALL_FUNC_SINGLE,
+				       &xics_ipi_message[cpu].value)) {
+			mb();
+			smp_message_recv(PPC_MSG_CALL_FUNC_SINGLE);
+		}
+#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
+		if (test_and_clear_bit(PPC_MSG_DEBUGGER_BREAK,
+				       &xics_ipi_message[cpu].value)) {
+			mb();
+			smp_message_recv(PPC_MSG_DEBUGGER_BREAK);
+		}
+#endif
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t xics_ipi_action_direct(int irq, void *dev_id)
+{
+	int cpu = smp_processor_id();
+
+	direct_qirr_info(cpu, 0xff);
+
+	return xics_ipi_dispatch(cpu);
+}
+
+static irqreturn_t xics_ipi_action_lpar(int irq, void *dev_id)
+{
+	int cpu = smp_processor_id();
+
+	lpar_qirr_info(cpu, 0xff);
+
+	return xics_ipi_dispatch(cpu);
+}
+
+static void xics_request_ipi(void)
+{
+	unsigned int ipi;
+	int rc;
+
+	ipi = irq_create_mapping(xics_host, XICS_IPI);
+	BUG_ON(ipi == NO_IRQ);
+
+	/*
+	 * IPIs are marked IRQF_DISABLED as they must run with irqs
+	 * disabled
+	 */
+	set_irq_handler(ipi, handle_percpu_irq);
+	if (firmware_has_feature(FW_FEATURE_LPAR))
+		rc = request_irq(ipi, xics_ipi_action_lpar, IRQF_DISABLED,
+				"IPI", NULL);
+	else
+		rc = request_irq(ipi, xics_ipi_action_direct, IRQF_DISABLED,
+				"IPI", NULL);
+	BUG_ON(rc);
+}
+
+int __init smp_xics_probe(void)
+{
+	xics_request_ipi();
+
+	return cpus_weight(cpu_possible_map);
+}
+
+#endif /* CONFIG_SMP */
+
+
+/* Initialization */
+
+static void xics_update_irq_servers(void)
+{
+	int i, j;
+	struct device_node *np;
+	u32 ilen;
+	const u32 *ireg, *isize;
+	u32 hcpuid;
+
+	/* Find the server numbers for the boot cpu. */
+	np = of_get_cpu_node(boot_cpuid, NULL);
+	BUG_ON(!np);
+
+	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
+	if (!ireg) {
+		of_node_put(np);
+		return;
+	}
+
+	i = ilen / sizeof(int);
+	hcpuid = get_hard_smp_processor_id(boot_cpuid);
+
+	/* Global interrupt distribution server is specified in the last
+	 * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last
+	 * entry fom this property for current boot cpu id and use it as
+	 * default distribution server
+	 */
+	for (j = 0; j < i; j += 2) {
+		if (ireg[j] == hcpuid) {
+			default_server = hcpuid;
+			default_distrib_server = ireg[j+1];
+
+			isize = of_get_property(np,
+					"ibm,interrupt-server#-size", NULL);
+			if (isize)
+				interrupt_server_size = *isize;
+		}
+	}
+
+	of_node_put(np);
+}
+
 static void __init xics_map_one_cpu(int hw_id, unsigned long addr,
 				     unsigned long size)
 {
@@ -716,39 +724,33 @@ void __init xics_init_IRQ(void)
 	ppc64_boot_msg(0x21, "XICS Done");
 }
 
+/* Cpu startup, shutdown, and hotplug */
 
-#ifdef CONFIG_SMP
-static void xics_request_ipi(void)
+static void xics_set_cpu_priority(unsigned char cppr)
 {
-	unsigned int ipi;
-	int rc;
-
-	ipi = irq_create_mapping(xics_host, XICS_IPI);
-	BUG_ON(ipi == NO_IRQ);
-
-	/*
-	 * IPIs are marked IRQF_DISABLED as they must run with irqs
-	 * disabled
-	 */
-	set_irq_handler(ipi, handle_percpu_irq);
 	if (firmware_has_feature(FW_FEATURE_LPAR))
-		rc = request_irq(ipi, xics_ipi_action_lpar, IRQF_DISABLED,
-				"IPI", NULL);
+		lpar_cppr_info(cppr);
 	else
-		rc = request_irq(ipi, xics_ipi_action_direct, IRQF_DISABLED,
-				"IPI", NULL);
-	BUG_ON(rc);
+		direct_cppr_info(cppr);
+	iosync();
 }
 
-int __init smp_xics_probe(void)
+
+void xics_setup_cpu(void)
 {
-	xics_request_ipi();
+	xics_set_cpu_priority(0xff);
 
-	return cpus_weight(cpu_possible_map);
+	/*
+	 * Put the calling processor into the GIQ.  This is really only
+	 * necessary from a secondary thread as the OF start-cpu interface
+	 * performs this function for us on primary threads.
+	 *
+	 * XXX: undo of teardown on kexec needs this too, as may hotplug
+	 */
+	rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
+		(1UL << interrupt_server_size) - 1 - default_distrib_server, 1);
 }
 
-#endif /* CONFIG_SMP */
-
 void xics_teardown_cpu(void)
 {
 	int cpu = smp_processor_id();
#6 lore raw

PATCH 5/16 xics: change arg type to remove casts

In reply to Milton Miller

The xirr is 32 bits in hardware, but the hypervisor requries the upper bits of the register to be clear on the hcall. By changing the type from signed to unsigned int we can drop masking it back to 32 bits.

arch/powerpc/platforms/pseries/xics.c +5 -6
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -87,7 +87,7 @@ static inline unsigned int direct_xirr_i
 	return in_be32(&xics_per_cpu[cpu]->xirr.word);
 }
 
-static inline void direct_xirr_info_set(int value)
+static inline void direct_xirr_info_set(unsigned int value)
 {
 	int cpu = smp_processor_id();
 
@@ -120,15 +120,14 @@ static inline unsigned int lpar_xirr_inf
 	return (unsigned int)return_value;
 }
 
-static inline void lpar_xirr_info_set(int value)
+static inline void lpar_xirr_info_set(unsigned int value)
 {
 	unsigned long lpar_rc;
-	unsigned long val64 = value & 0xffffffff;
 
-	lpar_rc = plpar_eoi(val64);
+	lpar_rc = plpar_eoi(value);
 	if (lpar_rc != H_SUCCESS)
-		panic("bad return code EOI - rc = %ld, value=%lx\n", lpar_rc,
-		      val64);
+		panic("bad return code EOI - rc = %ld, value=%x\n", lpar_rc,
+		      value);
 }
 
 static inline void lpar_cppr_info(u8 value)
#7 lore raw

PATCH 6/16 xics: trim includes

In reply to Milton Miller

Trim unneeded includes from xics.c. We don't use signals or gfp flags, we use only OF functions and don't need prom, and the 8259 is now handled by our caller.

---
asm/smp.h gives us cpu mask accessors for !CONFIG_SMP, used in the next patch

arch/powerpc/platforms/pseries/xics.c +1 -4
Index: work.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -15,21 +15,18 @@
 #include <linux/irq.h>
 #include <linux/smp.h>
 #include <linux/interrupt.h>
-#include <linux/signal.h>
 #include <linux/init.h>
-#include <linux/gfp.h>
 #include <linux/radix-tree.h>
 #include <linux/cpu.h>
+#include <linux/of.h>
 
 #include <asm/firmware.h>
-#include <asm/prom.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
 #include <asm/smp.h>
 #include <asm/rtas.h>
 #include <asm/hvcall.h>
 #include <asm/machdep.h>
-#include <asm/i8259.h>
 
 #include "xics.h"
 #include "plpar_wrappers.h"
#8 lore raw

PATCH 7/16 xics: initialization cleanups

In reply to Milton Miller

We only need to check the ibm,interrupt-server#-size property once, not once per global server and thread.

We can use !CONFIG_SMP cpu masks and hard_smp_processor_id() to avoid an ifdef. Put the node when breaking out of the loop on lpar systems.

arch/powerpc/platforms/pseries/xics.c +8 -12
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -606,21 +606,20 @@ static void xics_update_irq_servers(void
 		if (ireg[j] == hcpuid) {
 			default_server = hcpuid;
 			default_distrib_server = ireg[j+1];
-
-			isize = of_get_property(np,
-					"ibm,interrupt-server#-size", NULL);
-			if (isize)
-				interrupt_server_size = *isize;
 		}
 	}
 
+	/* get the bit size of server numbers */
+	isize = of_get_property(np, "ibm,interrupt-server#-size", NULL);
+	if (isize)
+		interrupt_server_size = *isize;
+
 	of_node_put(np);
 }
 
 static void __init xics_map_one_cpu(int hw_id, unsigned long addr,
 				     unsigned long size)
 {
-#ifdef CONFIG_SMP
 	int i;
 
 	/* This may look gross but it's good enough for now, we don't quite
@@ -634,11 +633,6 @@ static void __init xics_map_one_cpu(int 
 			return;
 		}
 	}
-#else
-	if (hw_id != 0)
-		return;
-	xics_per_cpu[0] = ioremap(addr, size);
-#endif /* CONFIG_SMP */
 }
 
 static void __init xics_init_one_node(struct device_node *np,
@@ -700,8 +694,10 @@ void __init xics_init_IRQ(void)
 
 	for_each_node_by_type(np, "PowerPC-External-Interrupt-Presentation") {
 		found = 1;
-		if (firmware_has_feature(FW_FEATURE_LPAR))
+		if (firmware_has_feature(FW_FEATURE_LPAR)) {
+			of_node_put(np);
 			break;
+			}
 		xics_init_one_node(np, &indx);
 	}
 	if (found == 0)
#9 lore raw

Re: [PATCH 7/16] xics: initialization cleanups

arch/powerpc/platforms/pseries/xics.c void __init xics_init_IRQ(void) · line 700

Quoted 11 lines

static void __init xics_init_one_node(struct device_node *np,

arch/powerpc/platforms/pseries/xics.c +3 -1
@@ -700,8 +694,10 @@ void __init xics_init_IRQ(void)
 
 	for_each_node_by_type(np, "PowerPC-External-Interrupt-Presentation") {
 		found = 1;
-		if (firmware_has_feature(FW_FEATURE_LPAR))
+		if (firmware_has_feature(FW_FEATURE_LPAR)) {
+			of_node_put(np);
 			break;
+			}

Little tab damage up here... no big deal.

Quoted 3 lines
arch/powerpc/platforms/pseries/xics.c +0 -0
 		xics_init_one_node(np, &indx);
 	}
 	if (found == 0
#10 lore raw

PATCH 8/16 xics: factor out giq set and unset

In reply to Milton Miller

There is a bit of math to calculate the arguments to rtas to join or leave the global interrupt queue, and a warning on failure afterwards. Make a helper for the 3 callers.

arch/powerpc/platforms/pseries/xics.c +13 -20
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -727,20 +727,19 @@ static void xics_set_cpu_priority(unsign
 	iosync();
 }
 
+/* Have the calling processor join or leave the specified global queue */
+static void xics_set_cpu_giq(unsigned int gserver, unsigned int join)
+{
+	int status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
+		(1UL << interrupt_server_size) - 1 - gserver, join);
+	WARN_ON(status < 0);
+}
 
 void xics_setup_cpu(void)
 {
 	xics_set_cpu_priority(0xff);
 
-	/*
-	 * Put the calling processor into the GIQ.  This is really only
-	 * necessary from a secondary thread as the OF start-cpu interface
-	 * performs this function for us on primary threads.
-	 *
-	 * XXX: undo of teardown on kexec needs this too, as may hotplug
-	 */
-	rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
-		(1UL << interrupt_server_size) - 1 - default_distrib_server, 1);
+	xics_set_cpu_giq(default_distrib_server, 1);
 }
 
 void xics_teardown_cpu(void)
@@ -749,9 +748,7 @@ void xics_teardown_cpu(void)
 
 	xics_set_cpu_priority(0);
 
-	/*
-	 * Clear IPI
-	 */
+	/* Clear any pending IPI request */
 	if (firmware_has_feature(FW_FEATURE_LPAR))
 		lpar_qirr_info(cpu, 0xff);
 	else
@@ -785,9 +782,7 @@ void xics_kexec_teardown_cpu(int seconda
 	 * so leave the master cpu in the group.
 	 */
 	if (secondary)
-		rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
-				   (1UL << interrupt_server_size) - 1 -
-				   default_distrib_server, 0);
+		xics_set_cpu_giq(default_distrib_server, 0);
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
@@ -795,7 +790,6 @@ void xics_kexec_teardown_cpu(int seconda
 /* Interrupts are disabled. */
 void xics_migrate_irqs_away(void)
 {
-	int status;
 	int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id();
 	unsigned int irq, virq;
 
@@ -806,10 +800,8 @@ void xics_migrate_irqs_away(void)
 	/* Reject any interrupt that was queued to us... */
 	xics_set_cpu_priority(0);
 
-	/* remove ourselves from the global interrupt queue */
-	status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
-		(1UL << interrupt_server_size) - 1 - default_distrib_server, 0);
-	WARN_ON(status < 0);
+	/* Remove ourselves from the global interrupt queue */
+	xics_set_cpu_giq(default_distrib_server, 0);
 
 	/* Allow IPIs again... */
 	xics_set_cpu_priority(DEFAULT_PRIORITY);
@@ -817,6 +809,7 @@ void xics_migrate_irqs_away(void)
 	for_each_irq(virq) {
 		struct irq_desc *desc;
 		int xics_status[2];
+		int status;
 		unsigned long flags;
 
 		/* We cant set affinity on ISA interrupts */
#11 lore raw

PATCH 9/16 powerpc: eoi xics ipi by hand in kexec

In reply to Milton Miller

EOI normally has the side effect of returning the cpu to the base priority to recieve the next interrupt. This is actually controlled
by the top byte of the xirr register. When we are exiting the
kernel in kexec we must eoi the ipi for the next kernel because we never return from the handler, but we want to leave interrupt delivery blocked until the next kernel takes action.

Since the hardware ipi vector is fixed, its easiest to just do the eoi explicitly.

arch/powerpc/platforms/pseries/xics.c +7 -11
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -757,25 +757,21 @@ void xics_teardown_cpu(void)
 
 void xics_kexec_teardown_cpu(int secondary)
 {
-	unsigned int ipi;
-	struct irq_desc *desc;
-
 	xics_teardown_cpu();
 
 	/*
-	 * we need to EOI the IPI
+	 * we take the ipi irq but and never return so we
+	 * need to EOI the IPI, but want to leave our priority 0
 	 *
-	 * probably need to check all the other interrupts too
+	 * should we check all the other interrupts too?
 	 * should we be flagging idle loop instead?
 	 * or creating some task to be scheduled?
 	 */
 
-	ipi = irq_find_mapping(xics_host, XICS_IPI);
-	if (ipi == XICS_IRQ_SPURIOUS)
-		return;
-	desc = get_irq_desc(ipi);
-	if (desc->chip && desc->chip->eoi)
-		desc->chip->eoi(ipi);
+	if (firmware_has_feature(FW_FEATURE_LPAR))
+		lpar_xirr_info_set((0x00 << 24) | XICS_IPI);
+	else
+		direct_xirr_info_set((0x00 << 24) | XICS_IPI);
 
 	/*
 	 * Some machines need to have at least one cpu in the GIQ,
#12 lore raw

PATCH 10/16 xics: make printk formats fit on one line

In reply to Milton Miller

Several printks were broken at word boundaries for line length. Some
even referred to old function names. Using __func__ and changing the
text slightly for the format allows these printk formats to fit on one line.

arch/powerpc/platforms/pseries/xics.c +18 -18
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -201,17 +201,17 @@ static void xics_unmask_irq(unsigned int
 	call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq, server,
 				DEFAULT_PRIORITY);
 	if (call_status != 0) {
-		printk(KERN_ERR "xics_enable_irq: irq=%u: ibm_set_xive "
-		       "returned %d\n", irq, call_status);
-		printk("set_xive %x, server %x\n", ibm_set_xive, server);
+		printk(KERN_ERR
+			"%s: ibm_set_xive irq %u server %x returned %d\n",
+			__func__, irq, server, call_status);
 		return;
 	}
 
 	/* Now unmask the interrupt (often a no-op) */
 	call_status = rtas_call(ibm_int_on, 1, 1, NULL, irq);
 	if (call_status != 0) {
-		printk(KERN_ERR "xics_enable_irq: irq=%u: ibm_int_on "
-		       "returned %d\n", irq, call_status);
+		printk(KERN_ERR "%s: ibm_int_on irq=%u returned %d\n",
+			__func__, irq, call_status);
 		return;
 	}
 }
@@ -232,8 +232,8 @@ static void xics_mask_real_irq(unsigned 
 
 	call_status = rtas_call(ibm_int_off, 1, 1, NULL, irq);
 	if (call_status != 0) {
-		printk(KERN_ERR "xics_disable_real_irq: irq=%u: "
-		       "ibm_int_off returned %d\n", irq, call_status);
+		printk(KERN_ERR "%s: ibm_int_off irq=%u returned %d\n",
+			__func__, irq, call_status);
 		return;
 	}
 
@@ -241,8 +241,8 @@ static void xics_mask_real_irq(unsigned 
 	call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq,
 				default_server, 0xff);
 	if (call_status != 0) {
-		printk(KERN_ERR "xics_disable_irq: irq=%u: ibm_set_xive(0xff)"
-		       " returned %d\n", irq, call_status);
+		printk(KERN_ERR "%s: ibm_set_xive(0xff) irq=%u returned %d\n",
+			__func__, irq, call_status);
 		return;
 	}
 }
@@ -346,8 +346,8 @@ static void xics_set_affinity(unsigned i
 	status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);
 
 	if (status) {
-		printk(KERN_ERR "xics_set_affinity: irq=%u ibm,get-xive "
-		       "returns %d\n", irq, status);
+		printk(KERN_ERR "%s: ibm,get-xive irq=%u returns %d\n",
+			__func__, irq, status);
 		return;
 	}
 
@@ -359,8 +359,9 @@ static void xics_set_affinity(unsigned i
 	if (irq_server == -1) {
 		char cpulist[128];
 		cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
-		printk(KERN_WARNING "xics_set_affinity: No online cpus in "
-				"the mask %s for irq %d\n", cpulist, virq);
+		printk(KERN_WARNING
+			"%s: No online cpus in the mask %s for irq %d\n",
+			__func__, cpulist, virq);
 		return;
 	}
 
@@ -368,8 +369,8 @@ static void xics_set_affinity(unsigned i
 				irq, irq_server, xics_status[1]);
 
 	if (status) {
-		printk(KERN_ERR "xics_set_affinity: irq=%u ibm,set-xive "
-		       "returns %d\n", irq, status);
+		printk(KERN_ERR "%s: ibm,set-xive irq=%u returns %d\n",
+			__func__, irq, status);
 		return;
 	}
 }
@@ -829,9 +830,8 @@ void xics_migrate_irqs_away(void)
 
 		status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);
 		if (status) {
-			printk(KERN_ERR "migrate_irqs_away: irq=%u "
-					"ibm,get-xive returns %d\n",
-					virq, status);
+			printk(KERN_ERR "%s: ibm,get-xive irq=%u returns %d\n",
+					__func__, irq, status);
 			goto unlock;
 		}
 
#13 lore raw

PATCH 11/16 powerpc: mark xics ipi percpu

In reply to Milton Miller

Its physically per-cpu, and we want the irq layer to treat it that way.

arch/powerpc/platforms/pseries/xics.c +4 -4
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -557,11 +557,11 @@ static void xics_request_ipi(void)
 	 */
 	set_irq_handler(ipi, handle_percpu_irq);
 	if (firmware_has_feature(FW_FEATURE_LPAR))
-		rc = request_irq(ipi, xics_ipi_action_lpar, IRQF_DISABLED,
-				"IPI", NULL);
+		rc = request_irq(ipi, xics_ipi_action_lpar,
+				IRQF_DISABLED|IRQF_PERCPU, "IPI", NULL);
 	else
-		rc = request_irq(ipi, xics_ipi_action_direct, IRQF_DISABLED,
-				"IPI", NULL);
+		rc = request_irq(ipi, xics_ipi_action_direct,
+				IRQF_DISABLED|IRQF_PERCPU, "IPI", NULL);
 	BUG_ON(rc);
 }
 
#14 lore raw

PATCH 12/16 powerpc: reduce and comment xics ipi memory barrier

In reply to Milton Miller

A single full sync (mb()) is requrired to order the mmio to the qirr reg with the set or clear of the message word. However, test_and_clear_bit has the effect of smp_mb() and we are not doing any other io from here, so we don't need a mb per bit processed.

arch/powerpc/platforms/pseries/xics.c +1 -4
Index: next.git/arch/powerpc/platforms/pseries/xics.c
===================================================================
@@ -498,26 +498,23 @@ static irqreturn_t xics_ipi_dispatch(int
 {
 	WARN_ON(cpu_is_offline(cpu));
 
+	mb();	/* order mmio clearing qirr */
 	while (xics_ipi_message[cpu].value) {
 		if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION,
 				       &xics_ipi_message[cpu].value)) {
-			mb();
 			smp_message_recv(PPC_MSG_CALL_FUNCTION);
 		}
 		if (test_and_clear_bit(PPC_MSG_RESCHEDULE,
 				       &xics_ipi_message[cpu].value)) {
-			mb();
 			smp_message_recv(PPC_MSG_RESCHEDULE);
 		}
 		if (test_and_clear_bit(PPC_MSG_CALL_FUNC_SINGLE,
 				       &xics_ipi_message[cpu].value)) {
-			mb();
 			smp_message_recv(PPC_MSG_CALL_FUNC_SINGLE);
 		}
 #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
 		if (test_and_clear_bit(PPC_MSG_DEBUGGER_BREAK,
 				       &xics_ipi_message[cpu].value)) {
-			mb();
 			smp_message_recv(PPC_MSG_DEBUGGER_BREAK);
 		}
 #endif
#15 lore raw

PATCH 13/16 powerpc smp: no need to set_need_resched in resched ipi

In reply to Milton Miller

The code was asking "Do we have to do this?", and according to x86 and s390 the answer is no, the scheduler will do it before calling the arch hook.

arch/powerpc/kernel/smp.c +1 -2
Index: next.git/arch/powerpc/kernel/smp.c
===================================================================
@@ -101,8 +101,7 @@ void smp_message_recv(int msg)
 		generic_smp_call_function_interrupt();
 		break;
 	case PPC_MSG_RESCHEDULE:
-		/* XXX Do we have to do this? */
-		set_need_resched();
+		/* we notice need_resched on exit */
 		break;
 	case PPC_MSG_CALL_FUNC_SINGLE:
 		generic_smp_call_function_single_interrupt();
#16 lore raw

PATCH 14/16 powerpc: expand vs demux ipi actions per message

With the new generic smp call function helpers, I noticed the code in smp_message_recv was a single function call in many cases. While getting the message number from the ipi data is easy, we can reduce the path length by a function call and a mult-way data dependent switch by registering seperate ipi actions for these simple calls.

Originally I left the ipi action array exposed, but then I realized the registration code should be common so that decisions like needing the driver data are common, along with flagging the interrupt per-cpu.

The three users each had their own name array and contents, so I made a fourth style for all users.

---
Perhaps we should make the common code look like an ipi action handler and remove this last call? Currently we still have to allocate a stack frame to load the return value.

arch/powerpc/include/asm/smp.h +7 -0
Index: next.git/arch/powerpc/include/asm/smp.h
===================================================================
@@ -81,6 +81,13 @@ extern int cpu_to_core_id(int cpu);
 #define PPC_MSG_CALL_FUNC_SINGLE	2
 #define PPC_MSG_DEBUGGER_BREAK  3
 
+/*
+ * irq controllers that have dedicated ipis per message and don't
+ * need additional code in the action handler may use this
+ */
+extern int smp_request_message_ipi(int irq, int message);
+extern const char *smp_ipi_name[];
+
 void smp_init_iSeries(void);
 void smp_init_pSeries(void);
 void smp_init_cell(void);
arch/powerpc/kernel/smp.c +59 -0

65 lines not on this page · raw message

#17 lore raw

PATCH 14/16 powerpc mpic: use smp_request_message_ipi

mpic has 4 ipis so use the new smp_request_message_ipi to reduce pathlength when reciving an ipi.

This has the side effect of using the common ipi names, and also continuing to try request the remaining messages when one fails.

arch/powerpc/sysdev/mpic.c +4 -28
Index: next.git/arch/powerpc/sysdev/mpic.c
===================================================================
@@ -616,17 +616,6 @@ static inline void mpic_eoi(struct mpic 
 	(void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI));
 }
 
-#ifdef CONFIG_SMP
-static irqreturn_t mpic_ipi_action(int irq, void *data)
-{
-	long ipi = (long)data;
-
-	smp_message_recv(ipi);
-
-	return IRQ_HANDLED;
-}
-#endif /* CONFIG_SMP */
-
 /*
  * Linux descriptor level callbacks
  */
@@ -1492,13 +1481,7 @@ unsigned int mpic_get_mcirq(void)
 void mpic_request_ipis(void)
 {
 	struct mpic *mpic = mpic_primary;
-	long i, err;
-	static char *ipi_names[] = {
-		"IPI0 (call function)",
-		"IPI1 (reschedule)",
-		"IPI2 (call function single)",
-		"IPI3 (debugger break)",
-	};
+	int i;
 	BUG_ON(mpic == NULL);
 
 	printk(KERN_INFO "mpic: requesting IPIs ... \n");
@@ -1507,17 +1490,10 @@ void mpic_request_ipis(void)
 		unsigned int vipi = irq_create_mapping(mpic->irqhost,
 						       mpic->ipi_vecs[0] + i);
 		if (vipi == NO_IRQ) {
-			printk(KERN_ERR "Failed to map IPI %ld\n", i);
-			break;
-		}
-		err = request_irq(vipi, mpic_ipi_action,
-				  IRQF_DISABLED|IRQF_PERCPU,
-				  ipi_names[i], (void *)i);
-		if (err) {
-			printk(KERN_ERR "Request of irq %d for IPI %ld failed\n",
-			       vipi, i);
-			break;
+			printk(KERN_ERR "Failed to map %s\n", smp_ipi_name[i]);
+			continue;
 		}
+		smp_request_message_ipi(vipi, i);
 	}
 }
 
#18 lore raw

PATCH 15/16 powerpc cell: use smp_request_message_ipi

In reply to Milton Miller

cell native has 4 interrupts for ipis, so use the new smp_request_message_ipi to save pathlength and the data-dependent branch.

This has the side effects of enabling the debugger ipi for kdump and setting IRQF_PERCPU for the ipi interrupts. It doesn't undo the virq mapping if it turns out the ipi is not used.

arch/powerpc/platforms/cell/interrupt.c +7 -20
Index: next.git/arch/powerpc/platforms/cell/interrupt.c
===================================================================
@@ -190,38 +190,25 @@ struct irq_host *iic_get_irq_host(int no
 }
 EXPORT_SYMBOL_GPL(iic_get_irq_host);
 
-static irqreturn_t iic_ipi_action(int irq, void *dev_id)
-{
-	int ipi = (int)(long)dev_id;
-
-	smp_message_recv(ipi);
-
-	return IRQ_HANDLED;
-}
-static void iic_request_ipi(int ipi, const char *name)
+static void iic_request_ipi(int ipi)
 {
 	int virq;
 
 	virq = irq_create_mapping(iic_host, iic_ipi_to_irq(ipi));
 	if (virq == NO_IRQ) {
 		printk(KERN_ERR
-		       "iic: failed to map IPI %s\n", name);
+		       "iic: failed to map IPI %s\n", smp_ipi_name[ipi]);
 		return;
 	}
-	if (request_irq(virq, iic_ipi_action, IRQF_DISABLED, name,
-			(void *)(long)ipi))
-		printk(KERN_ERR
-		       "iic: failed to request IPI %s\n", name);
+	smp_request_message_ipi(virq, ipi);
 }
 
 void iic_request_IPIs(void)
 {
-	iic_request_ipi(PPC_MSG_CALL_FUNCTION, "IPI-call");
-	iic_request_ipi(PPC_MSG_RESCHEDULE, "IPI-resched");
-	iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE, "IPI-call-single");
-#ifdef CONFIG_DEBUGGER
-	iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug");
-#endif /* CONFIG_DEBUGGER */
+	iic_request_ipi(PPC_MSG_CALL_FUNCTION);
+	iic_request_ipi(PPC_MSG_RESCHEDULE);
+	iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE);
+	iic_request_ipi(PPC_MSG_DEBUGGER_BREAK);
 }
 
 #endif /* CONFIG_SMP */
#19 lore raw

PATCH 16/16 powerpc ps3: use smp_request_message_ipi

In reply to Milton Miller

ps3 has 4 ipis per cpu and can use the new smp_request_message_ipi to reduce path length when receiving an ipi.

This has the side effect of setting IRQF_PERCPU.

arch/powerpc/platforms/ps3/smp.c +1 -15
Index: next.git/arch/powerpc/platforms/ps3/smp.c
===================================================================
@@ -32,12 +32,6 @@
 #define DBG pr_debug
 #endif
 
-static irqreturn_t ipi_function_handler(int irq, void *msg)
-{
-	smp_message_recv((int)(long)msg);
-	return IRQ_HANDLED;
-}
-
 /**
   * ps3_ipi_virqs - a per cpu array of virqs for ipi use
   */
@@ -45,13 +39,6 @@ static irqreturn_t ipi_function_handler(
 #define MSG_COUNT 4
 static DEFINE_PER_CPU(unsigned int, ps3_ipi_virqs[MSG_COUNT]);
 
-static const char *names[MSG_COUNT] = {
-	"ipi call",
-	"ipi reschedule",
-	"ipi migrate",
-	"ipi debug brk"
-};
-
 static void do_message_pass(int target, int msg)
 {
 	int result;
@@ -119,8 +106,7 @@ static void __init ps3_smp_setup_cpu(int
 		DBG("%s:%d: (%d, %d) => virq %u\n",
 			__func__, __LINE__, cpu, i, virqs[i]);
 
-		result = request_irq(virqs[i], ipi_function_handler,
-			IRQF_DISABLED, names[i], (void*)(long)i);
+		result = smp_request_message_ipi(virqs[i], i);
 
 		if (result)
 			virqs[i] = NO_IRQ;
#20 lore raw

Re: [PATCH 14/16] powerpc: expand vs demux ipi actions per message

In reply to Milton Miller

arch/powerpc/include/asm/smp.h extern int cpu_to_core_id(int cpu); · line 88

Quoted 5 lines · Milton Miller
arch/powerpc/include/asm/smp.h +5 -0
+/*
+ * irq controllers that have dedicated ipis per message and don't
+ * need additional code in the action handler may use this
+ */
+extern int smp_request_message_ipi(int irq, int message);
                                          ^^^      ^^^^^^^

arch/powerpc/kernel/smp.c void smp_message_recv(int msg) · line 165

Quoted 2 lines
arch/powerpc/kernel/smp.c +2 -0
+/* optional function to request ipi, for controllers with >= 4 ipis */
+int smp_request_message_ipi(int msg, int virq)
                                   ^^^      ^^^^

Woops, the order (and to a lesser extend, the name) of the arguments doesn't match?
Worse, all callers use the order of the prototype, not the one of the implementation.

With kind regards,

Geert Uytterhoeven
Software Architect

Sony Techsoft Centre Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium

Phone:    +32 (0)2 700 8453
Fax:      +32 (0)2 700 8622
E-mail:   [email protected]
Internet: http://www.sony-europe.com/

A division of Sony Europe (Belgium) N.V. VAT BE 0413.825.160 · RPR Brussels Fortis · BIC GEBABEBB · IBAN BE41293037680010

#21 lore raw

PATCH v2 14/16 powerpc: expand vs demux ipi actions per message

In reply to Milton Miller

With the new generic smp call function helpers, I noticed the code in smp_message_recv was a single function call in many cases. While getting the message number from the ipi data is easy, we can reduce the path length by a function and data dependent switch by registering separate ipi actions for these simple calls.

Originally I left the ipi action array exposed, but then I realized the registration code should be common too.

The three users each had their own name array, so I made a fourth to convert all users to use a common one.

---
v2: fix arg reversal noted by Geert Uytterhoeven. This order is most
similar to request_irq and message in data field, but without casting.

Perhaps we should make the common code look like an ipi action handler and remove this last call? Currently we still have to allocate a stack frame to load the return value.

arch/powerpc/include/asm/smp.h +7 -0
Index: next.git/arch/powerpc/include/asm/smp.h
===================================================================
@@ -81,6 +81,13 @@ extern int cpu_to_core_id(int cpu);
 #define PPC_MSG_CALL_FUNC_SINGLE	2
 #define PPC_MSG_DEBUGGER_BREAK  3
 
+/*
+ * irq controllers that have dedicated ipis per message and don't
+ * need additional code in the action handler may use this
+ */
+extern int smp_request_message_ipi(int virq, int message);
+extern const char *smp_ipi_name[];
+
 void smp_init_iSeries(void);
 void smp_init_pSeries(void);
 void smp_init_cell(void);
arch/powerpc/kernel/smp.c +59 -0

65 lines not on this page · raw message

#22 lore raw

Re: [PATCH 14/16 v2] powerpc: expand vs demux ipi actions per message

Quoted 5 lines · Milton Miller

With the new generic smp call function helpers, I noticed the code in smp_message_recv was a single function call in many cases. While getting the message number from the ipi data is easy, we can reduce the path length by a function and data dependent switch by registering separate ipi actions for these simple calls.

With this I get:

CC arch/powerpc/kernel/smp.o
arch/powerpc/kernel/smp.c: In function 'smp_request_message_ipi':
arch/powerpc/kernel/smp.c:177: error: 'ipi_names' undeclared (first use in this function) arch/powerpc/kernel/smp.c:177: error: (Each undeclared identifier is reported only once arch/powerpc/kernel/smp.c:177: error: for each function it appears in.) make[2]: *** [arch/powerpc/kernel/smp.o] Error 1

I think you need smp_ipi_name rather than ipi_names here:

arch/powerpc/kernel/smp.c void smp_message_recv(int msg) · line 180

Quoted 2 lines
arch/powerpc/kernel/smp.c +2 -0
+	WARN(err < 0, "unable to request_irq %d for %s (rc %d)\n",
+		virq, ipi_names[msg], err);

Paul.

#23 lore raw

Re: [PATCH 14/16 v2] powerpc: expand vs demux ipi actions per message

Quoted 17 lines · Paul Mackerras
Quoted 5 lines · Milton Miller

With the new generic smp call function helpers, I noticed the code in smp_message_recv was a single function call in many cases. While getting the message number from the ipi data is easy, we can reduce the path length by a function and data dependent switch by registering separate ipi actions for these simple calls.

With this I get:

CC arch/powerpc/kernel/smp.o
arch/powerpc/kernel/smp.c: In function 'smp_request_message_ipi':
arch/powerpc/kernel/smp.c:177: error: 'ipi_names' undeclared (first use in this function) arch/powerpc/kernel/smp.c:177: error: (Each undeclared identifier is reported only once arch/powerpc/kernel/smp.c:177: error: for each function it appears in.) make[2]: *** [arch/powerpc/kernel/smp.o] Error 1

Quoted 5 lines

I think you need smp_ipi_name rather than ipi_names here:

arch/powerpc/kernel/smp.c void smp_message_recv(int msg) · line 180

Quoted 2 lines
arch/powerpc/kernel/smp.c +2 -0
+	WARN(err < 0, "unable to request_irq %d for %s (rc %d)\n",
+		virq, ipi_names[msg], err);

You are exactly correct. I changed my mind on the name of this varable, and missed this spot on the final edit. However, my compile passed because it was during the time that the powerpc version of WARN was never evaluating its printf arguments.

I wrote and compile tested the change, but need to find or setup a mailer to send it.

milton

#24 lore raw
In reply to Milton Miller
Quoted 9 lines · Milton Miller

The first two patches in this series implememnt a couple of bug fixes for the xics interrupt controller accessed as found in pseries hardware. The next several patches implement some code orginization and cleanups, followed by some changes to reduce code or flag the behavior to generic code.

The last 5 patces are independent of the xics changes, although the first one affects xics. They are designed to reduce interrupt overhead for interprocessor interupts.

I've merged 1..13. The other ones, I'm less sure about, we need to have a closer look (in addition to Geert comments) and they are probably not .28 material anyway.

Cheers,
Ben.