fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / ia64 / kernel / palinfo.c
index 27123bb..a71df9a 100644 (file)
@@ -8,13 +8,16 @@
  *
  * Copyright (C) 2000-2001, 2003 Hewlett-Packard Co
  *     Stephane Eranian <eranian@hpl.hp.com>
  *
  * Copyright (C) 2000-2001, 2003 Hewlett-Packard Co
  *     Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 2004 Intel Corporation
+ *  Ashok Raj <ashok.raj@intel.com>
  *
  * 05/26/2000  S.Eranian       initial release
  * 08/21/2000  S.Eranian       updated to July 2000 PAL specs
  * 02/05/2001   S.Eranian      fixed module support
  * 10/23/2001  S.Eranian       updated pal_perf_mon_info bug fixes
  *
  * 05/26/2000  S.Eranian       initial release
  * 08/21/2000  S.Eranian       updated to July 2000 PAL specs
  * 02/05/2001   S.Eranian      fixed module support
  * 10/23/2001  S.Eranian       updated pal_perf_mon_info bug fixes
+ * 03/24/2004  Ashok Raj       updated to work with CPU Hotplug
+ * 10/26/2006   Russ Anderson  updated processor features to rev 2.2 spec
  */
  */
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -22,6 +25,9 @@
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/efi.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/efi.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
 
 #include <asm/pal.h>
 #include <asm/sal.h>
 
 #include <asm/pal.h>
 #include <asm/sal.h>
@@ -234,7 +240,7 @@ cache_info(char *page)
                        }
                        p += sprintf(p,
                                     "%s Cache level %lu:\n"
                        }
                        p += sprintf(p,
                                     "%s Cache level %lu:\n"
-                                    "\tSize           : %lu bytes\n"
+                                    "\tSize           : %u bytes\n"
                                     "\tAttributes     : ",
                                     cache_types[j+cci.pcci_unified], i+1,
                                     cci.pcci_cache_size);
                                     "\tAttributes     : ",
                                     cache_types[j+cci.pcci_unified], i+1,
                                     cci.pcci_cache_size);
@@ -301,104 +307,116 @@ vm_info(char *page)
 
        if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
                printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
 
        if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
                printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
-               return 0;
-       }
-
+       } else {
 
 
-       p += sprintf(p,
+               p += sprintf(p,
                     "Physical Address Space         : %d bits\n"
                     "Virtual Address Space          : %d bits\n"
                     "Protection Key Registers(PKR)  : %d\n"
                     "Implemented bits in PKR.key    : %d\n"
                     "Hash Tag ID                    : 0x%x\n"
                     "Physical Address Space         : %d bits\n"
                     "Virtual Address Space          : %d bits\n"
                     "Protection Key Registers(PKR)  : %d\n"
                     "Implemented bits in PKR.key    : %d\n"
                     "Hash Tag ID                    : 0x%x\n"
-                    "Size of RR.rid                 : %d\n",
+                    "Size of RR.rid                 : %d\n"
+                    "Max Purges                     : ",
                     vm_info_1.pal_vm_info_1_s.phys_add_size,
                     vm_info_1.pal_vm_info_1_s.phys_add_size,
-                    vm_info_2.pal_vm_info_2_s.impl_va_msb+1, vm_info_1.pal_vm_info_1_s.max_pkr+1,
-                    vm_info_1.pal_vm_info_1_s.key_size, vm_info_1.pal_vm_info_1_s.hash_tag_id,
+                    vm_info_2.pal_vm_info_2_s.impl_va_msb+1,
+                    vm_info_1.pal_vm_info_1_s.max_pkr+1,
+                    vm_info_1.pal_vm_info_1_s.key_size,
+                    vm_info_1.pal_vm_info_1_s.hash_tag_id,
                     vm_info_2.pal_vm_info_2_s.rid_size);
                     vm_info_2.pal_vm_info_2_s.rid_size);
+               if (vm_info_2.pal_vm_info_2_s.max_purges == PAL_MAX_PURGES)
+                       p += sprintf(p, "unlimited\n");
+               else
+                       p += sprintf(p, "%d\n",
+                               vm_info_2.pal_vm_info_2_s.max_purges ?
+                               vm_info_2.pal_vm_info_2_s.max_purges : 1);
+       }
 
 
-       if (ia64_pal_mem_attrib(&attrib) != 0)
-               return 0;
-
-       p += sprintf(p, "Supported memory attributes    : ");
-       sep = "";
-       for (i = 0; i < 8; i++) {
-               if (attrib & (1 << i)) {
-                       p += sprintf(p, "%s%s", sep, mem_attrib[i]);
-                       sep = ", ";
+       if (ia64_pal_mem_attrib(&attrib) == 0) {
+               p += sprintf(p, "Supported memory attributes    : ");
+               sep = "";
+               for (i = 0; i < 8; i++) {
+                       if (attrib & (1 << i)) {
+                               p += sprintf(p, "%s%s", sep, mem_attrib[i]);
+                               sep = ", ";
+                       }
                }
                }
+               p += sprintf(p, "\n");
        }
        }
