4 * Prints processor specific information reported by PAL.
5 * This code is based on specification of PAL as of the
6 * Intel IA-64 Architecture Software Developer's Manual v1.0.
9 * Copyright (C) 2000-2001, 2003 Hewlett-Packard Co
10 * Stephane Eranian <eranian@hpl.hp.com>
11 * Copyright (C) 2004 Intel Corporation
12 * Ashok Raj <ashok.raj@intel.com>
14 * 05/26/2000 S.Eranian initial release
15 * 08/21/2000 S.Eranian updated to July 2000 PAL specs
16 * 02/05/2001 S.Eranian fixed module support
17 * 10/23/2001 S.Eranian updated pal_perf_mon_info bug fixes
18 * 03/24/2004 Ashok Raj updated to work with CPU Hotplug
20 #include <linux/types.h>
21 #include <linux/errno.h>
22 #include <linux/init.h>
23 #include <linux/proc_fs.h>
25 #include <linux/module.h>
26 #include <linux/efi.h>
27 #include <linux/notifier.h>
28 #include <linux/cpu.h>
29 #include <linux/cpumask.h>
34 #include <asm/processor.h>
35 #include <linux/smp.h>
37 MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
38 MODULE_DESCRIPTION("/proc interface to IA-64 PAL");
39 MODULE_LICENSE("GPL");
41 #define PALINFO_VERSION "0.5"
43 typedef int (*palinfo_func_t)(char*);
46 const char *name; /* name of the proc entry */
47 palinfo_func_t proc_read; /* function to call for reading */
48 struct proc_dir_entry *entry; /* registered entry (removal) */
53 * A bunch of string array to get pretty printing
56 static char *cache_types[] = {
60 "Data/Instruction" /* unified */
63 static const char *cache_mattrib[]={
70 static const char *cache_st_hints[]={
74 "Non-temporal, all levels",
81 static const char *cache_ld_hints[]={
83 "Non-temporal, level 1",
85 "Non-temporal, all levels",
92 static const char *rse_hints[]={
96 "eager loads and stores"
99 #define RSE_HINTS_COUNT ARRAY_SIZE(rse_hints)
101 static const char *mem_attrib[]={
113 * Take a 64bit vector and produces a string such that
114 * if bit n is set then 2^n in clear text is generated. The adjustment
115 * to the right unit is also done.
118 * - a pointer to a buffer to hold the string
121 * - a pointer to the end of the buffer
125 bitvector_process(char *p, u64 vector)
128 const char *units[]={ "", "K", "M", "G", "T" };
130 for (i=0, j=0; i < 64; i++ , j=i/10) {
132 p += sprintf(p, "%d%s ", 1 << (i-j*10), units[j]);
140 * Take a 64bit vector and produces a string such that
141 * if bit n is set then register n is present. The function
142 * takes into account consecutive registers and prints out ranges.
145 * - a pointer to a buffer to hold the string
148 * - a pointer to the end of the buffer
152 bitregister_process(char *p, u64 *reg_info, int max)
154 int i, begin, skip = 0;
155 u64 value = reg_info[0];
157 value >>= i = begin = ffs(value) - 1;
159 for(; i < max; i++ ) {
161 if (i != 0 && (i%64) == 0) value = *++reg_info;
163 if ((value & 0x1) == 0 && skip == 0) {
165 p += sprintf(p, "%d-%d ", begin, i-1);
167 p += sprintf(p, "%d ", i-1);
170 } else if ((value & 0x1) && skip == 1) {
178 p += sprintf(p, "%d-127", begin);
180 p += sprintf(p, "127");
187 power_info(char *page)
191 u64 halt_info_buffer[8];
192 pal_power_mgmt_info_u_t *halt_info =(pal_power_mgmt_info_u_t *)halt_info_buffer;
195 status = ia64_pal_halt_info(halt_info);
196 if (status != 0) return 0;
198 for (i=0; i < 8 ; i++ ) {
199 if (halt_info[i].pal_power_mgmt_info_s.im == 1) {
200 p += sprintf(p, "Power level %d:\n"
201 "\tentry_latency : %d cycles\n"
202 "\texit_latency : %d cycles\n"
203 "\tpower consumption : %d mW\n"
204 "\tCache+TLB coherency : %s\n", i,
205 halt_info[i].pal_power_mgmt_info_s.entry_latency,
206 halt_info[i].pal_power_mgmt_info_s.exit_latency,
207 halt_info[i].pal_power_mgmt_info_s.power_consumption,
208 halt_info[i].pal_power_mgmt_info_s.co ? "Yes" : "No");
210 p += sprintf(p,"Power level %d: not implemented\n",i);
217 cache_info(char *page)
220 u64 i, levels, unique_caches;
221 pal_cache_config_info_t cci;
225 if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) {
226 printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status);
230 p += sprintf(p, "Cache levels : %ld\nUnique caches : %ld\n\n", levels, unique_caches);
232 for (i=0; i < levels; i++) {
234 for (j=2; j >0 ; j--) {
236 /* even without unification some level may not be present */
237 if ((status=ia64_pal_cache_config_info(i,j, &cci)) != 0) {
241 "%s Cache level %lu:\n"
242 "\tSize : %u bytes\n"
244 cache_types[j+cci.pcci_unified], i+1,
245 cci.pcci_cache_size);
247 if (cci.pcci_unified) p += sprintf(p, "Unified ");
249 p += sprintf(p, "%s\n", cache_mattrib[cci.pcci_cache_attr]);
252 "\tAssociativity : %d\n"
253 "\tLine size : %d bytes\n"
254 "\tStride : %d bytes\n",
255 cci.pcci_assoc, 1<<cci.pcci_line_size, 1<<cci.pcci_stride);
257 p += sprintf(p, "\tStore latency : N/A\n");
259 p += sprintf(p, "\tStore latency : %d cycle(s)\n",
260 cci.pcci_st_latency);
263 "\tLoad latency : %d cycle(s)\n"
264 "\tStore hints : ", cci.pcci_ld_latency);
266 for(k=0; k < 8; k++ ) {
267 if ( cci.pcci_st_hints & 0x1)
268 p += sprintf(p, "[%s]", cache_st_hints[k]);
269 cci.pcci_st_hints >>=1;
271 p += sprintf(p, "\n\tLoad hints : ");
273 for(k=0; k < 8; k++ ) {
274 if (cci.pcci_ld_hints & 0x1)
275 p += sprintf(p, "[%s]", cache_ld_hints[k]);
276 cci.pcci_ld_hints >>=1;
279 "\n\tAlias boundary : %d byte(s)\n"
282 1<<cci.pcci_alias_boundary, cci.pcci_tag_lsb,
285 /* when unified, data(j=2) is enough */
286 if (cci.pcci_unified) break;
297 u64 tr_pages =0, vw_pages=0, tc_pages;
299 pal_vm_info_1_u_t vm_info_1;
300 pal_vm_info_2_u_t vm_info_2;
301 pal_tc_info_u_t tc_info;
302 ia64_ptce_info_t ptce;
307 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
308 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
312 "Physical Address Space : %d bits\n"
313 "Virtual Address Space : %d bits\n"
314 "Protection Key Registers(PKR) : %d\n"
315 "Implemented bits in PKR.key : %d\n"
316 "Hash Tag ID : 0x%x\n"
317 "Size of RR.rid : %d\n",
318 vm_info_1.pal_vm_info_1_s.phys_add_size,
319 vm_info_2.pal_vm_info_2_s.impl_va_msb+1,
320 vm_info_1.pal_vm_info_1_s.max_pkr+1,
321 vm_info_1.pal_vm_info_1_s.key_size,
322 vm_info_1.pal_vm_info_1_s.hash_tag_id,
323 vm_info_2.pal_vm_info_2_s.rid_size);
326 if (ia64_pal_mem_attrib(&attrib) == 0) {
327 p += sprintf(p, "Supported memory attributes : ");
329 for (i = 0; i < 8; i++) {
330 if (attrib & (1 << i)) {
331 p += sprintf(p, "%s%s", sep, mem_attrib[i]);
335 p += sprintf(p, "\n");
338 if ((status = ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) {
339 printk(KERN_ERR "ia64_pal_vm_page_size=%ld\n", status);
343 "\nTLB walker : %simplemented\n"
344 "Number of DTR : %d\n"
345 "Number of ITR : %d\n"
346 "TLB insertable page sizes : ",
347 vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
348 vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
349 vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
352 p = bitvector_process(p, tr_pages);
354 p += sprintf(p, "\nTLB purgeable page sizes : ");
356 p = bitvector_process(p, vw_pages);
358 if ((status=ia64_get_ptce(&ptce)) != 0) {
359 printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
362 "\nPurge base address : 0x%016lx\n"
363 "Purge outer loop count : %d\n"
364 "Purge inner loop count : %d\n"
365 "Purge outer loop stride : %d\n"
366 "Purge inner loop stride : %d\n",
367 ptce.base, ptce.count[0], ptce.count[1],
368 ptce.stride[0], ptce.stride[1]);
372 "Unique TC(s) : %d\n",
373 vm_info_1.pal_vm_info_1_s.num_tc_levels,
374 vm_info_1.pal_vm_info_1_s.max_unique_tcs);
376 for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
377 for (j=2; j>0 ; j--) {
378 tc_pages = 0; /* just in case */
381 /* even without unification, some levels may not be present */
382 if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
387 "\n%s Translation Cache Level %d:\n"
389 "\tAssociativity : %d\n"
390 "\tNumber of entries : %d\n"
392 cache_types[j+tc_info.tc_unified], i+1,
394 tc_info.tc_associativity,
395 tc_info.tc_num_entries);
398 p += sprintf(p, "PreferredPageSizeOptimized ");
399 if (tc_info.tc_unified)
400 p += sprintf(p, "Unified ");
401 if (tc_info.tc_reduce_tr)
402 p += sprintf(p, "TCReduction");
404 p += sprintf(p, "\n\tSupported page sizes: ");
406 p = bitvector_process(p, tc_pages);
408 /* when unified date (j=2) is enough */
409 if (tc_info.tc_unified)
414 p += sprintf(p, "\n");
421 register_info(char *page)
431 "AR(s) with read side-effects",
433 "CR(s) with read side-effects",
436 for(info=0; info < 4; info++) {
438 if (ia64_pal_register_info(info, ®_info[0], ®_info[1]) != 0) return 0;
440 p += sprintf(p, "%-32s : ", info_type[info]);
442 p = bitregister_process(p, reg_info, 128);
444 p += sprintf(p, "\n");
447 if (ia64_pal_rse_info(&phys_stacked, &hints) == 0) {
450 "RSE stacked physical registers : %ld\n"
451 "RSE load/store hints : %ld (%s)\n",
452 phys_stacked, hints.ph_data,
453 hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)");
455 if (ia64_pal_debug_info(&iregs, &dregs))
459 "Instruction debug register pairs : %ld\n"
460 "Data debug register pairs : %ld\n", iregs, dregs);
465 static const char *proc_features[]={
466 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
467 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
468 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
469 NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
470 NULL,NULL,NULL,NULL,NULL,
471 "XIP,XPSR,XFS implemented",
472 "XR1-XR3 implemented",
473 "Disable dynamic predicate prediction",
474 "Disable processor physical number",
475 "Disable dynamic data cache prefetch",
476 "Disable dynamic inst cache prefetch",
477 "Disable dynamic branch prediction",
478 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
479 "Disable BINIT on processor time-out",
480 "Disable dynamic power management (DPM)",
483 "Enable CMCI promotion",
484 "Enable MCA to BINIT promotion",
485 "Enable MCA promotion",
486 "Enable BERR promotion"
491 processor_info(char *page)
494 const char **v = proc_features;
495 u64 avail=1, status=1, control=1;
499 if ((ret=ia64_pal_proc_get_features(&avail, &status, &control)) != 0) return 0;
501 for(i=0; i < 64; i++, v++,avail >>=1, status >>=1, control >>=1) {
502 if ( ! *v ) continue;
503 p += sprintf(p, "%-40s : %s%s %s\n", *v,
504 avail & 0x1 ? "" : "NotImpl",
505 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
506 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
511 static const char *bus_features[]={
512 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
513 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
514 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
516 "Request Bus Parking",
518 "Enable Half Transfer",
519 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
520 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
521 NULL, NULL, NULL, NULL,
522 "Enable Cache Line Repl. Shared",
523 "Enable Cache Line Repl. Exclusive",
524 "Disable Transaction Queuing",
525 "Disable Response Error Checking",
526 "Disable Bus Error Checking",
527 "Disable Bus Requester Internal Error Signalling",
528 "Disable Bus Requester Error Signalling",
529 "Disable Bus Initialization Event Checking",
530 "Disable Bus Initialization Event Signalling",
531 "Disable Bus Address Error Checking",
532 "Disable Bus Address Error Signalling",
533 "Disable Bus Data Error Checking"
541 const char **v = bus_features;
542 pal_bus_features_u_t av, st, ct;
543 u64 avail, status, control;
547 if ((ret=ia64_pal_bus_get_features(&av, &st, &ct)) != 0) return 0;
549 avail = av.pal_bus_features_val;
550 status = st.pal_bus_features_val;
551 control = ct.pal_bus_features_val;
553 for(i=0; i < 64; i++, v++, avail >>=1, status >>=1, control >>=1) {
554 if ( ! *v ) continue;
555 p += sprintf(p, "%-48s : %s%s %s\n", *v,
556 avail & 0x1 ? "" : "NotImpl",
557 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
558 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
564 version_info(char *page)
566 pal_version_u_t min_ver, cur_ver;
569 /* The PAL_VERSION call is advertised as being able to support
570 * both physical and virtual mode calls. This seems to be a documentation
571 * bug rather than firmware bug. In fact, it does only support physical mode.
572 * So now the code reflects this fact and the pal_version() has been updated
575 if (ia64_pal_version(&min_ver, &cur_ver) != 0) return 0;
578 "PAL_vendor : 0x%02x (min=0x%02x)\n"
579 "PAL_A : %x.%x.%x (min=%x.%x.%x)\n"
580 "PAL_B : %x.%x.%x (min=%x.%x.%x)\n",
581 cur_ver.pal_version_s.pv_pal_vendor, min_ver.pal_version_s.pv_pal_vendor,
583 cur_ver.pal_version_s.pv_pal_a_model>>4,
584 cur_ver.pal_version_s.pv_pal_a_model&0xf, cur_ver.pal_version_s.pv_pal_a_rev,
585 min_ver.pal_version_s.pv_pal_a_model>>4,
586 min_ver.pal_version_s.pv_pal_a_model&0xf, min_ver.pal_version_s.pv_pal_a_rev,
588 cur_ver.pal_version_s.pv_pal_b_model>>4,
589 cur_ver.pal_version_s.pv_pal_b_model&0xf, cur_ver.pal_version_s.pv_pal_b_rev,
590 min_ver.pal_version_s.pv_pal_b_model>>4,
591 min_ver.pal_version_s.pv_pal_b_model&0xf, min_ver.pal_version_s.pv_pal_b_rev);
596 perfmon_info(char *page)
600 pal_perf_mon_info_u_t pm_info;
602 if (ia64_pal_perf_mon_info(pm_buffer, &pm_info) != 0) return 0;
605 "PMC/PMD pairs : %d\n"
606 "Counter width : %d bits\n"
607 "Cycle event number : %d\n"
608 "Retired event number : %d\n"
609 "Implemented PMC : ",
610 pm_info.pal_perf_mon_info_s.generic, pm_info.pal_perf_mon_info_s.width,
611 pm_info.pal_perf_mon_info_s.cycles, pm_info.pal_perf_mon_info_s.retired);
613 p = bitregister_process(p, pm_buffer, 256);
614 p += sprintf(p, "\nImplemented PMD : ");
615 p = bitregister_process(p, pm_buffer+4, 256);
616 p += sprintf(p, "\nCycles count capable : ");
617 p = bitregister_process(p, pm_buffer+8, 256);
618 p += sprintf(p, "\nRetired bundles count capable : ");
620 #ifdef CONFIG_ITANIUM
622 * PAL_PERF_MON_INFO reports that only PMC4 can be used to count CPU_CYCLES
623 * which is wrong, both PMC4 and PMD5 support it.
625 if (pm_buffer[12] == 0x10) pm_buffer[12]=0x30;
628 p = bitregister_process(p, pm_buffer+12, 256);
630 p += sprintf(p, "\n");
636 frequency_info(char *page)
639 struct pal_freq_ratio proc, itc, bus;
642 if (ia64_pal_freq_base(&base) == -1)
643 p += sprintf(p, "Output clock : not implemented\n");
645 p += sprintf(p, "Output clock : %ld ticks/s\n", base);
647 if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
650 "Processor/Clock ratio : %d/%d\n"
651 "Bus/Clock ratio : %d/%d\n"
652 "ITC/Clock ratio : %d/%d\n",
653 proc.num, proc.den, bus.num, bus.den, itc.num, itc.den);
663 pal_tr_valid_u_t tr_valid;
665 pal_vm_info_1_u_t vm_info_1;
666 pal_vm_info_2_u_t vm_info_2;
701 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
702 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
705 max[0] = vm_info_1.pal_vm_info_1_s.max_itr_entry+1;
706 max[1] = vm_info_1.pal_vm_info_1_s.max_dtr_entry+1;
708 for (i=0; i < 2; i++ ) {
709 for (j=0; j < max[i]; j++) {
711 status = ia64_pal_tr_read(j, i, tr_buffer, &tr_valid);
713 printk(KERN_ERR "palinfo: pal call failed on tr[%lu:%lu]=%ld\n",
718 ifa_reg = (struct ifa_reg *)&tr_buffer[2];
720 if (ifa_reg->valid == 0) continue;
722 gr_reg = (struct gr_reg *)tr_buffer;
723 itir_reg = (struct itir_reg *)&tr_buffer[1];
724 rid_reg = (struct rid_reg *)&tr_buffer[3];
726 pgm = -1 << (itir_reg->ps - 12);
728 "%cTR%lu: av=%d pv=%d dv=%d mv=%d\n"
733 tr_valid.pal_tr_valid_s.access_rights_valid,
734 tr_valid.pal_tr_valid_s.priv_level_valid,
735 tr_valid.pal_tr_valid_s.dirty_bit_valid,
736 tr_valid.pal_tr_valid_s.mem_attr_valid,
737 (gr_reg->ppn & pgm)<< 12, (ifa_reg->vpn & pgm)<< 12);
739 p = bitvector_process(p, 1<< itir_reg->ps);
748 gr_reg->pl, gr_reg->ar, rid_reg->rid, gr_reg->p, gr_reg->ma,
758 * List {name,function} pairs for every entry in /proc/palinfo/cpu*
760 static palinfo_entry_t palinfo_entries[]={
761 { "version_info", version_info, },
762 { "vm_info", vm_info, },
763 { "cache_info", cache_info, },
764 { "power_info", power_info, },
765 { "register_info", register_info, },
766 { "processor_info", processor_info, },
767 { "perfmon_info", perfmon_info, },
768 { "frequency_info", frequency_info, },
769 { "bus_info", bus_info },
770 { "tr_info", tr_info, }
773 #define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
776 * this array is used to keep track of the proc entries we create. This is
777 * required in the module mode when we need to remove all entries. The procfs code
778 * does not do recursion of deletion
781 * - +1 accounts for the cpuN directory entry in /proc/pal
783 #define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1))
785 static struct proc_dir_entry *palinfo_proc_entries[NR_PALINFO_PROC_ENTRIES];
786 static struct proc_dir_entry *palinfo_dir;
789 * This data structure is used to pass which cpu,function is being requested
790 * It must fit in a 64bit quantity to be passed to the proc callback routine
792 * In SMP mode, when we get a request for another CPU, we must call that
793 * other CPU using IPI and wait for the result before returning.
798 unsigned req_cpu: 32; /* for which CPU this info is */
799 unsigned func_id: 32; /* which function is requested */
803 #define req_cpu pal_func_cpu.req_cpu
804 #define func_id pal_func_cpu.func_id
809 * used to hold information about final function to call
812 palinfo_func_t func; /* pointer to function to call */
813 char *page; /* buffer to store results */
814 int ret; /* return value from call */
815 } palinfo_smp_data_t;
819 * this function does the actual final call and he called
820 * from the smp code, i.e., this is the palinfo callback routine
823 palinfo_smp_call(void *info)
825 palinfo_smp_data_t *data = (palinfo_smp_data_t *)info;
827 printk(KERN_ERR "palinfo: data pointer is NULL\n");
828 data->ret = 0; /* no output */
831 /* does this actual call */
832 data->ret = (*data->func)(data->page);
836 * function called to trigger the IPI, we need to access a remote CPU
838 * 0 : error or nothing to output
839 * otherwise how many bytes in the "page" buffer were written
842 int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page)
844 palinfo_smp_data_t ptr;
847 ptr.func = palinfo_entries[f->func_id].proc_read;
849 ptr.ret = 0; /* just in case */
852 /* will send IPI to other CPU and wait for completion of remote call */
853 if ((ret=smp_call_function_single(f->req_cpu, palinfo_smp_call, &ptr, 0, 1))) {
854 printk(KERN_ERR "palinfo: remote CPU call from %d to %d on function %d: "
855 "error %d\n", smp_processor_id(), f->req_cpu, f->func_id, ret);
860 #else /* ! CONFIG_SMP */
862 int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page)
864 printk(KERN_ERR "palinfo: should not be called with non SMP kernel\n");
867 #endif /* CONFIG_SMP */
870 * Entry point routine: all calls go through this function
873 palinfo_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data)
876 pal_func_cpu_u_t *f = (pal_func_cpu_u_t *)&data;
879 * in SMP mode, we may need to call another CPU to get correct
880 * information. PAL, by definition, is processor specific
882 if (f->req_cpu == get_cpu())
883 len = (*palinfo_entries[f->func_id].proc_read)(page);
885 len = palinfo_handle_smp(f, page);
889 if (len <= off+count) *eof = 1;
894 if (len>count) len = count;
901 create_palinfo_proc_entries(unsigned int cpu)
903 # define CPUSTR "cpu%d"
906 struct proc_dir_entry **pdir;
907 struct proc_dir_entry *cpu_dir;
909 char cpustr[sizeof(CPUSTR)];
913 * we keep track of created entries in a depth-first order for
914 * cleanup purposes. Each entry is stored into palinfo_proc_entries
916 sprintf(cpustr,CPUSTR, cpu);
918 cpu_dir = proc_mkdir(cpustr, palinfo_dir);
923 * Compute the location to store per cpu entries
924 * We dont store the top level entry in this list, but
925 * remove it finally after removing all cpu entries.
927 pdir = &palinfo_proc_entries[cpu*(NR_PALINFO_ENTRIES+1)];
929 for (j=0; j < NR_PALINFO_ENTRIES; j++) {
931 *pdir = create_proc_read_entry(
932 palinfo_entries[j].name, 0, cpu_dir,
933 palinfo_read_entry, (void *)f.value);
935 (*pdir)->owner = THIS_MODULE;
941 remove_palinfo_proc_entries(unsigned int hcpu)
944 struct proc_dir_entry *cpu_dir, **pdir;
946 pdir = &palinfo_proc_entries[hcpu*(NR_PALINFO_ENTRIES+1)];
949 for (j=0; j < (NR_PALINFO_ENTRIES); j++) {
951 remove_proc_entry ((*pdir)->name, cpu_dir);
957 remove_proc_entry(cpu_dir->name, palinfo_dir);
961 static int __cpuinit palinfo_cpu_callback(struct notifier_block *nfb,
962 unsigned long action,
965 unsigned int hotcpu = (unsigned long)hcpu;
969 create_palinfo_proc_entries(hotcpu);
971 #ifdef CONFIG_HOTPLUG_CPU
973 remove_palinfo_proc_entries(hotcpu);
980 static struct notifier_block __cpuinitdata palinfo_cpu_notifier =
982 .notifier_call = palinfo_cpu_callback,
991 printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
992 palinfo_dir = proc_mkdir("pal", NULL);
994 /* Create palinfo dirs in /proc for all online cpus */
995 for_each_online_cpu(i) {
996 create_palinfo_proc_entries(i);
999 /* Register for future delivery via notify registration */
1000 register_hotcpu_notifier(&palinfo_cpu_notifier);
1010 /* remove all nodes: depth first pass. Could optimize this */
1011 for_each_online_cpu(i) {
1012 remove_palinfo_proc_entries(i);
1016 * Remove the top level entry finally
1018 remove_proc_entry(palinfo_dir->name, NULL);
1021 * Unregister from cpu notifier callbacks
1023 unregister_cpu_notifier(&palinfo_cpu_notifier);
1026 module_init(palinfo_init);
1027 module_exit(palinfo_exit);