diff options
-rw-r--r-- | arch/powerpc/Kconfig.debug | 14 | ||||
-rw-r--r-- | arch/powerpc/kernel/asm-offsets.c | 7 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hvCall.S | 72 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hvCall_inst.c | 129 | ||||
-rw-r--r-- | include/asm-powerpc/hvcall.h | 12 |
6 files changed, 234 insertions, 1 deletions
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index e29ef77d3b00..d7b2aedd89aa 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug | |||
@@ -18,6 +18,20 @@ config DEBUG_STACK_USAGE | |||
18 | 18 | ||
19 | This option will slow down process creation somewhat. | 19 | This option will slow down process creation somewhat. |
20 | 20 | ||
21 | config HCALL_STATS | ||
22 | bool "Hypervisor call instrumentation" | ||
23 | depends on PPC_PSERIES && DEBUG_FS | ||
24 | help | ||
25 | Adds code to keep track of the number of hypervisor calls made and | ||
26 | the amount of time spent in hypervisor callsr. Wall time spent in | ||
27 | each call is always calculated, and if available CPU cycles spent | ||
28 | are also calculated. A directory named hcall_inst is added at the | ||
29 | root of the debugfs filesystem. Within the hcall_inst directory | ||
30 | are files that contain CPU specific call statistics. | ||
31 | |||
32 | This option will add a small amount of overhead to all hypervisor | ||
33 | calls. | ||
34 | |||
21 | config DEBUGGER | 35 | config DEBUGGER |
22 | bool "Enable debugger hooks" | 36 | bool "Enable debugger hooks" |
23 | depends on DEBUG_KERNEL | 37 | depends on DEBUG_KERNEL |
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index c53acd2a6dfc..c578e7ab8173 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c | |||
@@ -137,6 +137,7 @@ int main(void) | |||
137 | DEFINE(PACA_USER_TIME, offsetof(struct paca_struct, user_time)); | 137 | DEFINE(PACA_USER_TIME, offsetof(struct paca_struct, user_time)); |
138 | DEFINE(PACA_SYSTEM_TIME, offsetof(struct paca_struct, system_time)); | 138 | DEFINE(PACA_SYSTEM_TIME, offsetof(struct paca_struct, system_time)); |
139 | DEFINE(PACA_SLBSHADOWPTR, offsetof(struct paca_struct, slb_shadow_ptr)); | 139 | DEFINE(PACA_SLBSHADOWPTR, offsetof(struct paca_struct, slb_shadow_ptr)); |
140 | DEFINE(PACA_DATA_OFFSET, offsetof(struct paca_struct, data_offset)); | ||
140 | 141 | ||
141 | DEFINE(SLBSHADOW_STACKVSID, | 142 | DEFINE(SLBSHADOW_STACKVSID, |
142 | offsetof(struct slb_shadow, save_area[SLB_NUM_BOLTED - 1].vsid)); | 143 | offsetof(struct slb_shadow, save_area[SLB_NUM_BOLTED - 1].vsid)); |
@@ -165,6 +166,12 @@ int main(void) | |||
165 | /* Create extra stack space for SRR0 and SRR1 when calling prom/rtas. */ | 166 | /* Create extra stack space for SRR0 and SRR1 when calling prom/rtas. */ |
166 | DEFINE(PROM_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16); | 167 | DEFINE(PROM_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16); |
167 | DEFINE(RTAS_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16); | 168 | DEFINE(RTAS_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16); |
169 | |||
170 | /* hcall statistics */ | ||
171 | DEFINE(HCALL_STAT_SIZE, sizeof(struct hcall_stats)); | ||
172 | DEFINE(HCALL_STAT_CALLS, offsetof(struct hcall_stats, num_calls)); | ||
173 | DEFINE(HCALL_STAT_TB, offsetof(struct hcall_stats, tb_total)); | ||
174 | DEFINE(HCALL_STAT_PURR, offsetof(struct hcall_stats, purr_total)); | ||
168 | #endif /* CONFIG_PPC64 */ | 175 | #endif /* CONFIG_PPC64 */ |
169 | DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0])); | 176 | DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0])); |
170 | DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1])); | 177 | DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1])); |
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index e5e0ff466904..997243a91be8 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile | |||
@@ -12,3 +12,4 @@ obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o | |||
12 | 12 | ||
13 | obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o | 13 | obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o |
14 | obj-$(CONFIG_HVCS) += hvcserver.o | 14 | obj-$(CONFIG_HVCS) += hvcserver.o |
15 | obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o | ||
diff --git a/arch/powerpc/platforms/pseries/hvCall.S b/arch/powerpc/platforms/pseries/hvCall.S index 9a99b056bd27..c00cfed7af2c 100644 --- a/arch/powerpc/platforms/pseries/hvCall.S +++ b/arch/powerpc/platforms/pseries/hvCall.S | |||
@@ -10,9 +10,69 @@ | |||
10 | #include <asm/hvcall.h> | 10 | #include <asm/hvcall.h> |
11 | #include <asm/processor.h> | 11 | #include <asm/processor.h> |
12 | #include <asm/ppc_asm.h> | 12 | #include <asm/ppc_asm.h> |
13 | #include <asm/asm-offsets.h> | ||
13 | 14 | ||
14 | #define STK_PARM(i) (48 + ((i)-3)*8) | 15 | #define STK_PARM(i) (48 + ((i)-3)*8) |
15 | 16 | ||
17 | #ifdef CONFIG_HCALL_STATS | ||
18 | /* | ||
19 | * precall must preserve all registers. use unused STK_PARM() | ||
20 | * areas to save snapshots and opcode. | ||
21 | */ | ||
22 | #define HCALL_INST_PRECALL \ | ||
23 | std r3,STK_PARM(r3)(r1); /* save opcode */ \ | ||
24 | mftb r0; /* get timebase and */ \ | ||
25 | std r0,STK_PARM(r5)(r1); /* save for later */ \ | ||
26 | BEGIN_FTR_SECTION; \ | ||
27 | mfspr r0,SPRN_PURR; /* get PURR and */ \ | ||
28 | std r0,STK_PARM(r6)(r1); /* save for later */ \ | ||
29 | END_FTR_SECTION_IFCLR(CPU_FTR_PURR); | ||
30 | |||
31 | /* | ||
32 | * postcall is performed immediately before function return which | ||
33 | * allows liberal use of volatile registers. | ||
34 | */ | ||
35 | #define HCALL_INST_POSTCALL \ | ||
36 | ld r4,STK_PARM(r3)(r1); /* validate opcode */ \ | ||
37 | cmpldi cr7,r4,MAX_HCALL_OPCODE; \ | ||
38 | bgt- cr7,1f; \ | ||
39 | \ | ||
40 | /* get time and PURR snapshots after hcall */ \ | ||
41 | mftb r7; /* timebase after */ \ | ||
42 | BEGIN_FTR_SECTION; \ | ||
43 | mfspr r8,SPRN_PURR; /* PURR after */ \ | ||
44 | ld r6,STK_PARM(r6)(r1); /* PURR before */ \ | ||
45 | subf r6,r6,r8; /* delta */ \ | ||
46 | END_FTR_SECTION_IFCLR(CPU_FTR_PURR); \ | ||
47 | ld r5,STK_PARM(r5)(r1); /* timebase before */ \ | ||
48 | subf r5,r5,r7; /* time delta */ \ | ||
49 | \ | ||
50 | /* calculate address of stat structure r4 = opcode */ \ | ||
51 | srdi r4,r4,2; /* index into array */ \ | ||
52 | mulli r4,r4,HCALL_STAT_SIZE; \ | ||
53 | LOAD_REG_ADDR(r7, per_cpu__hcall_stats); \ | ||
54 | add r4,r4,r7; \ | ||
55 | ld r7,PACA_DATA_OFFSET(r13); /* per cpu offset */ \ | ||
56 | add r4,r4,r7; \ | ||
57 | \ | ||
58 | /* update stats */ \ | ||
59 | ld r7,HCALL_STAT_CALLS(r4); /* count */ \ | ||
60 | addi r7,r7,1; \ | ||
61 | std r7,HCALL_STAT_CALLS(r4); \ | ||
62 | ld r7,HCALL_STAT_TB(r4); /* timebase */ \ | ||
63 | add r7,r7,r5; \ | ||
64 | std r7,HCALL_STAT_TB(r4); \ | ||
65 | BEGIN_FTR_SECTION; \ | ||
66 | ld r7,HCALL_STAT_PURR(r4); /* PURR */ \ | ||
67 | add r7,r7,r6; \ | ||
68 | std r7,HCALL_STAT_PURR(r4); \ | ||
69 | END_FTR_SECTION_IFCLR(CPU_FTR_PURR); \ | ||
70 | 1: | ||
71 | #else | ||
72 | #define HCALL_INST_PRECALL | ||
73 | #define HCALL_INST_POSTCALL | ||
74 | #endif | ||
75 | |||
16 | .text | 76 | .text |
17 | 77 | ||
18 | _GLOBAL(plpar_hcall_norets) | 78 | _GLOBAL(plpar_hcall_norets) |
@@ -21,8 +81,12 @@ _GLOBAL(plpar_hcall_norets) | |||
21 | mfcr r0 | 81 | mfcr r0 |
22 | stw r0,8(r1) | 82 | stw r0,8(r1) |
23 | 83 | ||
84 | HCALL_INST_PRECALL | ||
85 | |||
24 | HVSC /* invoke the hypervisor */ | 86 | HVSC /* invoke the hypervisor */ |
25 | 87 | ||
88 | HCALL_INST_POSTCALL | ||
89 | |||
26 | lwz r0,8(r1) | 90 | lwz r0,8(r1) |
27 | mtcrf 0xff,r0 | 91 | mtcrf 0xff,r0 |
28 | blr /* return r3 = status */ | 92 | blr /* return r3 = status */ |
@@ -33,6 +97,8 @@ _GLOBAL(plpar_hcall) | |||
33 | mfcr r0 | 97 | mfcr r0 |
34 | stw r0,8(r1) | 98 | stw r0,8(r1) |
35 | 99 | ||
100 | HCALL_INST_PRECALL | ||
101 | |||
36 | std r4,STK_PARM(r4)(r1) /* Save ret buffer */ | 102 | std r4,STK_PARM(r4)(r1) /* Save ret buffer */ |
37 | 103 | ||
38 | mr r4,r5 | 104 | mr r4,r5 |
@@ -50,6 +116,8 @@ _GLOBAL(plpar_hcall) | |||
50 | std r6, 16(r12) | 116 | std r6, 16(r12) |
51 | std r7, 24(r12) | 117 | std r7, 24(r12) |
52 | 118 | ||
119 | HCALL_INST_POSTCALL | ||
120 | |||
53 | lwz r0,8(r1) | 121 | lwz r0,8(r1) |
54 | mtcrf 0xff,r0 | 122 | mtcrf 0xff,r0 |
55 | 123 | ||
@@ -61,6 +129,8 @@ _GLOBAL(plpar_hcall9) | |||
61 | mfcr r0 | 129 | mfcr r0 |
62 | stw r0,8(r1) | 130 | stw r0,8(r1) |
63 | 131 | ||
132 | HCALL_INST_PRECALL | ||
133 | |||
64 | std r4,STK_PARM(r4)(r1) /* Save ret buffer */ | 134 | std r4,STK_PARM(r4)(r1) /* Save ret buffer */ |
65 | 135 | ||
66 | mr r4,r5 | 136 | mr r4,r5 |
@@ -86,6 +156,8 @@ _GLOBAL(plpar_hcall9) | |||
86 | std r11,56(r12) | 156 | std r11,56(r12) |
87 | std r12,64(r12) | 157 | std r12,64(r12) |
88 | 158 | ||
159 | HCALL_INST_POSTCALL | ||
160 | |||
89 | lwz r0,8(r1) | 161 | lwz r0,8(r1) |
90 | mtcrf 0xff,r0 | 162 | mtcrf 0xff,r0 |
91 | 163 | ||
diff --git a/arch/powerpc/platforms/pseries/hvCall_inst.c b/arch/powerpc/platforms/pseries/hvCall_inst.c new file mode 100644 index 000000000000..641e6511cf06 --- /dev/null +++ b/arch/powerpc/platforms/pseries/hvCall_inst.c | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006 Mike Kravetz IBM Corporation | ||
3 | * | ||
4 | * Hypervisor Call Instrumentation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/percpu.h> | ||
23 | #include <linux/debugfs.h> | ||
24 | #include <linux/seq_file.h> | ||
25 | #include <linux/cpumask.h> | ||
26 | #include <asm/hvcall.h> | ||
27 | #include <asm/firmware.h> | ||
28 | #include <asm/cputable.h> | ||
29 | |||
30 | DEFINE_PER_CPU(struct hcall_stats[HCALL_STAT_ARRAY_SIZE], hcall_stats); | ||
31 | |||
32 | /* | ||
33 | * Routines for displaying the statistics in debugfs | ||
34 | */ | ||
35 | static void *hc_start(struct seq_file *m, loff_t *pos) | ||
36 | { | ||
37 | if ((int)*pos < HCALL_STAT_ARRAY_SIZE) | ||
38 | return (void *)(unsigned long)(*pos + 1); | ||
39 | |||
40 | return NULL; | ||
41 | } | ||
42 | |||
43 | static void *hc_next(struct seq_file *m, void *p, loff_t * pos) | ||
44 | { | ||
45 | ++*pos; | ||
46 | |||
47 | return hc_start(m, pos); | ||
48 | } | ||
49 | |||
50 | static void hc_stop(struct seq_file *m, void *p) | ||
51 | { | ||
52 | } | ||
53 | |||
54 | static int hc_show(struct seq_file *m, void *p) | ||
55 | { | ||
56 | unsigned long h_num = (unsigned long)p; | ||
57 | struct hcall_stats *hs = (struct hcall_stats *)m->private; | ||
58 | |||
59 | if (hs[h_num].num_calls) { | ||
60 | if (!cpu_has_feature(CPU_FTR_PURR)) | ||
61 | seq_printf(m, "%lu %lu %lu %lu\n", h_num<<2, | ||
62 | hs[h_num].num_calls, | ||
63 | hs[h_num].tb_total, | ||
64 | hs[h_num].purr_total); | ||
65 | else | ||
66 | seq_printf(m, "%lu %lu %lu\n", h_num<<2, | ||
67 | hs[h_num].num_calls, | ||
68 | hs[h_num].tb_total); | ||
69 | } | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static struct seq_operations hcall_inst_seq_ops = { | ||
75 | .start = hc_start, | ||
76 | .next = hc_next, | ||
77 | .stop = hc_stop, | ||
78 | .show = hc_show | ||
79 | }; | ||
80 | |||
81 | static int hcall_inst_seq_open(struct inode *inode, struct file *file) | ||
82 | { | ||
83 | int rc; | ||
84 | struct seq_file *seq; | ||
85 | |||
86 | rc = seq_open(file, &hcall_inst_seq_ops); | ||
87 | seq = file->private_data; | ||
88 | seq->private = file->f_dentry->d_inode->u.generic_ip; | ||
89 | |||
90 | return rc; | ||
91 | } | ||
92 | |||
93 | static struct file_operations hcall_inst_seq_fops = { | ||
94 | .open = hcall_inst_seq_open, | ||
95 | .read = seq_read, | ||
96 | .llseek = seq_lseek, | ||
97 | .release = seq_release, | ||
98 | }; | ||
99 | |||
100 | #define HCALL_ROOT_DIR "hcall_inst" | ||
101 | #define CPU_NAME_BUF_SIZE 32 | ||
102 | |||
103 | static int __init hcall_inst_init(void) | ||
104 | { | ||
105 | struct dentry *hcall_root; | ||
106 | struct dentry *hcall_file; | ||
107 | char cpu_name_buf[CPU_NAME_BUF_SIZE]; | ||
108 | int cpu; | ||
109 | |||
110 | if (!firmware_has_feature(FW_FEATURE_LPAR)) | ||
111 | return 0; | ||
112 | |||
113 | hcall_root = debugfs_create_dir(HCALL_ROOT_DIR, NULL); | ||
114 | if (!hcall_root) | ||
115 | return -ENOMEM; | ||
116 | |||
117 | for_each_possible_cpu(cpu) { | ||
118 | snprintf(cpu_name_buf, CPU_NAME_BUF_SIZE, "cpu%d", cpu); | ||
119 | hcall_file = debugfs_create_file(cpu_name_buf, S_IRUGO, | ||
120 | hcall_root, | ||
121 | per_cpu(hcall_stats, cpu), | ||
122 | &hcall_inst_seq_fops); | ||
123 | if (!hcall_file) | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | __initcall(hcall_inst_init); | ||
diff --git a/include/asm-powerpc/hvcall.h b/include/asm-powerpc/hvcall.h index 63ce1ac8c1f4..257d1cecb8c9 100644 --- a/include/asm-powerpc/hvcall.h +++ b/include/asm-powerpc/hvcall.h | |||
@@ -208,7 +208,7 @@ | |||
208 | #define H_JOIN 0x298 | 208 | #define H_JOIN 0x298 |
209 | #define H_VASI_STATE 0x2A4 | 209 | #define H_VASI_STATE 0x2A4 |
210 | #define H_ENABLE_CRQ 0x2B0 | 210 | #define H_ENABLE_CRQ 0x2B0 |
211 | #define MAX_HCALL_OPCODES (H_ENABLE_CRQ >> 2) | 211 | #define MAX_HCALL_OPCODE H_ENABLE_CRQ |
212 | 212 | ||
213 | #ifndef __ASSEMBLY__ | 213 | #ifndef __ASSEMBLY__ |
214 | 214 | ||
@@ -246,6 +246,16 @@ long plpar_hcall(unsigned long opcode, unsigned long *retbuf, ...); | |||
246 | #define PLPAR_HCALL9_BUFSIZE 9 | 246 | #define PLPAR_HCALL9_BUFSIZE 9 |
247 | long plpar_hcall9(unsigned long opcode, unsigned long *retbuf, ...); | 247 | long plpar_hcall9(unsigned long opcode, unsigned long *retbuf, ...); |
248 | 248 | ||
249 | /* For hcall instrumentation. One structure per-hcall, per-CPU */ | ||
250 | struct hcall_stats { | ||
251 | unsigned long num_calls; /* number of calls (on this CPU) */ | ||
252 | unsigned long tb_total; /* total wall time (mftb) of calls. */ | ||
253 | unsigned long purr_total; /* total cpu time (PURR) of calls. */ | ||
254 | }; | ||
255 | void update_hcall_stats(unsigned long opcode, unsigned long tb_delta, | ||
256 | unsigned long purr_delta); | ||
257 | #define HCALL_STAT_ARRAY_SIZE ((MAX_HCALL_OPCODE >> 2) + 1) | ||
258 | |||
249 | #endif /* __ASSEMBLY__ */ | 259 | #endif /* __ASSEMBLY__ */ |
250 | #endif /* __KERNEL__ */ | 260 | #endif /* __KERNEL__ */ |
251 | #endif /* _ASM_POWERPC_HVCALL_H */ | 261 | #endif /* _ASM_POWERPC_HVCALL_H */ |