-       p += sprintf(p, "\n");
 
        if ((status = ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) {
                printk(KERN_ERR "ia64_pal_vm_page_size=%ld\n", status);
 
        if ((status = ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) {
                printk(KERN_ERR "ia64_pal_vm_page_size=%ld\n", status);
-               return 0;
-       }
-
-       p += sprintf(p,
-                    "\nTLB walker                     : %simplemented\n"
-                    "Number of DTR                  : %d\n"
-                    "Number of ITR                  : %d\n"
-                    "TLB insertable page sizes      : ",
-                    vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
-                    vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
-                    vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
+       } else {
 
 
+               p += sprintf(p,
+                            "\nTLB walker                     : %simplemented\n"
+                            "Number of DTR                  : %d\n"
+                            "Number of ITR                  : %d\n"
+                            "TLB insertable page sizes      : ",
+                            vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
+                            vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
+                            vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
 
 
-       p = bitvector_process(p, tr_pages);
 
 
-       p += sprintf(p, "\nTLB purgeable page sizes       : ");
+               p = bitvector_process(p, tr_pages);
 
 
-       p = bitvector_process(p, vw_pages);
+               p += sprintf(p, "\nTLB purgeable page sizes       : ");
 
 
+               p = bitvector_process(p, vw_pages);
+       }
        if ((status=ia64_get_ptce(&ptce)) != 0) {
                printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
        if ((status=ia64_get_ptce(&ptce)) != 0) {
                printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
-               return 0;
-       }
-
-       p += sprintf(p,
+       } else {
+               p += sprintf(p,
                     "\nPurge base address             : 0x%016lx\n"
                     "Purge outer loop count         : %d\n"
                     "Purge inner loop count         : %d\n"
                     "Purge outer loop stride        : %d\n"
                     "Purge inner loop stride        : %d\n",
                     "\nPurge base address             : 0x%016lx\n"
                     "Purge outer loop count         : %d\n"
                     "Purge inner loop count         : %d\n"
                     "Purge outer loop stride        : %d\n"
                     "Purge inner loop stride        : %d\n",
-                    ptce.base, ptce.count[0], ptce.count[1], ptce.stride[0], ptce.stride[1]);
+                    ptce.base, ptce.count[0], ptce.count[1],
+                    ptce.stride[0], ptce.stride[1]);
 
 
-       p += sprintf(p,
+               p += sprintf(p,
                     "TC Levels                      : %d\n"
                     "Unique TC(s)                   : %d\n",
                     vm_info_1.pal_vm_info_1_s.num_tc_levels,
                     vm_info_1.pal_vm_info_1_s.max_unique_tcs);
 
                     "TC Levels                      : %d\n"
                     "Unique TC(s)                   : %d\n",
                     vm_info_1.pal_vm_info_1_s.num_tc_levels,
                     vm_info_1.pal_vm_info_1_s.max_unique_tcs);
 
-       for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
-               for (j=2; j>0 ; j--) {
-                       tc_pages = 0; /* just in case */
+               for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
+                       for (j=2; j>0 ; j--) {
+                               tc_pages = 0; /* just in case */
 
 
 
 
-                       /* even without unification, some levels may not be present */
-                       if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
-                               continue;
-                       }
+                               /* even without unification, some levels may not be present */
+                               if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
+                                       continue;
+                               }
 
 
-                       p += sprintf(p,
+                               p += sprintf(p,
                                     "\n%s Translation Cache Level %d:\n"
                                     "\tHash sets           : %d\n"
                                     "\tAssociativity       : %d\n"
                                     "\tNumber of entries   : %d\n"
                                     "\tFlags               : ",
                                     "\n%s Translation Cache Level %d:\n"
                                     "\tHash sets           : %d\n"
                                     "\tAssociativity       : %d\n"
                                     "\tNumber of entries   : %d\n"
                                     "\tFlags               : ",
-                                    cache_types[j+tc_info.tc_unified], i+1, tc_info.tc_num_sets,
-                                    tc_info.tc_associativity, tc_info.tc_num_entries);
+                                    cache_types[j+tc_info.tc_unified], i+1,
+                                    tc_info.tc_num_sets,
+                                    tc_info.tc_associativity,
+                                    tc_info.tc_num_entries);
 
 
-                       if (tc_info.tc_pf) p += sprintf(p, "PreferredPageSizeOptimized ");
-                       if (tc_info.tc_unified) p += sprintf(p, "Unified ");
-                       if (tc_info.tc_reduce_tr) p += sprintf(p, "TCReduction");
+                               if (tc_info.tc_pf)
+                                       p += sprintf(p, "PreferredPageSizeOptimized ");
+                               if (tc_info.tc_unified)
+                                       p += sprintf(p, "Unified ");
+                               if (tc_info.tc_reduce_tr)
+                                       p += sprintf(p, "TCReduction");
 
 
-                       p += sprintf(p, "\n\tSupported page sizes: ");
+                               p += sprintf(p, "\n\tSupported page sizes: ");
 
 
-                       p = bitvector_process(p, tc_pages);
+                               p = bitvector_process(p, tc_pages);
 
 
-                       /* when unified date (j=2) is enough */
-                       if (tc_info.tc_unified) break;
+                               /* when unified date (j=2) is enough */
+                               if (tc_info.tc_unified)
+                                       break;
+                       }
                }
        }
        p += sprintf(p, "\n");
                }
        }
        p += sprintf(p, "\n");
