From 967e012ef306e99cfddcd7423f37414e6b568361 Mon Sep 17 00:00:00 2001 From: Sebastien Dugue Date: Thu, 4 Sep 2008 22:37:07 +1000 Subject: powerpc: Separate the irq radix tree insertion and lookup irq_radix_revmap() currently serves 2 purposes, irq mapping lookup and insertion which happen in interrupt and process context respectively. Separate the function into its 2 components, one for lookup only and one for insertion only. Fix the only user of the revmap tree (XICS) to use the new functions. Also, move the insertion into the radix tree of those irqs that were requested before it was initialized at said tree initialization. Mutual exclusion between the tree initialization and readers/writers is handled via a state variable (revmap_trees_allocated) set to 1 when the tree has been initialized and set to 2 after the already requested irqs have been inserted in the tree by the init path. This state is checked before any reader or writer access just like we used to check for tree.gfp_mask != 0 before. Finally, now that we're not any longer inserting nodes into the radix-tree in interrupt context, turn the GFP_ATOMIC allocations into GFP_KERNEL ones. Signed-off-by: Sebastien Dugue Cc: Benjamin Herrenschmidt Cc: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/xics.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 0fc830f576f..6b1a005cc0c 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -310,12 +310,6 @@ static void xics_mask_irq(unsigned int virq) static unsigned int xics_startup(unsigned int virq) { - unsigned int irq; - - /* force a reverse mapping of the interrupt so it gets in the cache */ - irq = (unsigned int)irq_map[virq].hwirq; - irq_radix_revmap(xics_host, irq); - /* unmask it */ xics_unmask_irq(virq); return 0; @@ -346,7 +340,7 @@ static inline unsigned int xics_remap_irq(unsigned int vec) if (vec == XICS_IRQ_SPURIOUS) return NO_IRQ; - irq = irq_radix_revmap(xics_host, vec); + irq = irq_radix_revmap_lookup(xics_host, vec); if (likely(irq != NO_IRQ)) return irq; @@ -530,6 +524,9 @@ static int xics_host_map(struct irq_host *h, unsigned int virq, { pr_debug("xics: map virq %d, hwirq 0x%lx\n", virq, hw); + /* Insert the interrupt mapping into the radix tree for fast lookup */ + irq_radix_revmap_insert(xics_host, virq, hw); + get_irq_desc(virq)->status |= IRQ_LEVEL; set_irq_chip_and_handler(virq, xics_irq_chip, handle_fasteoi_irq); return 0; -- cgit v1.2.3 From 8767e9badca7cdf0adc2564d7524092d47ababf3 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:23 +0000 Subject: powerpc/xics: EOI unmapped irqs after disabling them 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 Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 53 +++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 6b1a005cc0c..1bccd4a56b5 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -332,32 +332,61 @@ static void xics_eoi_lpar(unsigned int virq) 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 -- cgit v1.2.3 From 302905a3473d9a1f00e4b2fe373d2763a041a93d Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:28 +0000 Subject: powerpc/xics: Update default_server during migrate_irqs_away 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 1bccd4a56b5..c95697912fe 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -208,9 +208,6 @@ static int get_irq_server(unsigned int virq, unsigned int strict_check) 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); -- cgit v1.2.3 From d13f7208b211dd3613bdb04e2647081a5160d68f Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:29 +0000 Subject: powerpc/xics: Consolidate ipi message encode and decode 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 61 ++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index c95697912fe..c0cb356833f 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -71,11 +71,6 @@ static unsigned int interrupt_server_size = 8; static struct irq_host *xics_host; -/* - * XICS only has a single IPI, so encode the messages per CPU - */ -struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned; - /* RTAS service tokens */ static int ibm_get_xive; static int ibm_set_xive; @@ -201,6 +196,15 @@ static void xics_update_irq_servers(void) } #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; @@ -387,7 +391,6 @@ static unsigned int xics_get_irq_lpar(void) } #ifdef CONFIG_SMP - static irqreturn_t xics_ipi_dispatch(int cpu) { WARN_ON(cpu_is_offline(cpu)); @@ -419,6 +422,33 @@ static irqreturn_t xics_ipi_dispatch(int cpu) 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) { int cpu = smp_processor_id(); @@ -436,15 +466,6 @@ static irqreturn_t xics_ipi_action_lpar(int irq, void *dev_id) return xics_ipi_dispatch(cpu); } - -void xics_cause_IPI(int cpu) -{ - if (firmware_has_feature(FW_FEATURE_LPAR)) - lpar_qirr_info(cpu, IPI_PRIORITY); - else - direct_qirr_info(cpu, IPI_PRIORITY); -} - #endif /* CONFIG_SMP */ static void xics_set_cpu_priority(unsigned char cppr) @@ -697,7 +718,7 @@ void __init xics_init_IRQ(void) #ifdef CONFIG_SMP -void xics_request_IPIs(void) +static void xics_request_ipi(void) { unsigned int ipi; int rc; @@ -718,6 +739,14 @@ void xics_request_IPIs(void) "IPI", NULL); BUG_ON(rc); } + +int __init smp_xics_probe(void) +{ + xics_request_ipi(); + + return cpus_weight(cpu_possible_map); +} + #endif /* CONFIG_SMP */ void xics_teardown_cpu(void) -- cgit v1.2.3 From 0641cc91b08937578263589feb15182b9ad2b0fc Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:30 +0000 Subject: powerpc/xics: Rearrange file to group code by function 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 432 +++++++++++++++++----------------- 1 file changed, 217 insertions(+), 215 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index c0cb356833f..c98e3a12846 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ - #include #include #include @@ -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 n_cpu, u8 value) /* 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_cpu , u8 value) } -/* 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 virq, unsigned int strict_check) } #endif - static void xics_unmask_irq(unsigned int virq) { unsigned int irq; @@ -273,6 +220,13 @@ static void xics_unmask_irq(unsigned int virq) } } +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 virq) 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_vector(unsigned int xirr) 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(void) 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 int virq, cpumask_t cpumask) } } -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(); -- cgit v1.2.3 From 9dc2d44113d1521d8ead8e89e0772c0957b093c2 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:32 +0000 Subject: powerpc/xics: Change *_xirr_info_set() prototype to avoid casts 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index c98e3a12846..98e73720433 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -87,7 +87,7 @@ static inline unsigned int direct_xirr_info_get(void) 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_info_get(void) 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) -- cgit v1.2.3 From 188bdddd243e6872608099bd1142a03b70571132 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:33 +0000 Subject: powerpc/xics: Trim #include list 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 98e73720433..3501fa1fe8c 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -15,21 +15,18 @@ #include #include #include -#include #include -#include #include #include +#include #include -#include #include #include #include #include #include #include -#include #include "xics.h" #include "plpar_wrappers.h" -- cgit v1.2.3 From a244a957ab15ddbeccf4018ef4b3ac8f5fd1566d Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:33 +0000 Subject: powerpc/xics: Initialization code cleanups 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 3501fa1fe8c..27327fc8605 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/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 hw_id, unsigned long addr, 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) -- cgit v1.2.3 From b4963255ad5a426f04a0bb15c4315fa4bb40cde9 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:34 +0000 Subject: powerpc/xics: Factor out cpu joining/unjoining the GIQ This factors out processors joining and unjoining the Global Interrupt Queue into a separate function. 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 27327fc8605..0bb553331f4 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -727,20 +727,19 @@ static void xics_set_cpu_priority(unsigned char cppr) 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 secondary) * 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 secondary) /* 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 */ -- cgit v1.2.3 From 1a57c926b6da56b4f904a0d8117ac362724f8c66 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:35 +0000 Subject: powerpc/xics: EOI xics ipi by hand in kexec 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 0bb553331f4..165234d2599 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/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, -- cgit v1.2.3 From d879f3849c93743e170a8dc60a8dfb66150c420d Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:39 +0000 Subject: powerpc/xics: Mark xics IPI interrupt as per-cpu It is physically per-cpu, and we want the irq layer to treat it that way. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 165234d2599..5ba3e009296 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -556,11 +556,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); } -- cgit v1.2.3 From 2172fe8704a1df7cbb988ae1ec4edbfef3e28860 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:39 +0000 Subject: powerpc/xics: Make printk format strings fit on one line 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 5ba3e009296..62b0400577d 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -201,17 +201,17 @@ static void xics_unmask_irq(unsigned int virq) 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 int irq) 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 int irq) 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 int virq, cpumask_t cpumask) 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 int virq, cpumask_t cpumask) 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 int virq, cpumask_t cpumask) 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; } -- cgit v1.2.3 From 199f45c45e8d4f58a5f568464be933534460eb82 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 10 Oct 2008 01:56:44 +0000 Subject: powerpc/xics: Reduce and comment xics IPI use of memory barriers 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. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/xics.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 62b0400577d..e1904774a70 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -498,26 +498,23 @@ static irqreturn_t xics_ipi_dispatch(int cpu) { 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 -- cgit v1.2.3 From 1ef8014debb6410ed1960c4477d0006df11157c1 Mon Sep 17 00:00:00 2001 From: Sebastien Dugue Date: Wed, 22 Oct 2008 04:36:32 +0000 Subject: powerpc/pseries: Fix getting the server number size The 'ibm,interrupt-server#-size' properties are not in the cpu nodes, which is where we currently look for them, but rather live under the interrupt source controller nodes (which have "ibm,ppc-xics" in their compatible property). This moves the code that looks for the ibm,interrupt-server#-size properties from xics_update_irq_servers() into xics_init_IRQ(). Also this adds a check for mismatched sizes across the interrupt source controller nodes. Not sure this is necessary as in this case the firmware might be seriously busted. This property only appears on POWER6 boxes and is only used in the set-indicator(gqirm) call, and apparently firmware currently ignores the value we pass. Nevertheless we need to fix it in case future firmware versions use it. Signed-off-by: Sebastien Dugue Cc: Benjamin Herrenschmidt Acked-by: Milton Miller Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/xics.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index e1904774a70..75a289ba66b 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -579,7 +579,7 @@ static void xics_update_irq_servers(void) int i, j; struct device_node *np; u32 ilen; - const u32 *ireg, *isize; + const u32 *ireg; u32 hcpuid; /* Find the server numbers for the boot cpu. */ @@ -607,11 +607,6 @@ static void xics_update_irq_servers(void) } } - /* 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); } @@ -682,6 +677,7 @@ void __init xics_init_IRQ(void) struct device_node *np; u32 indx = 0; int found = 0; + const u32 *isize; ppc64_boot_msg(0x20, "XICS Init"); @@ -701,6 +697,26 @@ void __init xics_init_IRQ(void) if (found == 0) return; + /* get the bit size of server numbers */ + found = 0; + + for_each_compatible_node(np, NULL, "ibm,ppc-xics") { + isize = of_get_property(np, "ibm,interrupt-server#-size", NULL); + + if (!isize) + continue; + + if (!found) { + interrupt_server_size = *isize; + found = 1; + } else if (*isize != interrupt_server_size) { + printk(KERN_WARNING "XICS: " + "mismatched ibm,interrupt-server#-size\n"); + interrupt_server_size = max(*isize, + interrupt_server_size); + } + } + xics_update_irq_servers(); xics_init_host(); -- cgit v1.2.3 From edc72ac4a0894247a6d3f1157a8ec8d603fff52d Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Thu, 11 Dec 2008 09:14:25 +0000 Subject: powerpc/pseries: Check for GIQ indicator before calling set-indicator Since "Factor out cpu joining/unjoining the GIQ" (b4963255ad5a426f04a0bb15c4315fa4bb40cde9) the WARN_ON in xics_set_cpu_giq() is being triggered during boot on JS20 because the GIQ indicator is not available on that platform. While the warning is harmless and the system runs normally, it's nicer to check for the existence of the indicator before trying to manipulate it. Implement rtas_indicator_present(), which searches the /rtas/rtas-indicators property for the given indicator token, and use this function in xics_set_cpu_giq(). Also use a WARN statement in xics_set_cpu_giq to get better information on failure. Signed-off-by: Nathan Lynch Acked-by: Milton Miller Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/xics.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/platforms/pseries/xics.c') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 75a289ba66b..f7a69021b7b 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -744,9 +744,18 @@ static void xics_set_cpu_priority(unsigned char cppr) /* 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); + int index; + int status; + + if (!rtas_indicator_present(GLOBAL_INTERRUPT_QUEUE, NULL)) + return; + + index = (1UL << interrupt_server_size) - 1 - gserver; + + status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE, index, join); + + WARN(status < 0, "set-indicator(%d, %d, %u) returned %d\n", + GLOBAL_INTERRUPT_QUEUE, index, join, status); } void xics_setup_cpu(void) -- cgit v1.2.3