1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2006 Mike Kravetz IBM Corporation |
4 | * |
5 | * Hypervisor Call Instrumentation |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/percpu.h> |
10 | #include <linux/debugfs.h> |
11 | #include <linux/seq_file.h> |
12 | #include <linux/cpumask.h> |
13 | #include <asm/hvcall.h> |
14 | #include <asm/firmware.h> |
15 | #include <asm/cputable.h> |
16 | #include <asm/trace.h> |
17 | #include <asm/machdep.h> |
18 | |
19 | /* For hcall instrumentation. One structure per-hcall, per-CPU */ |
20 | struct hcall_stats { |
21 | unsigned long num_calls; /* number of calls (on this CPU) */ |
22 | unsigned long tb_total; /* total wall time (mftb) of calls. */ |
23 | unsigned long purr_total; /* total cpu time (PURR) of calls. */ |
24 | unsigned long tb_start; |
25 | unsigned long purr_start; |
26 | }; |
27 | #define HCALL_STAT_ARRAY_SIZE ((MAX_HCALL_OPCODE >> 2) + 1) |
28 | |
29 | static DEFINE_PER_CPU(struct hcall_stats[HCALL_STAT_ARRAY_SIZE], hcall_stats); |
30 | |
31 | /* |
32 | * Routines for displaying the statistics in debugfs |
33 | */ |
34 | static void *hc_start(struct seq_file *m, loff_t *pos) |
35 | { |
36 | if ((int)*pos < (HCALL_STAT_ARRAY_SIZE-1)) |
37 | return (void *)(unsigned long)(*pos + 1); |
38 | |
39 | return NULL; |
40 | } |
41 | |
42 | static void *hc_next(struct seq_file *m, void *p, loff_t * pos) |
43 | { |
44 | ++*pos; |
45 | |
46 | return hc_start(m, pos); |
47 | } |
48 | |
49 | static void hc_stop(struct seq_file *m, void *p) |
50 | { |
51 | } |
52 | |
53 | static int hc_show(struct seq_file *m, void *p) |
54 | { |
55 | unsigned long h_num = (unsigned long)p; |
56 | struct hcall_stats *hs = m->private; |
57 | |
58 | if (hs[h_num].num_calls) { |
59 | if (cpu_has_feature(CPU_FTR_PURR)) |
60 | seq_printf(m, fmt: "%lu %lu %lu %lu\n" , h_num<<2, |
61 | hs[h_num].num_calls, |
62 | hs[h_num].tb_total, |
63 | hs[h_num].purr_total); |
64 | else |
65 | seq_printf(m, fmt: "%lu %lu %lu\n" , h_num<<2, |
66 | hs[h_num].num_calls, |
67 | hs[h_num].tb_total); |
68 | } |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | static const struct seq_operations hcall_inst_sops = { |
74 | .start = hc_start, |
75 | .next = hc_next, |
76 | .stop = hc_stop, |
77 | .show = hc_show |
78 | }; |
79 | |
80 | DEFINE_SEQ_ATTRIBUTE(hcall_inst); |
81 | |
82 | #define HCALL_ROOT_DIR "hcall_inst" |
83 | #define CPU_NAME_BUF_SIZE 32 |
84 | |
85 | |
86 | static void probe_hcall_entry(void *ignored, unsigned long opcode, unsigned long *args) |
87 | { |
88 | struct hcall_stats *h; |
89 | |
90 | if (opcode > MAX_HCALL_OPCODE) |
91 | return; |
92 | |
93 | h = this_cpu_ptr(&hcall_stats[opcode / 4]); |
94 | h->tb_start = mftb(); |
95 | h->purr_start = mfspr(SPRN_PURR); |
96 | } |
97 | |
98 | static void probe_hcall_exit(void *ignored, unsigned long opcode, long retval, |
99 | unsigned long *retbuf) |
100 | { |
101 | struct hcall_stats *h; |
102 | |
103 | if (opcode > MAX_HCALL_OPCODE) |
104 | return; |
105 | |
106 | h = this_cpu_ptr(&hcall_stats[opcode / 4]); |
107 | h->num_calls++; |
108 | h->tb_total += mftb() - h->tb_start; |
109 | h->purr_total += mfspr(SPRN_PURR) - h->purr_start; |
110 | } |
111 | |
112 | static int __init hcall_inst_init(void) |
113 | { |
114 | struct dentry *hcall_root; |
115 | char cpu_name_buf[CPU_NAME_BUF_SIZE]; |
116 | int cpu; |
117 | |
118 | if (!firmware_has_feature(FW_FEATURE_LPAR)) |
119 | return 0; |
120 | |
121 | if (register_trace_hcall_entry(probe_hcall_entry, NULL)) |
122 | return -EINVAL; |
123 | |
124 | if (register_trace_hcall_exit(probe_hcall_exit, NULL)) { |
125 | unregister_trace_hcall_entry(probe_hcall_entry, NULL); |
126 | return -EINVAL; |
127 | } |
128 | |
129 | hcall_root = debugfs_create_dir(HCALL_ROOT_DIR, NULL); |
130 | |
131 | for_each_possible_cpu(cpu) { |
132 | snprintf(buf: cpu_name_buf, CPU_NAME_BUF_SIZE, fmt: "cpu%d" , cpu); |
133 | debugfs_create_file(name: cpu_name_buf, mode: 0444, parent: hcall_root, |
134 | per_cpu(hcall_stats, cpu), |
135 | fops: &hcall_inst_fops); |
136 | } |
137 | |
138 | return 0; |
139 | } |
140 | machine_device_initcall(pseries, hcall_inst_init); |
141 | |