@@ -434,14 +452,14 @@ register_info(char *page)
                p += sprintf(p, "\n");
        }
 
                p += sprintf(p, "\n");
        }
 
-       if (ia64_pal_rse_info(&phys_stacked, &hints) != 0) return 0;
+       if (ia64_pal_rse_info(&phys_stacked, &hints) == 0) {
 
        p += sprintf(p,
                     "RSE stacked physical registers   : %ld\n"
                     "RSE load/store hints             : %ld (%s)\n",
                     phys_stacked, hints.ph_data,
 
        p += sprintf(p,
                     "RSE stacked physical registers   : %ld\n"
                     "RSE load/store hints             : %ld (%s)\n",
                     phys_stacked, hints.ph_data,
-                    hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(\?\?)");
-
+                    hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)");
+       }
        if (ia64_pal_debug_info(&iregs, &dregs))
                return 0;
 
        if (ia64_pal_debug_info(&iregs, &dregs))
                return 0;
 
@@ -457,7 +475,11 @@ static const char *proc_features[]={
        NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
        NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
        NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
-       NULL,NULL,NULL,NULL,NULL,
+       "Unimplemented instruction address fault",
+       "INIT, PMI, and LINT pins",
+       "Simple unimplemented instr addresses",
+       "Variable P-state performance",
+       "Virtual machine features implemented",
        "XIP,XPSR,XFS implemented",
        "XR1-XR3 implemented",
        "Disable dynamic predicate prediction",
        "XIP,XPSR,XFS implemented",
        "XR1-XR3 implemented",
        "Disable dynamic predicate prediction",
@@ -465,7 +487,11 @@ static const char *proc_features[]={
        "Disable dynamic data cache prefetch",
        "Disable dynamic inst cache prefetch",
        "Disable dynamic branch prediction",
        "Disable dynamic data cache prefetch",
        "Disable dynamic inst cache prefetch",
        "Disable dynamic branch prediction",
-       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL,
+       "Disable P-states",
+       "Enable MCA on Data Poisoning",
+       "Enable vmsw instruction",
+       "Enable extern environmental notification",
        "Disable BINIT on processor time-out",
        "Disable dynamic power management (DPM)",
        "Disable coherency",
        "Disable BINIT on processor time-out",
        "Disable dynamic power management (DPM)",
        "Disable coherency",
@@ -473,7 +499,7 @@ static const char *proc_features[]={
        "Enable CMCI promotion",
        "Enable MCA to BINIT promotion",
        "Enable MCA promotion",
        "Enable CMCI promotion",
        "Enable MCA to BINIT promotion",
        "Enable MCA promotion",
-       "Enable BEER promotion"
+       "Enable BERR promotion"
 };
 
 
 };
 
 
@@ -509,10 +535,10 @@ static const char *bus_features[]={
        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
        NULL, NULL, NULL, NULL,
        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
        NULL, NULL, NULL, NULL,
-       "Enable Cache Line Repl. Exclusive",
        "Enable Cache Line Repl. Shared",
        "Enable Cache Line Repl. Shared",
+       "Enable Cache Line Repl. Exclusive",
        "Disable Transaction Queuing",
        "Disable Transaction Queuing",
-       "Disable Reponse Error Checking",
+       "Disable Response Error Checking",
        "Disable Bus Error Checking",
        "Disable Bus Requester Internal Error Signalling",
        "Disable Bus Requester Error Signalling",
        "Disable Bus Error Checking",
        "Disable Bus Requester Internal Error Signalling",
        "Disable Bus Requester Error Signalling",
@@ -556,29 +582,23 @@ version_info(char *page)
        pal_version_u_t min_ver, cur_ver;
        char *p = page;
 
        pal_version_u_t min_ver, cur_ver;
        char *p = page;
 
-       /* The PAL_VERSION call is advertised as being able to support
-        * both physical and virtual mode calls. This seems to be a documentation
-        * bug rather than firmware bug. In fact, it does only support physical mode.
-        * So now the code reflects this fact and the pal_version() has been updated
-        * accordingly.
-        */
-       if (ia64_pal_version(&min_ver, &cur_ver) != 0) return 0;
+       if (ia64_pal_version(&min_ver, &cur_ver) != 0)
+               return 0;
 
        p += sprintf(p,
                     "PAL_vendor : 0x%02x (min=0x%02x)\n"
 
        p += sprintf(p,
                     "PAL_vendor : 0x%02x (min=0x%02x)\n"
-                    "PAL_A      : %x.%x.%x (min=%x.%x.%x)\n"
-                    "PAL_B      : %x.%x.%x (min=%x.%x.%x)\n",
-                    cur_ver.pal_version_s.pv_pal_vendor, min_ver.pal_version_s.pv_pal_vendor,
-
-                    cur_ver.pal_version_s.pv_pal_a_model>>4,
-                    cur_ver.pal_version_s.pv_pal_a_model&0xf, cur_ver.pal_version_s.pv_pal_a_rev,
-                    min_ver.pal_version_s.pv_pal_a_model>>4,
-                    min_ver.pal_version_s.pv_pal_a_model&0xf, min_ver.pal_version_s.pv_pal_a_rev,
-
-                    cur_ver.pal_version_s.pv_pal_b_model>>4,
-                    cur_ver.pal_version_s.pv_pal_b_model&0xf, cur_ver.pal_version_s.pv_pal_b_rev,
-                    min_ver.pal_version_s.pv_pal_b_model>>4,
-                    min_ver.pal_version_s.pv_pal_b_model&0xf, min_ver.pal_version_s.pv_pal_b_rev);
+                    "PAL_A      : %02x.%02x (min=%02x.%02x)\n"
+                    "PAL_B      : %02x.%02x (min=%02x.%02x)\n",
+                    cur_ver.pal_version_s.pv_pal_vendor,
+                    min_ver.pal_version_s.pv_pal_vendor,
+                    cur_ver.pal_version_s.pv_pal_a_model,
+                    cur_ver.pal_version_s.pv_pal_a_rev,
+                    min_ver.pal_version_s.pv_pal_a_model,
+                    min_ver.pal_version_s.pv_pal_a_rev,
+                    cur_ver.pal_version_s.pv_pal_b_model,
+                    cur_ver.pal_version_s.pv_pal_b_rev,
+                    min_ver.pal_version_s.pv_pal_b_model,
+                    min_ver.pal_version_s.pv_pal_b_rev);
        return p - page;
 }
 
        return p - page;
 }
 
@@ -637,9 +657,9 @@ frequency_info(char *page)
        if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
 
        p += sprintf(p,
        if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
 
        p += sprintf(p,
-                    "Processor/Clock ratio   : %ld/%ld\n"
-                    "Bus/Clock ratio         : %ld/%ld\n"
-                    "ITC/Clock ratio         : %ld/%ld\n",
+                    "Processor/Clock ratio   : %d/%d\n"
+                    "Bus/Clock ratio         : %d/%d\n"
+                    "ITC/Clock ratio         : %d/%d\n",
                     proc.num, proc.den, bus.num, bus.den, itc.num, itc.den);
 
        return p - page;
                     proc.num, proc.den, bus.num, bus.den, itc.num, itc.den);
 
        return p - page;
@@ -768,13 +788,12 @@ static palinfo_entry_t palinfo_entries[]={
  * does not do recursion of deletion
  *
  * Notes:
  * does not do recursion of deletion
  *
  * Notes:
- *     - first +1 accounts for the cpuN entry
- *     - second +1 account for toplevel palinfo
- *
+ *     - +1 accounts for the cpuN directory entry in /proc/pal
  */
  */
-#define NR_PALINFO_PROC_ENTRIES        (NR_CPUS*(NR_PALINFO_ENTRIES+1)+1)
+#define NR_PALINFO_PROC_ENTRIES        (NR_CPUS*(NR_PALINFO_ENTRIES+1))
 
 static struct proc_dir_entry *palinfo_proc_entries[NR_PALINFO_PROC_ENTRIES];
 
 static struct proc_dir_entry *palinfo_proc_entries[NR_PALINFO_PROC_ENTRIES];
+static struct proc_dir_entry *palinfo_dir;
 
 /*
  * This data structure is used to pass which cpu,function is being requested
 
 /*
  * This data structure is used to pass which cpu,function is being requested
@@ -888,47 +907,104 @@ palinfo_read_entry(char *page, char **start, off_t off, int count, int *eof, voi
        return len;
 }
 
        return len;
 }
 
-static int __init
-palinfo_init(void)
+static void
+create_palinfo_proc_entries(unsigned int cpu)
 {
 #      define CPUSTR   "cpu%d"
 
        pal_func_cpu_u_t f;
 {
 #      define CPUSTR   "cpu%d"
 
        pal_func_cpu_u_t f;
-       struct proc_dir_entry **pdir = palinfo_proc_entries;
-       struct proc_dir_entry *palinfo_dir, *cpu_dir;
-       int i, j;
+       struct proc_dir_entry **pdir;
+       struct proc_dir_entry *cpu_dir;
+       int j;
        char cpustr[sizeof(CPUSTR)];
 
        char cpustr[sizeof(CPUSTR)];
 
-       printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
-
-       palinfo_dir = proc_mkdir("pal", NULL);
 
        /*
         * we keep track of created entries in a depth-first order for
         * cleanup purposes. Each entry is stored into palinfo_proc_entries
         */
 
        /*
         * we keep track of created entries in a depth-first order for
         * cleanup purposes. Each entry is stored into palinfo_proc_entries
         */
-       for (i=0; i < NR_CPUS; i++) {
+       sprintf(cpustr,CPUSTR, cpu);
 
 
-               if (!cpu_online(i)) continue;
+       cpu_dir = proc_mkdir(cpustr, palinfo_dir);
 
 
-               sprintf(cpustr,CPUSTR, i);
+       f.req_cpu = cpu;
 
 
-               cpu_dir = proc_mkdir(cpustr, palinfo_dir);
-
-               f.req_cpu = i;
+       /*
+        * Compute the location to store per cpu entries
+        * We dont store the top level entry in this list, but
+        * remove it finally after removing all cpu entries.
+        */
+       pdir = &palinfo_proc_entries[cpu*(NR_PALINFO_ENTRIES+1)];
+       *pdir++ = cpu_dir;
+       for (j=0; j < NR_PALINFO_ENTRIES; j++) {
+               f.func_id = j;
+               *pdir = create_proc_read_entry(
+                               palinfo_entries[j].name, 0, cpu_dir,
+                               palinfo_read_entry, (void *)f.value);
+               if (*pdir)
+                       (*pdir)->owner = THIS_MODULE;
+               pdir++;
+       }
+}
 
 
-               for (j=0; j < NR_PALINFO_ENTRIES; j++) {
-                       f.func_id = j;
-                       *pdir = create_proc_read_entry(
-                                       palinfo_entries[j].name, 0, cpu_dir,
-                                       palinfo_read_entry, (void *)f.value);
-                       if (*pdir)
-                               (*pdir)->owner = THIS_MODULE;
-                       pdir++;
+static void
+remove_palinfo_proc_entries(unsigned int hcpu)
+{
+       int j;
+       struct proc_dir_entry *cpu_dir, **pdir;
+
+       pdir = &palinfo_proc_entries[hcpu*(NR_PALINFO_ENTRIES+1)];
+       cpu_dir = *pdir;
+       *pdir++=NULL;
+       for (j=0; j < (NR_PALINFO_ENTRIES); j++) {
+               if ((*pdir)) {
+                       remove_proc_entry ((*pdir)->name, cpu_dir);
+                       *pdir ++= NULL;
                }
                }
-               *pdir++ = cpu_dir;
        }
        }
-       *pdir = palinfo_dir;
+
+       if (cpu_dir) {
+               remove_proc_entry(cpu_dir->name, palinfo_dir);
+       }
+}
+
+static int palinfo_cpu_callback(struct notifier_block *nfb,
+                                       unsigned long action, void *hcpu)
+{
+       unsigned int hotcpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+               create_palinfo_proc_entries(hotcpu);
+               break;
+       case CPU_DEAD:
+               remove_palinfo_proc_entries(hotcpu);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block palinfo_cpu_notifier =
+{
+       .notifier_call = palinfo_cpu_callback,
+       .priority = 0,
+};
+
+static int __init
+palinfo_init(void)
+{
+       int i = 0;
+
+       printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
+       palinfo_dir = proc_mkdir("pal", NULL);
+
+       /* Create palinfo dirs in /proc for all online cpus */
+       for_each_online_cpu(i) {
+               create_palinfo_proc_entries(i);
+       }
+
+       /* Register for future delivery via notify registration */
+       register_hotcpu_notifier(&palinfo_cpu_notifier);
 
        return 0;
 }
 
        return 0;
 }
@@ -939,10 +1015,19 @@ palinfo_exit(void)
        int i = 0;
 
        /* remove all nodes: depth first pass. Could optimize this  */
        int i = 0;
 
        /* remove all nodes: depth first pass. Could optimize this  */
-       for (i=0; i< NR_PALINFO_PROC_ENTRIES ; i++) {
-               if (palinfo_proc_entries[i])
-                       remove_proc_entry (palinfo_proc_entries[i]->name, NULL);
+       for_each_online_cpu(i) {
+               remove_palinfo_proc_entries(i);
        }
        }
+
+       /*
+        * Remove the top level entry finally
+        */
+       remove_proc_entry(palinfo_dir->name, NULL);
+
+       /*
+        * Unregister from cpu notifier callbacks
+        */
+       unregister_hotcpu_notifier(&palinfo_cpu_notifier);
 }
 
 module_init(palinfo_init);
 }
 
 module_init(palinfo_init);