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>
12 * 05/26/2000 S.Eranian initial release
13 * 08/21/2000 S.Eranian updated to July 2000 PAL specs
14 * 02/05/2001 S.Eranian fixed module support
15 * 10/23/2001 S.Eranian updated pal_perf_mon_info bug fixes
17 #include <linux/config.h>
18 #include <linux/types.h>
19 #include <linux/errno.h>
20 #include <linux/init.h>
21 #include <linux/proc_fs.h>
23 #include <linux/module.h>
24 #include <linux/efi.h>
29 #include <asm/processor.h>
30 #include <linux/smp.h>
32 MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
33 MODULE_DESCRIPTION("/proc interface to IA-64 PAL");
34 MODULE_LICENSE("GPL");
36 #define PALINFO_VERSION "0.5"
38 typedef int (*palinfo_func_t)(char*);
41 const char *name; /* name of the proc entry */
42 palinfo_func_t proc_read; /* function to call for reading */
43 struct proc_dir_entry *entry; /* registered entry (removal) */
48 * A bunch of string array to get pretty printing
51 static char *cache_types[] = {
55 "Data/Instruction" /* unified */
58 static const char *cache_mattrib[]={
65 static const char *cache_st_hints[]={
69 "Non-temporal, all levels",
76 static const char *cache_ld_hints[]={
78 "Non-temporal, level 1",
80 "Non-temporal, all levels",
87 static const char *rse_hints[]={
91 "eager loads and stores"
94 #define RSE_HINTS_COUNT (sizeof(rse_hints)/sizeof(const char *))
96 static const char *mem_attrib[]={
108 * Take a 64bit vector and produces a string such that
109 * if bit n is set then 2^n in clear text is generated. The adjustment
110 * to the right unit is also done.
113 * - a pointer to a buffer to hold the string
116 * - a pointer to the end of the buffer
120 bitvector_process(char *p, u64 vector)
123 const char *units[]={ "", "K", "M", "G", "T" };
125 for (i=0, j=0; i < 64; i++ , j=i/10) {
127 p += sprintf(p, "%d%s ", 1 << (i-j*10), units[j]);
135 * Take a 64bit vector and produces a string such that
136 * if bit n is set then register n is present. The function
137 * takes into account consecutive registers and prints out ranges.
140 * - a pointer to a buffer to hold the string
143 * - a pointer to the end of the buffer
147 bitregister_process(char *p, u64 *reg_info, int max)
149 int i, begin, skip = 0;
150 u64 value = reg_info[0];
152 value >>= i = begin = ffs(value) - 1;
154 for(; i < max; i++ ) {
156 if (i != 0 && (i%64) == 0) value = *++reg_info;
158 if ((value & 0x1) == 0 && skip == 0) {
160 p += sprintf(p, "%d-%d ", begin, i-1);
162 p += sprintf(p, "%d ", i-1);
165 } else if ((value & 0x1) && skip == 1) {
173 p += sprintf(p, "%d-127", begin);
175 p += sprintf(p, "127");
182 power_info(char *page)
186 u64 halt_info_buffer[8];
187 pal_power_mgmt_info_u_t *halt_info =(pal_power_mgmt_info_u_t *)halt_info_buffer;
190 status = ia64_pal_halt_info(halt_info);
191 if (status != 0) return 0;
193 for (i=0; i < 8 ; i++ ) {
194 if (halt_info[i].pal_power_mgmt_info_s.im == 1) {
195 p += sprintf(p, "Power level %d:\n" \
196 "\tentry_latency : %d cycles\n" \
197 "\texit_latency : %d cycles\n" \
198 "\tpower consumption : %d mW\n" \
199 "\tCache+TLB coherency : %s\n", i,
200 halt_info[i].pal_power_mgmt_info_s.entry_latency,
201 halt_info[i].pal_power_mgmt_info_s.exit_latency,
202 halt_info[i].pal_power_mgmt_info_s.power_consumption,
203 halt_info[i].pal_power_mgmt_info_s.co ? "Yes" : "No");
205 p += sprintf(p,"Power level %d: not implemented\n",i);
212 cache_info(char *page)
215 u64 levels, unique_caches;
216 pal_cache_config_info_t cci;
220 if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) {
221 printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status);
225 p += sprintf(p, "Cache levels : %ld\nUnique caches : %ld\n\n", levels, unique_caches);
227 for (i=0; i < levels; i++) {
229 for (j=2; j >0 ; j--) {
231 /* even without unification some level may not be present */
232 if ((status=ia64_pal_cache_config_info(i,j, &cci)) != 0) {
235 p += sprintf(p, "%s Cache level %d:\n" \
236 "\tSize : %ld bytes\n" \
238 cache_types[j+cci.pcci_unified], i+1,
239 cci.pcci_cache_size);
241 if (cci.pcci_unified) p += sprintf(p, "Unified ");
243 p += sprintf(p, "%s\n", cache_mattrib[cci.pcci_cache_attr]);
245 p += sprintf(p, "\tAssociativity : %d\n" \
246 "\tLine size : %d bytes\n" \
247 "\tStride : %d bytes\n",
249 1<<cci.pcci_line_size,
252 p += sprintf(p, "\tStore latency : N/A\n");
254 p += sprintf(p, "\tStore latency : %d cycle(s)\n",
255 cci.pcci_st_latency);
257 p += sprintf(p, "\tLoad latency : %d cycle(s)\n" \
259 cci.pcci_ld_latency);
261 for(k=0; k < 8; k++ ) {
262 if ( cci.pcci_st_hints & 0x1) p += sprintf(p, "[%s]", cache_st_hints[k]);
263 cci.pcci_st_hints >>=1;
265 p += sprintf(p, "\n\tLoad hints : ");
267 for(k=0; k < 8; k++ ) {
268 if ( cci.pcci_ld_hints & 0x1) p += sprintf(p, "[%s]", cache_ld_hints[k]);
269 cci.pcci_ld_hints >>=1;
271 p += sprintf(p, "\n\tAlias boundary : %d byte(s)\n" \
274 1<<cci.pcci_alias_boundary,
278 /* when unified, data(j=2) is enough */
279 if (cci.pcci_unified) break;
290 u64 tr_pages =0, vw_pages=0, tc_pages;
292 pal_vm_info_1_u_t vm_info_1;
293 pal_vm_info_2_u_t vm_info_2;
294 pal_tc_info_u_t tc_info;
295 ia64_ptce_info_t ptce;
300 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
301 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
306 p += sprintf(p, "Physical Address Space : %d bits\n" \
307 "Virtual Address Space : %d bits\n" \
308 "Protection Key Registers(PKR) : %d\n" \
309 "Implemented bits in PKR.key : %d\n" \
310 "Hash Tag ID : 0x%x\n" \
311 "Size of RR.rid : %d\n",
312 vm_info_1.pal_vm_info_1_s.phys_add_size,
313 vm_info_2.pal_vm_info_2_s.impl_va_msb+1,
314 vm_info_1.pal_vm_info_1_s.max_pkr+1,
315 vm_info_1.pal_vm_info_1_s.key_size,
316 vm_info_1.pal_vm_info_1_s.hash_tag_id,
317 vm_info_2.pal_vm_info_2_s.rid_size);
319 if (ia64_pal_mem_attrib(&attrib) != 0) return 0;
321 p += sprintf(p, "Supported memory attributes : ");
323 for (i = 0; i < 8; i++) {
324 if (attrib & (1 << i)) {
325 p += sprintf(p, "%s%s", sep, mem_attrib[i]);
329 p += sprintf(p, "\n");
331 if ((status = ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) {
332 printk(KERN_ERR "ia64_pal_vm_page_size=%ld\n", status);
336 p += sprintf(p, "\nTLB walker : %simplemented\n" \
337 "Number of DTR : %d\n" \
338 "Number of ITR : %d\n" \
339 "TLB insertable page sizes : ",
340 vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
341 vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
342 vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
345 p = bitvector_process(p, tr_pages);
347 p += sprintf(p, "\nTLB purgeable page sizes : ");
349 p = bitvector_process(p, vw_pages);
351 if ((status=ia64_get_ptce(&ptce)) != 0) {
352 printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
356 p += sprintf(p, "\nPurge base address : 0x%016lx\n" \
357 "Purge outer loop count : %d\n" \
358 "Purge inner loop count : %d\n" \
359 "Purge outer loop stride : %d\n" \
360 "Purge inner loop stride : %d\n",
367 p += sprintf(p, "TC Levels : %d\n" \
368 "Unique TC(s) : %d\n",
369 vm_info_1.pal_vm_info_1_s.num_tc_levels,
370 vm_info_1.pal_vm_info_1_s.max_unique_tcs);
372 for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
373 for (j=2; j>0 ; j--) {
374 tc_pages = 0; /* just in case */
377 /* even without unification, some levels may not be present */
378 if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
382 p += sprintf(p, "\n%s Translation Cache Level %d:\n" \
383 "\tHash sets : %d\n" \
384 "\tAssociativity : %d\n" \
385 "\tNumber of entries : %d\n" \
387 cache_types[j+tc_info.tc_unified], i+1,
389 tc_info.tc_associativity,
390 tc_info.tc_num_entries);
392 if (tc_info.tc_pf) p += sprintf(p, "PreferredPageSizeOptimized ");
393 if (tc_info.tc_unified) p += sprintf(p, "Unified ");
394 if (tc_info.tc_reduce_tr) p += sprintf(p, "TCReduction");
396 p += sprintf(p, "\n\tSupported page sizes: ");
398 p = bitvector_process(p, tc_pages);
400 /* when unified date (j=2) is enough */
401 if (tc_info.tc_unified) break;
404 p += sprintf(p, "\n");
411 register_info(char *page)
421 "AR(s) with read side-effects",
423 "CR(s) with read side-effects",
426 for(info=0; info < 4; info++) {
428 if (ia64_pal_register_info(info, ®_info[0], ®_info[1]) != 0) return 0;
430 p += sprintf(p, "%-32s : ", info_type[info]);
432 p = bitregister_process(p, reg_info, 128);
434 p += sprintf(p, "\n");
437 if (ia64_pal_rse_info(&phys_stacked, &hints) != 0) return 0;
439 p += sprintf(p, "RSE stacked physical registers : %ld\n" \
440 "RSE load/store hints : %ld (%s)\n",
443 hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(\?\?)");
445 if (ia64_pal_debug_info(&iregs, &dregs)) return 0;
447 p += sprintf(p, "Instruction debug register pairs : %ld\n" \
448 "Data debug register pairs : %ld\n",
454 static const char *proc_features[]={
455 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
456 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
457 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
458 NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
459 NULL,NULL,NULL,NULL,NULL,
460 "XIP,XPSR,XFS implemented",
461 "XR1-XR3 implemented",
462 "Disable dynamic predicate prediction",
463 "Disable processor physical number",
464 "Disable dynamic data cache prefetch",
465 "Disable dynamic inst cache prefetch",
466 "Disable dynamic branch prediction",
467 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
468 "Disable BINIT on processor time-out",
469 "Disable dynamic power management (DPM)",
472 "Enable CMCI promotion",
473 "Enable MCA to BINIT promotion",
474 "Enable MCA promotion",
475 "Enable BEER promotion"
480 processor_info(char *page)
483 const char **v = proc_features;
484 u64 avail=1, status=1, control=1;
488 if ((ret=ia64_pal_proc_get_features(&avail, &status, &control)) != 0) return 0;
490 for(i=0; i < 64; i++, v++,avail >>=1, status >>=1, control >>=1) {
491 if ( ! *v ) continue;
492 p += sprintf(p, "%-40s : %s%s %s\n", *v,
493 avail & 0x1 ? "" : "NotImpl",
494 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
495 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
500 static const char *bus_features[]={
501 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
502 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
503 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
505 "Request Bus Parking",
507 "Enable Half Transfer",
508 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
509 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
510 NULL, NULL, NULL, NULL,
511 "Enable Cache Line Repl. Exclusive",
512 "Enable Cache Line Repl. Shared",
513 "Disable Transaction Queuing",
514 "Disable Reponse Error Checking",
515 "Disable Bus Error Checking",
516 "Disable Bus Requester Internal Error Signalling",
517 "Disable Bus Requester Error Signalling",
518 "Disable Bus Initialization Event Checking",
519 "Disable Bus Initialization Event Signalling",
520 "Disable Bus Address Error Checking",
521 "Disable Bus Address Error Signalling",
522 "Disable Bus Data Error Checking"
530 const char **v = bus_features;
531 pal_bus_features_u_t av, st, ct;
532 u64 avail, status, control;
536 if ((ret=ia64_pal_bus_get_features(&av, &st, &ct)) != 0) return 0;
538 avail = av.pal_bus_features_val;
539 status = st.pal_bus_features_val;
540 control = ct.pal_bus_features_val;
542 for(i=0; i < 64; i++, v++, avail >>=1, status >>=1, control >>=1) {
543 if ( ! *v ) continue;
544 p += sprintf(p, "%-48s : %s%s %s\n", *v,
545 avail & 0x1 ? "" : "NotImpl",
546 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
547 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
553 version_info(char *page)
555 pal_version_u_t min_ver, cur_ver;
558 /* The PAL_VERSION call is advertised as being able to support
559 * both physical and virtual mode calls. This seems to be a documentation
560 * bug rather than firmware bug. In fact, it does only support physical mode.
561 * So now the code reflects this fact and the pal_version() has been updated
564 if (ia64_pal_version(&min_ver, &cur_ver) != 0) return 0;
566 p += sprintf(p, "PAL_vendor : 0x%02x (min=0x%02x)\n" \
567 "PAL_A : %x.%x.%x (min=%x.%x.%x)\n" \
568 "PAL_B : %x.%x.%x (min=%x.%x.%x)\n",
569 cur_ver.pal_version_s.pv_pal_vendor,
570 min_ver.pal_version_s.pv_pal_vendor,
572 cur_ver.pal_version_s.pv_pal_a_model>>4,
573 cur_ver.pal_version_s.pv_pal_a_model&0xf,
574 cur_ver.pal_version_s.pv_pal_a_rev,
575 min_ver.pal_version_s.pv_pal_a_model>>4,
576 min_ver.pal_version_s.pv_pal_a_model&0xf,
577 min_ver.pal_version_s.pv_pal_a_rev,
579 cur_ver.pal_version_s.pv_pal_b_model>>4,
580 cur_ver.pal_version_s.pv_pal_b_model&0xf,
581 cur_ver.pal_version_s.pv_pal_b_rev,
582 min_ver.pal_version_s.pv_pal_b_model>>4,
583 min_ver.pal_version_s.pv_pal_b_model&0xf,
584 min_ver.pal_version_s.pv_pal_b_rev);
590 perfmon_info(char *page)
594 pal_perf_mon_info_u_t pm_info;
596 if (ia64_pal_perf_mon_info(pm_buffer, &pm_info) != 0) return 0;
598 p += sprintf(p, "PMC/PMD pairs : %d\n" \
599 "Counter width : %d bits\n" \
600 "Cycle event number : %d\n" \
601 "Retired event number : %d\n" \
602 "Implemented PMC : ",
603 pm_info.pal_perf_mon_info_s.generic,
604 pm_info.pal_perf_mon_info_s.width,
605 pm_info.pal_perf_mon_info_s.cycles,
606 pm_info.pal_perf_mon_info_s.retired);
608 p = bitregister_process(p, pm_buffer, 256);
610 p += sprintf(p, "\nImplemented PMD : ");
612 p = bitregister_process(p, pm_buffer+4, 256);
614 p += sprintf(p, "\nCycles count capable : ");
616 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;
649 p += sprintf(p, "Processor/Clock ratio : %ld/%ld\n" \
650 "Bus/Clock ratio : %ld/%ld\n" \
651 "ITC/Clock ratio : %ld/%ld\n",
664 pal_tr_valid_u_t tr_valid;
666 pal_vm_info_1_u_t vm_info_1;
667 pal_vm_info_2_u_t vm_info_2;
702 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
703 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
706 max[0] = vm_info_1.pal_vm_info_1_s.max_itr_entry+1;
707 max[1] = vm_info_1.pal_vm_info_1_s.max_dtr_entry+1;
709 for (i=0; i < 2; i++ ) {
710 for (j=0; j < max[i]; j++) {
712 status = ia64_pal_tr_read(j, i, tr_buffer, &tr_valid);
714 printk(KERN_ERR "palinfo: pal call failed on tr[%d:%d]=%ld\n",
719 ifa_reg = (struct ifa_reg *)&tr_buffer[2];
721 if (ifa_reg->valid == 0) continue;
723 gr_reg = (struct gr_reg *)tr_buffer;
724 itir_reg = (struct itir_reg *)&tr_buffer[1];
725 rid_reg = (struct rid_reg *)&tr_buffer[3];
727 pgm = -1 << (itir_reg->ps - 12);
728 p += sprintf(p, "%cTR%d: av=%d pv=%d dv=%d mv=%d\n" \
735 tr_valid.pal_tr_valid_s.access_rights_valid,
736 tr_valid.pal_tr_valid_s.priv_level_valid,
737 tr_valid.pal_tr_valid_s.dirty_bit_valid,
738 tr_valid.pal_tr_valid_s.mem_attr_valid,
739 (gr_reg->ppn & pgm)<< 12,
740 (ifa_reg->vpn & pgm)<< 12);
742 p = bitvector_process(p, 1<< itir_reg->ps);
744 p += sprintf(p, "\n\tpl : %d\n" \
764 * List {name,function} pairs for every entry in /proc/palinfo/cpu*
766 static palinfo_entry_t palinfo_entries[]={
767 { "version_info", version_info, },
768 { "vm_info", vm_info, },
769 { "cache_info", cache_info, },
770 { "power_info", power_info, },
771 { "register_info", register_info, },
772 { "processor_info", processor_info, },
773 { "perfmon_info", perfmon_info, },
774 { "frequency_info", frequency_info, },
775 { "bus_info", bus_info },
776 { "tr_info", tr_info, }
779 #define NR_PALINFO_ENTRIES (sizeof(palinfo_entries)/sizeof(palinfo_entry_t))
782 * this array is used to keep track of the proc entries we create. This is
783 * required in the module mode when we need to remove all entries. The procfs code
784 * does not do recursion of deletion
787 * - first +1 accounts for the cpuN entry
788 * - second +1 account for toplevel palinfo
791 #define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1)+1)
793 static struct proc_dir_entry *palinfo_proc_entries[NR_PALINFO_PROC_ENTRIES];
796 * This data structure is used to pass which cpu,function is being requested
797 * It must fit in a 64bit quantity to be passed to the proc callback routine
799 * In SMP mode, when we get a request for another CPU, we must call that
800 * other CPU using IPI and wait for the result before returning.
805 unsigned req_cpu: 32; /* for which CPU this info is */
806 unsigned func_id: 32; /* which function is requested */
810 #define req_cpu pal_func_cpu.req_cpu
811 #define func_id pal_func_cpu.func_id
816 * used to hold information about final function to call
819 palinfo_func_t func; /* pointer to function to call */
820 char *page; /* buffer to store results */
821 int ret; /* return value from call */
822 } palinfo_smp_data_t;
826 * this function does the actual final call and he called
827 * from the smp code, i.e., this is the palinfo callback routine
830 palinfo_smp_call(void *info)
832 palinfo_smp_data_t *data = (palinfo_smp_data_t *)info;
834 printk(KERN_ERR "palinfo: data pointer is NULL\n");
835 data->ret = 0; /* no output */
838 /* does this actual call */
839 data->ret = (*data->func)(data->page);
843 * function called to trigger the IPI, we need to access a remote CPU
845 * 0 : error or nothing to output
846 * otherwise how many bytes in the "page" buffer were written
849 int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page)
851 palinfo_smp_data_t ptr;
854 ptr.func = palinfo_entries[f->func_id].proc_read;
856 ptr.ret = 0; /* just in case */
859 /* will send IPI to other CPU and wait for completion of remote call */
860 if ((ret=smp_call_function_single(f->req_cpu, palinfo_smp_call, &ptr, 0, 1))) {
861 printk(KERN_ERR "palinfo: remote CPU call from %d to %d on function %d: "
862 "error %d\n", smp_processor_id(), f->req_cpu, f->func_id, ret);
867 #else /* ! CONFIG_SMP */
869 int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page)
871 printk(KERN_ERR "palinfo: should not be called with non SMP kernel\n");
874 #endif /* CONFIG_SMP */
877 * Entry point routine: all calls go through this function
880 palinfo_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data)
883 pal_func_cpu_u_t *f = (pal_func_cpu_u_t *)&data;
886 * in SMP mode, we may need to call another CPU to get correct
887 * information. PAL, by definition, is processor specific
889 if (f->req_cpu == get_cpu())
890 len = (*palinfo_entries[f->func_id].proc_read)(page);
892 len = palinfo_handle_smp(f, page);
896 if (len <= off+count) *eof = 1;
901 if (len>count) len = count;
910 # define CPUSTR "cpu%d"
913 struct proc_dir_entry **pdir = palinfo_proc_entries;
914 struct proc_dir_entry *palinfo_dir, *cpu_dir;
916 char cpustr[sizeof(CPUSTR)];
918 printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
920 palinfo_dir = proc_mkdir("pal", NULL);
923 * we keep track of created entries in a depth-first order for
924 * cleanup purposes. Each entry is stored into palinfo_proc_entries
926 for (i=0; i < NR_CPUS; i++) {
928 if (!cpu_online(i)) continue;
930 sprintf(cpustr,CPUSTR, i);
932 cpu_dir = proc_mkdir(cpustr, palinfo_dir);
936 for (j=0; j < NR_PALINFO_ENTRIES; j++) {
938 *pdir = create_proc_read_entry(
939 palinfo_entries[j].name, 0, cpu_dir,
940 palinfo_read_entry, (void *)f.value);
942 (*pdir)->owner = THIS_MODULE;
957 /* remove all nodes: depth first pass. Could optimize this */
958 for (i=0; i< NR_PALINFO_PROC_ENTRIES ; i++) {
959 if (palinfo_proc_entries[i])
960 remove_proc_entry (palinfo_proc_entries[i]->name, NULL);
964 module_init(palinfo_init);
965 module_exit(palinfo_exit);