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/config.h>
21 #include <linux/types.h>
22 #include <linux/errno.h>
23 #include <linux/init.h>
24 #include <linux/proc_fs.h>
26 #include <linux/module.h>
27 #include <linux/efi.h>
28 #include <linux/notifier.h>
29 #include <linux/cpu.h>
30 #include <linux/cpumask.h>
35 #include <asm/processor.h>
36 #include <linux/smp.h>
38 MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
39 MODULE_DESCRIPTION("/proc interface to IA-64 PAL");
40 MODULE_LICENSE("GPL");
42 #define PALINFO_VERSION "0.5"
44 typedef int (*palinfo_func_t
)(char*);
47 const char *name
; /* name of the proc entry */
48 palinfo_func_t proc_read
; /* function to call for reading */
49 struct proc_dir_entry
*entry
; /* registered entry (removal) */
54 * A bunch of string array to get pretty printing
57 static char *cache_types
[] = {
61 "Data/Instruction" /* unified */
64 static const char *cache_mattrib
[]={
71 static const char *cache_st_hints
[]={
75 "Non-temporal, all levels",
82 static const char *cache_ld_hints
[]={
84 "Non-temporal, level 1",
86 "Non-temporal, all levels",
93 static const char *rse_hints
[]={
97 "eager loads and stores"
100 #define RSE_HINTS_COUNT ARRAY_SIZE(rse_hints)
102 static const char *mem_attrib
[]={
114 * Take a 64bit vector and produces a string such that
115 * if bit n is set then 2^n in clear text is generated. The adjustment
116 * to the right unit is also done.
119 * - a pointer to a buffer to hold the string
122 * - a pointer to the end of the buffer
126 bitvector_process(char *p
, u64 vector
)
129 const char *units
[]={ "", "K", "M", "G", "T" };
131 for (i
=0, j
=0; i
< 64; i
++ , j
=i
/10) {
133 p
+= sprintf(p
, "%d%s ", 1 << (i
-j
*10), units
[j
]);
141 * Take a 64bit vector and produces a string such that
142 * if bit n is set then register n is present. The function
143 * takes into account consecutive registers and prints out ranges.
146 * - a pointer to a buffer to hold the string
149 * - a pointer to the end of the buffer
153 bitregister_process(char *p
, u64
*reg_info
, int max
)
155 int i
, begin
, skip
= 0;
156 u64 value
= reg_info
[0];
158 value
>>= i
= begin
= ffs(value
) - 1;
160 for(; i
< max
; i
++ ) {
162 if (i
!= 0 && (i
%64) == 0) value
= *++reg_info
;
164 if ((value
& 0x1) == 0 && skip
== 0) {
166 p
+= sprintf(p
, "%d-%d ", begin
, i
-1);
168 p
+= sprintf(p
, "%d ", i
-1);
171 } else if ((value
& 0x1) && skip
== 1) {
179 p
+= sprintf(p
, "%d-127", begin
);
181 p
+= sprintf(p
, "127");
188 power_info(char *page
)
192 u64 halt_info_buffer
[8];
193 pal_power_mgmt_info_u_t
*halt_info
=(pal_power_mgmt_info_u_t
*)halt_info_buffer
;
196 status
= ia64_pal_halt_info(halt_info
);
197 if (status
!= 0) return 0;
199 for (i
=0; i
< 8 ; i
++ ) {
200 if (halt_info
[i
].pal_power_mgmt_info_s
.im
== 1) {
201 p
+= sprintf(p
, "Power level %d:\n"
202 "\tentry_latency : %d cycles\n"
203 "\texit_latency : %d cycles\n"
204 "\tpower consumption : %d mW\n"
205 "\tCache+TLB coherency : %s\n", i
,
206 halt_info
[i
].pal_power_mgmt_info_s
.entry_latency
,
207 halt_info
[i
].pal_power_mgmt_info_s
.exit_latency
,
208 halt_info
[i
].pal_power_mgmt_info_s
.power_consumption
,
209 halt_info
[i
].pal_power_mgmt_info_s
.co
? "Yes" : "No");
211 p
+= sprintf(p
,"Power level %d: not implemented\n",i
);
218 cache_info(char *page
)
221 u64 i
, levels
, unique_caches
;
222 pal_cache_config_info_t cci
;
226 if ((status
= ia64_pal_cache_summary(&levels
, &unique_caches
)) != 0) {
227 printk(KERN_ERR
"ia64_pal_cache_summary=%ld\n", status
);
231 p
+= sprintf(p
, "Cache levels : %ld\nUnique caches : %ld\n\n", levels
, unique_caches
);
233 for (i
=0; i
< levels
; i
++) {
235 for (j
=2; j
>0 ; j
--) {
237 /* even without unification some level may not be present */
238 if ((status
=ia64_pal_cache_config_info(i
,j
, &cci
)) != 0) {
242 "%s Cache level %lu:\n"
243 "\tSize : %lu bytes\n"
245 cache_types
[j
+cci
.pcci_unified
], i
+1,
246 cci
.pcci_cache_size
);
248 if (cci
.pcci_unified
) p
+= sprintf(p
, "Unified ");
250 p
+= sprintf(p
, "%s\n", cache_mattrib
[cci
.pcci_cache_attr
]);
253 "\tAssociativity : %d\n"
254 "\tLine size : %d bytes\n"
255 "\tStride : %d bytes\n",
256 cci
.pcci_assoc
, 1<<cci
.pcci_line_size
, 1<<cci
.pcci_stride
);
258 p
+= sprintf(p
, "\tStore latency : N/A\n");
260 p
+= sprintf(p
, "\tStore latency : %d cycle(s)\n",
261 cci
.pcci_st_latency
);
264 "\tLoad latency : %d cycle(s)\n"
265 "\tStore hints : ", cci
.pcci_ld_latency
);
267 for(k
=0; k
< 8; k
++ ) {
268 if ( cci
.pcci_st_hints
& 0x1)
269 p
+= sprintf(p
, "[%s]", cache_st_hints
[k
]);
270 cci
.pcci_st_hints
>>=1;
272 p
+= sprintf(p
, "\n\tLoad hints : ");
274 for(k
=0; k
< 8; k
++ ) {
275 if (cci
.pcci_ld_hints
& 0x1)
276 p
+= sprintf(p
, "[%s]", cache_ld_hints
[k
]);
277 cci
.pcci_ld_hints
>>=1;
280 "\n\tAlias boundary : %d byte(s)\n"
283 1<<cci
.pcci_alias_boundary
, cci
.pcci_tag_lsb
,
286 /* when unified, data(j=2) is enough */
287 if (cci
.pcci_unified
) break;
298 u64 tr_pages
=0, vw_pages
=0, tc_pages
;
300 pal_vm_info_1_u_t vm_info_1
;
301 pal_vm_info_2_u_t vm_info_2
;
302 pal_tc_info_u_t tc_info
;
303 ia64_ptce_info_t ptce
;
308 if ((status
= ia64_pal_vm_summary(&vm_info_1
, &vm_info_2
)) !=0) {
309 printk(KERN_ERR
"ia64_pal_vm_summary=%ld\n", status
);
315 "Physical Address Space : %d bits\n"
316 "Virtual Address Space : %d bits\n"
317 "Protection Key Registers(PKR) : %d\n"
318 "Implemented bits in PKR.key : %d\n"
319 "Hash Tag ID : 0x%x\n"
320 "Size of RR.rid : %d\n",
321 vm_info_1
.pal_vm_info_1_s
.phys_add_size
,
322 vm_info_2
.pal_vm_info_2_s
.impl_va_msb
+1, vm_info_1
.pal_vm_info_1_s
.max_pkr
+1,
323 vm_info_1
.pal_vm_info_1_s
.key_size
, vm_info_1
.pal_vm_info_1_s
.hash_tag_id
,
324 vm_info_2
.pal_vm_info_2_s
.rid_size
);
326 if (ia64_pal_mem_attrib(&attrib
) != 0)
329 p
+= sprintf(p
, "Supported memory attributes : ");
331 for (i
= 0; i
< 8; i
++) {
332 if (attrib
& (1 << i
)) {
333 p
+= sprintf(p
, "%s%s", sep
, mem_attrib
[i
]);
337 p
+= sprintf(p
, "\n");
339 if ((status
= ia64_pal_vm_page_size(&tr_pages
, &vw_pages
)) !=0) {
340 printk(KERN_ERR
"ia64_pal_vm_page_size=%ld\n", status
);
345 "\nTLB walker : %simplemented\n"
346 "Number of DTR : %d\n"
347 "Number of ITR : %d\n"
348 "TLB insertable page sizes : ",
349 vm_info_1
.pal_vm_info_1_s
.vw
? "" : "not ",
350 vm_info_1
.pal_vm_info_1_s
.max_dtr_entry
+1,
351 vm_info_1
.pal_vm_info_1_s
.max_itr_entry
+1);
354 p
= bitvector_process(p
, tr_pages
);
356 p
+= sprintf(p
, "\nTLB purgeable page sizes : ");
358 p
= bitvector_process(p
, vw_pages
);
360 if ((status
=ia64_get_ptce(&ptce
)) != 0) {
361 printk(KERN_ERR
"ia64_get_ptce=%ld\n", status
);
366 "\nPurge base address : 0x%016lx\n"
367 "Purge outer loop count : %d\n"
368 "Purge inner loop count : %d\n"
369 "Purge outer loop stride : %d\n"
370 "Purge inner loop stride : %d\n",
371 ptce
.base
, ptce
.count
[0], ptce
.count
[1], ptce
.stride
[0], ptce
.stride
[1]);
375 "Unique TC(s) : %d\n",
376 vm_info_1
.pal_vm_info_1_s
.num_tc_levels
,
377 vm_info_1
.pal_vm_info_1_s
.max_unique_tcs
);
379 for(i
=0; i
< vm_info_1
.pal_vm_info_1_s
.num_tc_levels
; i
++) {
380 for (j
=2; j
>0 ; j
--) {
381 tc_pages
= 0; /* just in case */
384 /* even without unification, some levels may not be present */
385 if ((status
=ia64_pal_vm_info(i
,j
, &tc_info
, &tc_pages
)) != 0) {
390 "\n%s Translation Cache Level %d:\n"
392 "\tAssociativity : %d\n"
393 "\tNumber of entries : %d\n"
395 cache_types
[j
+tc_info
.tc_unified
], i
+1, tc_info
.tc_num_sets
,
396 tc_info
.tc_associativity
, tc_info
.tc_num_entries
);
398 if (tc_info
.tc_pf
) p
+= sprintf(p
, "PreferredPageSizeOptimized ");
399 if (tc_info
.tc_unified
) p
+= sprintf(p
, "Unified ");
400 if (tc_info
.tc_reduce_tr
) p
+= sprintf(p
, "TCReduction");
402 p
+= sprintf(p
, "\n\tSupported page sizes: ");
404 p
= bitvector_process(p
, tc_pages
);
406 /* when unified date (j=2) is enough */
407 if (tc_info
.tc_unified
) break;
410 p
+= sprintf(p
, "\n");
417 register_info(char *page
)
427 "AR(s) with read side-effects",
429 "CR(s) with read side-effects",
432 for(info
=0; info
< 4; info
++) {
434 if (ia64_pal_register_info(info
, ®_info
[0], ®_info
[1]) != 0) return 0;
436 p
+= sprintf(p
, "%-32s : ", info_type
[info
]);
438 p
= bitregister_process(p
, reg_info
, 128);
440 p
+= sprintf(p
, "\n");
443 if (ia64_pal_rse_info(&phys_stacked
, &hints
) != 0) return 0;
446 "RSE stacked physical registers : %ld\n"
447 "RSE load/store hints : %ld (%s)\n",
448 phys_stacked
, hints
.ph_data
,
449 hints
.ph_data
< RSE_HINTS_COUNT
? rse_hints
[hints
.ph_data
]: "(??)");
451 if (ia64_pal_debug_info(&iregs
, &dregs
))
455 "Instruction debug register pairs : %ld\n"
456 "Data debug register pairs : %ld\n", iregs
, dregs
);
461 static const char *proc_features
[]={
462 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
463 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
, NULL
,NULL
,
464 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
465 NULL
,NULL
,NULL
,NULL
,NULL
, NULL
,NULL
,NULL
,NULL
,
466 NULL
,NULL
,NULL
,NULL
,NULL
,
467 "XIP,XPSR,XFS implemented",
468 "XR1-XR3 implemented",
469 "Disable dynamic predicate prediction",
470 "Disable processor physical number",
471 "Disable dynamic data cache prefetch",
472 "Disable dynamic inst cache prefetch",
473 "Disable dynamic branch prediction",
474 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
475 "Disable BINIT on processor time-out",
476 "Disable dynamic power management (DPM)",
479 "Enable CMCI promotion",
480 "Enable MCA to BINIT promotion",
481 "Enable MCA promotion",
482 "Enable BERR promotion"
487 processor_info(char *page
)
490 const char **v
= proc_features
;
491 u64 avail
=1, status
=1, control
=1;
495 if ((ret
=ia64_pal_proc_get_features(&avail
, &status
, &control
)) != 0) return 0;
497 for(i
=0; i
< 64; i
++, v
++,avail
>>=1, status
>>=1, control
>>=1) {
498 if ( ! *v
) continue;
499 p
+= sprintf(p
, "%-40s : %s%s %s\n", *v
,
500 avail
& 0x1 ? "" : "NotImpl",
501 avail
& 0x1 ? (status
& 0x1 ? "On" : "Off"): "",
502 avail
& 0x1 ? (control
& 0x1 ? "Ctrl" : "NoCtrl"): "");
507 static const char *bus_features
[]={
508 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
509 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
, NULL
,NULL
,
510 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
512 "Request Bus Parking",
514 "Enable Half Transfer",
515 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
516 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
517 NULL
, NULL
, NULL
, NULL
,
518 "Enable Cache Line Repl. Shared",
519 "Enable Cache Line Repl. Exclusive",
520 "Disable Transaction Queuing",
521 "Disable Response Error Checking",
522 "Disable Bus Error Checking",
523 "Disable Bus Requester Internal Error Signalling",
524 "Disable Bus Requester Error Signalling",
525 "Disable Bus Initialization Event Checking",
526 "Disable Bus Initialization Event Signalling",
527 "Disable Bus Address Error Checking",
528 "Disable Bus Address Error Signalling",
529 "Disable Bus Data Error Checking"
537 const char **v
= bus_features
;
538 pal_bus_features_u_t av
, st
, ct
;
539 u64 avail
, status
, control
;
543 if ((ret
=ia64_pal_bus_get_features(&av
, &st
, &ct
)) != 0) return 0;
545 avail
= av
.pal_bus_features_val
;
546 status
= st
.pal_bus_features_val
;
547 control
= ct
.pal_bus_features_val
;
549 for(i
=0; i
< 64; i
++, v
++, avail
>>=1, status
>>=1, control
>>=1) {
550 if ( ! *v
) continue;
551 p
+= sprintf(p
, "%-48s : %s%s %s\n", *v
,
552 avail
& 0x1 ? "" : "NotImpl",
553 avail
& 0x1 ? (status
& 0x1 ? "On" : "Off"): "",
554 avail
& 0x1 ? (control
& 0x1 ? "Ctrl" : "NoCtrl"): "");
560 version_info(char *page
)
562 pal_version_u_t min_ver
, cur_ver
;
565 /* The PAL_VERSION call is advertised as being able to support
566 * both physical and virtual mode calls. This seems to be a documentation
567 * bug rather than firmware bug. In fact, it does only support physical mode.
568 * So now the code reflects this fact and the pal_version() has been updated
571 if (ia64_pal_version(&min_ver
, &cur_ver
) != 0) return 0;
574 "PAL_vendor : 0x%02x (min=0x%02x)\n"
575 "PAL_A : %x.%x.%x (min=%x.%x.%x)\n"
576 "PAL_B : %x.%x.%x (min=%x.%x.%x)\n",
577 cur_ver
.pal_version_s
.pv_pal_vendor
, min_ver
.pal_version_s
.pv_pal_vendor
,
579 cur_ver
.pal_version_s
.pv_pal_a_model
>>4,
580 cur_ver
.pal_version_s
.pv_pal_a_model
&0xf, cur_ver
.pal_version_s
.pv_pal_a_rev
,
581 min_ver
.pal_version_s
.pv_pal_a_model
>>4,
582 min_ver
.pal_version_s
.pv_pal_a_model
&0xf, min_ver
.pal_version_s
.pv_pal_a_rev
,
584 cur_ver
.pal_version_s
.pv_pal_b_model
>>4,
585 cur_ver
.pal_version_s
.pv_pal_b_model
&0xf, cur_ver
.pal_version_s
.pv_pal_b_rev
,
586 min_ver
.pal_version_s
.pv_pal_b_model
>>4,
587 min_ver
.pal_version_s
.pv_pal_b_model
&0xf, min_ver
.pal_version_s
.pv_pal_b_rev
);
592 perfmon_info(char *page
)
596 pal_perf_mon_info_u_t pm_info
;
598 if (ia64_pal_perf_mon_info(pm_buffer
, &pm_info
) != 0) return 0;
601 "PMC/PMD pairs : %d\n"
602 "Counter width : %d bits\n"
603 "Cycle event number : %d\n"
604 "Retired event number : %d\n"
605 "Implemented PMC : ",
606 pm_info
.pal_perf_mon_info_s
.generic
, pm_info
.pal_perf_mon_info_s
.width
,
607 pm_info
.pal_perf_mon_info_s
.cycles
, pm_info
.pal_perf_mon_info_s
.retired
);
609 p
= bitregister_process(p
, pm_buffer
, 256);
610 p
+= sprintf(p
, "\nImplemented PMD : ");
611 p
= bitregister_process(p
, pm_buffer
+4, 256);
612 p
+= sprintf(p
, "\nCycles count capable : ");
613 p
= bitregister_process(p
, pm_buffer
+8, 256);
614 p
+= sprintf(p
, "\nRetired bundles count capable : ");
616 #ifdef CONFIG_ITANIUM
618 * PAL_PERF_MON_INFO reports that only PMC4 can be used to count CPU_CYCLES
619 * which is wrong, both PMC4 and PMD5 support it.
621 if (pm_buffer
[12] == 0x10) pm_buffer
[12]=0x30;
624 p
= bitregister_process(p
, pm_buffer
+12, 256);
626 p
+= sprintf(p
, "\n");
632 frequency_info(char *page
)
635 struct pal_freq_ratio proc
, itc
, bus
;
638 if (ia64_pal_freq_base(&base
) == -1)
639 p
+= sprintf(p
, "Output clock : not implemented\n");
641 p
+= sprintf(p
, "Output clock : %ld ticks/s\n", base
);
643 if (ia64_pal_freq_ratios(&proc
, &bus
, &itc
) != 0) return 0;
646 "Processor/Clock ratio : %ld/%ld\n"
647 "Bus/Clock ratio : %ld/%ld\n"
648 "ITC/Clock ratio : %ld/%ld\n",
649 proc
.num
, proc
.den
, bus
.num
, bus
.den
, itc
.num
, itc
.den
);
659 pal_tr_valid_u_t tr_valid
;
661 pal_vm_info_1_u_t vm_info_1
;
662 pal_vm_info_2_u_t vm_info_2
;
697 if ((status
= ia64_pal_vm_summary(&vm_info_1
, &vm_info_2
)) !=0) {
698 printk(KERN_ERR
"ia64_pal_vm_summary=%ld\n", status
);
701 max
[0] = vm_info_1
.pal_vm_info_1_s
.max_itr_entry
+1;
702 max
[1] = vm_info_1
.pal_vm_info_1_s
.max_dtr_entry
+1;
704 for (i
=0; i
< 2; i
++ ) {
705 for (j
=0; j
< max
[i
]; j
++) {
707 status
= ia64_pal_tr_read(j
, i
, tr_buffer
, &tr_valid
);
709 printk(KERN_ERR
"palinfo: pal call failed on tr[%lu:%lu]=%ld\n",
714 ifa_reg
= (struct ifa_reg
*)&tr_buffer
[2];
716 if (ifa_reg
->valid
== 0) continue;
718 gr_reg
= (struct gr_reg
*)tr_buffer
;
719 itir_reg
= (struct itir_reg
*)&tr_buffer
[1];
720 rid_reg
= (struct rid_reg
*)&tr_buffer
[3];
722 pgm
= -1 << (itir_reg
->ps
- 12);
724 "%cTR%lu: av=%d pv=%d dv=%d mv=%d\n"
729 tr_valid
.pal_tr_valid_s
.access_rights_valid
,
730 tr_valid
.pal_tr_valid_s
.priv_level_valid
,
731 tr_valid
.pal_tr_valid_s
.dirty_bit_valid
,
732 tr_valid
.pal_tr_valid_s
.mem_attr_valid
,
733 (gr_reg
->ppn
& pgm
)<< 12, (ifa_reg
->vpn
& pgm
)<< 12);
735 p
= bitvector_process(p
, 1<< itir_reg
->ps
);
744 gr_reg
->pl
, gr_reg
->ar
, rid_reg
->rid
, gr_reg
->p
, gr_reg
->ma
,
754 * List {name,function} pairs for every entry in /proc/palinfo/cpu*
756 static palinfo_entry_t palinfo_entries
[]={
757 { "version_info", version_info
, },
758 { "vm_info", vm_info
, },
759 { "cache_info", cache_info
, },
760 { "power_info", power_info
, },
761 { "register_info", register_info
, },
762 { "processor_info", processor_info
, },
763 { "perfmon_info", perfmon_info
, },
764 { "frequency_info", frequency_info
, },
765 { "bus_info", bus_info
},
766 { "tr_info", tr_info
, }
769 #define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
772 * this array is used to keep track of the proc entries we create. This is
773 * required in the module mode when we need to remove all entries. The procfs code
774 * does not do recursion of deletion
777 * - +1 accounts for the cpuN directory entry in /proc/pal
779 #define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1))
781 static struct proc_dir_entry
*palinfo_proc_entries
[NR_PALINFO_PROC_ENTRIES
];
782 static struct proc_dir_entry
*palinfo_dir
;
785 * This data structure is used to pass which cpu,function is being requested
786 * It must fit in a 64bit quantity to be passed to the proc callback routine
788 * In SMP mode, when we get a request for another CPU, we must call that
789 * other CPU using IPI and wait for the result before returning.
794 unsigned req_cpu
: 32; /* for which CPU this info is */
795 unsigned func_id
: 32; /* which function is requested */
799 #define req_cpu pal_func_cpu.req_cpu
800 #define func_id pal_func_cpu.func_id
805 * used to hold information about final function to call
808 palinfo_func_t func
; /* pointer to function to call */
809 char *page
; /* buffer to store results */
810 int ret
; /* return value from call */
811 } palinfo_smp_data_t
;
815 * this function does the actual final call and he called
816 * from the smp code, i.e., this is the palinfo callback routine
819 palinfo_smp_call(void *info
)
821 palinfo_smp_data_t
*data
= (palinfo_smp_data_t
*)info
;
823 printk(KERN_ERR
"palinfo: data pointer is NULL\n");
824 data
->ret
= 0; /* no output */
827 /* does this actual call */
828 data
->ret
= (*data
->func
)(data
->page
);
832 * function called to trigger the IPI, we need to access a remote CPU
834 * 0 : error or nothing to output
835 * otherwise how many bytes in the "page" buffer were written
838 int palinfo_handle_smp(pal_func_cpu_u_t
*f
, char *page
)
840 palinfo_smp_data_t ptr
;
843 ptr
.func
= palinfo_entries
[f
->func_id
].proc_read
;
845 ptr
.ret
= 0; /* just in case */
848 /* will send IPI to other CPU and wait for completion of remote call */
849 if ((ret
=smp_call_function_single(f
->req_cpu
, palinfo_smp_call
, &ptr
, 0, 1))) {
850 printk(KERN_ERR
"palinfo: remote CPU call from %d to %d on function %d: "
851 "error %d\n", smp_processor_id(), f
->req_cpu
, f
->func_id
, ret
);
856 #else /* ! CONFIG_SMP */
858 int palinfo_handle_smp(pal_func_cpu_u_t
*f
, char *page
)
860 printk(KERN_ERR
"palinfo: should not be called with non SMP kernel\n");
863 #endif /* CONFIG_SMP */
866 * Entry point routine: all calls go through this function
869 palinfo_read_entry(char *page
, char **start
, off_t off
, int count
, int *eof
, void *data
)
872 pal_func_cpu_u_t
*f
= (pal_func_cpu_u_t
*)&data
;
875 * in SMP mode, we may need to call another CPU to get correct
876 * information. PAL, by definition, is processor specific
878 if (f
->req_cpu
== get_cpu())
879 len
= (*palinfo_entries
[f
->func_id
].proc_read
)(page
);
881 len
= palinfo_handle_smp(f
, page
);
885 if (len
<= off
+count
) *eof
= 1;
890 if (len
>count
) len
= count
;
897 create_palinfo_proc_entries(unsigned int cpu
)
899 # define CPUSTR "cpu%d"
902 struct proc_dir_entry
**pdir
;
903 struct proc_dir_entry
*cpu_dir
;
905 char cpustr
[sizeof(CPUSTR
)];
909 * we keep track of created entries in a depth-first order for
910 * cleanup purposes. Each entry is stored into palinfo_proc_entries
912 sprintf(cpustr
,CPUSTR
, cpu
);
914 cpu_dir
= proc_mkdir(cpustr
, palinfo_dir
);
919 * Compute the location to store per cpu entries
920 * We dont store the top level entry in this list, but
921 * remove it finally after removing all cpu entries.
923 pdir
= &palinfo_proc_entries
[cpu
*(NR_PALINFO_ENTRIES
+1)];
925 for (j
=0; j
< NR_PALINFO_ENTRIES
; j
++) {
927 *pdir
= create_proc_read_entry(
928 palinfo_entries
[j
].name
, 0, cpu_dir
,
929 palinfo_read_entry
, (void *)f
.value
);
931 (*pdir
)->owner
= THIS_MODULE
;
937 remove_palinfo_proc_entries(unsigned int hcpu
)
940 struct proc_dir_entry
*cpu_dir
, **pdir
;
942 pdir
= &palinfo_proc_entries
[hcpu
*(NR_PALINFO_ENTRIES
+1)];
945 for (j
=0; j
< (NR_PALINFO_ENTRIES
); j
++) {
947 remove_proc_entry ((*pdir
)->name
, cpu_dir
);
953 remove_proc_entry(cpu_dir
->name
, palinfo_dir
);
957 static int __devinit
palinfo_cpu_callback(struct notifier_block
*nfb
,
958 unsigned long action
,
961 unsigned int hotcpu
= (unsigned long)hcpu
;
965 create_palinfo_proc_entries(hotcpu
);
967 #ifdef CONFIG_HOTPLUG_CPU
969 remove_palinfo_proc_entries(hotcpu
);
976 static struct notifier_block palinfo_cpu_notifier
=
978 .notifier_call
= palinfo_cpu_callback
,
987 printk(KERN_INFO
"PAL Information Facility v%s\n", PALINFO_VERSION
);
988 palinfo_dir
= proc_mkdir("pal", NULL
);
990 /* Create palinfo dirs in /proc for all online cpus */
991 for_each_online_cpu(i
) {
992 create_palinfo_proc_entries(i
);
995 /* Register for future delivery via notify registration */
996 register_cpu_notifier(&palinfo_cpu_notifier
);
1006 /* remove all nodes: depth first pass. Could optimize this */
1007 for_each_online_cpu(i
) {
1008 remove_palinfo_proc_entries(i
);
1012 * Remove the top level entry finally
1014 remove_proc_entry(palinfo_dir
->name
, NULL
);
1017 * Unregister from cpu notifier callbacks
1019 unregister_cpu_notifier(&palinfo_cpu_notifier
);
1022 module_init(palinfo_init
);
1023 module_exit(palinfo_exit
);