[POWERPC] pSeries/kexec: Fix for interrupt distribution
authorMohan Kumar M <mohan@in.ibm.com>
Fri, 17 Nov 2006 12:12:24 +0000 (17:42 +0530)
committerPaul Mackerras <paulus@samba.org>
Mon, 4 Dec 2006 09:41:22 +0000 (20:41 +1100)
This allows any secondary CPU thread also to become boot cpu for
POWER5.  The patch is required to solve kdump boot issue when the
kdump kernel is booted with parameter "maxcpus=1".  XICS init code
tries to match the current boot cpu id with "reg" property in each CPU
node in the device tree.  But CPU node is created only for primary
thread CPU ids and "reg" property only reflects primary CPU ids.  So
when a kernel is booted on a secondary cpu thread above condition will
never meet and the default distribution server is left as zero.  This
leads to route the interrupts to CPU 0, but which is not online at
this time.

We use ibm,ppc-interrupt-server#s to check for both primary and
secondary CPU ids.  Accordingly default distribution server value is
initialized from "ibm,ppc-interrupt-gserver#s" property.  We loop
through ibm,ppc-interrupt-gserver#s property to find the global
distribution server from the last entry that matches with boot cpuid.

Signed-off-by: Mohan Kumar M <mohan@in.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/platforms/pseries/xics.c

index d071abe78ab196943a56b8ee9ce097650a34cb00..b5b2b1103de8d58dd2f49b6b766fe4d429913c46 100644 (file)
@@ -656,13 +656,38 @@ static void __init xics_setup_8259_cascade(void)
        set_irq_chained_handler(cascade, pseries_8259_cascade);
 }
 
+static struct device_node *cpuid_to_of_node(int cpu)
+{
+       struct device_node *np;
+       u32 hcpuid = get_hard_smp_processor_id(cpu);
+
+       for_each_node_by_type(np, "cpu") {
+               int i, len;
+               const u32 *intserv;
+
+               intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len);
+
+               if (!intserv)
+                       intserv = get_property(np, "reg", &len);
+
+               i = len / sizeof(u32);
+
+               while (i--)
+                       if (intserv[i] == hcpuid)
+                               return np;
+       }
+
+       return NULL;
+}
+
 void __init xics_init_IRQ(void)
 {
-       int i;
+       int i, j;
        struct device_node *np;
        u32 ilen, indx = 0;
-       const u32 *ireg;
+       const u32 *ireg, *isize;
        int found = 0;
+       u32 hcpuid;
 
        ppc64_boot_msg(0x20, "XICS Init");
 
@@ -683,26 +708,31 @@ void __init xics_init_IRQ(void)
        xics_init_host();
 
        /* Find the server numbers for the boot cpu. */
-       for (np = of_find_node_by_type(NULL, "cpu");
-            np;
-            np = of_find_node_by_type(np, "cpu")) {
-               ireg = get_property(np, "reg", &ilen);
-               if (ireg && ireg[0] == get_hard_smp_processor_id(boot_cpuid)) {
-                       ireg = get_property(np,
-                                       "ibm,ppc-interrupt-gserver#s", &ilen);
-                       i = ilen / sizeof(int);
-                       if (ireg && i > 0) {
-                               default_server = ireg[0];
-                               /* take last element */
-                               default_distrib_server = ireg[i-1];
-                       }
-                       ireg = get_property(np,
+       np = cpuid_to_of_node(boot_cpuid);
+       BUG_ON(!np);
+       ireg = get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
+       if (!ireg)
+               goto skip_gserver_check;
+       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 = get_property(np,
                                        "ibm,interrupt-server#-size", NULL);
-                       if (ireg)
-                               interrupt_server_size = *ireg;
-                       break;
+                       if (isize)
+                               interrupt_server_size = *isize;
                }
        }
+skip_gserver_check:
        of_node_put(np);
 
        if (firmware_has_feature(FW_FEATURE_LPAR))