1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * SN Platform GRU Driver |
4 | * |
5 | * PROC INTERFACES |
6 | * |
7 | * This file supports the /proc interfaces for the GRU driver |
8 | * |
9 | * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. |
10 | */ |
11 | |
12 | #include <linux/proc_fs.h> |
13 | #include <linux/device.h> |
14 | #include <linux/seq_file.h> |
15 | #include <linux/uaccess.h> |
16 | #include "gru.h" |
17 | #include "grulib.h" |
18 | #include "grutables.h" |
19 | |
20 | #define printstat(s, f) printstat_val(s, &gru_stats.f, #f) |
21 | |
22 | static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id) |
23 | { |
24 | unsigned long val = atomic_long_read(v); |
25 | |
26 | seq_printf(m: s, fmt: "%16lu %s\n" , val, id); |
27 | } |
28 | |
29 | static int statistics_show(struct seq_file *s, void *p) |
30 | { |
31 | printstat(s, vdata_alloc); |
32 | printstat(s, vdata_free); |
33 | printstat(s, gts_alloc); |
34 | printstat(s, gts_free); |
35 | printstat(s, gms_alloc); |
36 | printstat(s, gms_free); |
37 | printstat(s, gts_double_allocate); |
38 | printstat(s, assign_context); |
39 | printstat(s, assign_context_failed); |
40 | printstat(s, free_context); |
41 | printstat(s, load_user_context); |
42 | printstat(s, load_kernel_context); |
43 | printstat(s, lock_kernel_context); |
44 | printstat(s, unlock_kernel_context); |
45 | printstat(s, steal_user_context); |
46 | printstat(s, steal_kernel_context); |
47 | printstat(s, steal_context_failed); |
48 | printstat(s, nopfn); |
49 | printstat(s, asid_new); |
50 | printstat(s, asid_next); |
51 | printstat(s, asid_wrap); |
52 | printstat(s, asid_reuse); |
53 | printstat(s, intr); |
54 | printstat(s, intr_cbr); |
55 | printstat(s, intr_tfh); |
56 | printstat(s, intr_spurious); |
57 | printstat(s, intr_mm_lock_failed); |
58 | printstat(s, call_os); |
59 | printstat(s, call_os_wait_queue); |
60 | printstat(s, user_flush_tlb); |
61 | printstat(s, user_unload_context); |
62 | printstat(s, user_exception); |
63 | printstat(s, set_context_option); |
64 | printstat(s, check_context_retarget_intr); |
65 | printstat(s, check_context_unload); |
66 | printstat(s, tlb_dropin); |
67 | printstat(s, tlb_preload_page); |
68 | printstat(s, tlb_dropin_fail_no_asid); |
69 | printstat(s, tlb_dropin_fail_upm); |
70 | printstat(s, tlb_dropin_fail_invalid); |
71 | printstat(s, tlb_dropin_fail_range_active); |
72 | printstat(s, tlb_dropin_fail_idle); |
73 | printstat(s, tlb_dropin_fail_fmm); |
74 | printstat(s, tlb_dropin_fail_no_exception); |
75 | printstat(s, tfh_stale_on_fault); |
76 | printstat(s, mmu_invalidate_range); |
77 | printstat(s, mmu_invalidate_page); |
78 | printstat(s, flush_tlb); |
79 | printstat(s, flush_tlb_gru); |
80 | printstat(s, flush_tlb_gru_tgh); |
81 | printstat(s, flush_tlb_gru_zero_asid); |
82 | printstat(s, copy_gpa); |
83 | printstat(s, read_gpa); |
84 | printstat(s, mesq_receive); |
85 | printstat(s, mesq_receive_none); |
86 | printstat(s, mesq_send); |
87 | printstat(s, mesq_send_failed); |
88 | printstat(s, mesq_noop); |
89 | printstat(s, mesq_send_unexpected_error); |
90 | printstat(s, mesq_send_lb_overflow); |
91 | printstat(s, mesq_send_qlimit_reached); |
92 | printstat(s, mesq_send_amo_nacked); |
93 | printstat(s, mesq_send_put_nacked); |
94 | printstat(s, mesq_qf_locked); |
95 | printstat(s, mesq_qf_noop_not_full); |
96 | printstat(s, mesq_qf_switch_head_failed); |
97 | printstat(s, mesq_qf_unexpected_error); |
98 | printstat(s, mesq_noop_unexpected_error); |
99 | printstat(s, mesq_noop_lb_overflow); |
100 | printstat(s, mesq_noop_qlimit_reached); |
101 | printstat(s, mesq_noop_amo_nacked); |
102 | printstat(s, mesq_noop_put_nacked); |
103 | printstat(s, mesq_noop_page_overflow); |
104 | return 0; |
105 | } |
106 | |
107 | static ssize_t statistics_write(struct file *file, const char __user *userbuf, |
108 | size_t count, loff_t *data) |
109 | { |
110 | memset(&gru_stats, 0, sizeof(gru_stats)); |
111 | return count; |
112 | } |
113 | |
114 | static int mcs_statistics_show(struct seq_file *s, void *p) |
115 | { |
116 | int op; |
117 | unsigned long total, count, max; |
118 | static char *id[] = {"cch_allocate" , "cch_start" , "cch_interrupt" , |
119 | "cch_interrupt_sync" , "cch_deallocate" , "tfh_write_only" , |
120 | "tfh_write_restart" , "tgh_invalidate" }; |
121 | |
122 | seq_puts(m: s, s: "#id count aver-clks max-clks\n" ); |
123 | for (op = 0; op < mcsop_last; op++) { |
124 | count = atomic_long_read(v: &mcs_op_statistics[op].count); |
125 | total = atomic_long_read(v: &mcs_op_statistics[op].total); |
126 | max = mcs_op_statistics[op].max; |
127 | seq_printf(m: s, fmt: "%-20s%12ld%12ld%12ld\n" , id[op], count, |
128 | count ? total / count : 0, max); |
129 | } |
130 | return 0; |
131 | } |
132 | |
133 | static ssize_t mcs_statistics_write(struct file *file, |
134 | const char __user *userbuf, size_t count, loff_t *data) |
135 | { |
136 | memset(mcs_op_statistics, 0, sizeof(mcs_op_statistics)); |
137 | return count; |
138 | } |
139 | |
140 | static int options_show(struct seq_file *s, void *p) |
141 | { |
142 | seq_printf(m: s, fmt: "#bitmask: 1=trace, 2=statistics\n" ); |
143 | seq_printf(m: s, fmt: "0x%lx\n" , gru_options); |
144 | return 0; |
145 | } |
146 | |
147 | static ssize_t options_write(struct file *file, const char __user *userbuf, |
148 | size_t count, loff_t *data) |
149 | { |
150 | int ret; |
151 | |
152 | ret = kstrtoul_from_user(s: userbuf, count, base: 0, res: &gru_options); |
153 | if (ret) |
154 | return ret; |
155 | |
156 | return count; |
157 | } |
158 | |
159 | static int cch_seq_show(struct seq_file *file, void *data) |
160 | { |
161 | long gid = *(long *)data; |
162 | int i; |
163 | struct gru_state *gru = GID_TO_GRU(gid); |
164 | struct gru_thread_state *ts; |
165 | const char *mode[] = { "??" , "UPM" , "INTR" , "OS_POLL" }; |
166 | |
167 | if (gid == 0) |
168 | seq_puts(m: file, s: "# gid bid ctx# asid pid cbrs dsbytes mode\n" ); |
169 | if (gru) |
170 | for (i = 0; i < GRU_NUM_CCH; i++) { |
171 | ts = gru->gs_gts[i]; |
172 | if (!ts) |
173 | continue; |
174 | seq_printf(m: file, fmt: " %5d%5d%6d%7d%9d%6d%8d%8s\n" , |
175 | gru->gs_gid, gru->gs_blade_id, i, |
176 | is_kernel_context(gts: ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid, |
177 | is_kernel_context(gts: ts) ? 0 : ts->ts_tgid_owner, |
178 | ts->ts_cbr_au_count * GRU_CBR_AU_SIZE, |
179 | ts->ts_cbr_au_count * GRU_DSR_AU_BYTES, |
180 | mode[ts->ts_user_options & |
181 | GRU_OPT_MISS_MASK]); |
182 | } |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static int gru_seq_show(struct seq_file *file, void *data) |
188 | { |
189 | long gid = *(long *)data, ctxfree, cbrfree, dsrfree; |
190 | struct gru_state *gru = GID_TO_GRU(gid); |
191 | |
192 | if (gid == 0) { |
193 | seq_puts(m: file, s: "# gid nid ctx cbr dsr ctx cbr dsr\n" ); |
194 | seq_puts(m: file, s: "# busy busy busy free free free\n" ); |
195 | } |
196 | if (gru) { |
197 | ctxfree = GRU_NUM_CCH - gru->gs_active_contexts; |
198 | cbrfree = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE; |
199 | dsrfree = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES; |
200 | seq_printf(m: file, fmt: " %5d%5d%7ld%6ld%6ld%8ld%6ld%6ld\n" , |
201 | gru->gs_gid, gru->gs_blade_id, GRU_NUM_CCH - ctxfree, |
202 | GRU_NUM_CBE - cbrfree, GRU_NUM_DSR_BYTES - dsrfree, |
203 | ctxfree, cbrfree, dsrfree); |
204 | } |
205 | |
206 | return 0; |
207 | } |
208 | |
209 | static void seq_stop(struct seq_file *file, void *data) |
210 | { |
211 | } |
212 | |
213 | static void *seq_start(struct seq_file *file, loff_t *gid) |
214 | { |
215 | if (*gid < gru_max_gids) |
216 | return gid; |
217 | return NULL; |
218 | } |
219 | |
220 | static void *seq_next(struct seq_file *file, void *data, loff_t *gid) |
221 | { |
222 | (*gid)++; |
223 | if (*gid < gru_max_gids) |
224 | return gid; |
225 | return NULL; |
226 | } |
227 | |
228 | static const struct seq_operations cch_seq_ops = { |
229 | .start = seq_start, |
230 | .next = seq_next, |
231 | .stop = seq_stop, |
232 | .show = cch_seq_show |
233 | }; |
234 | |
235 | static const struct seq_operations gru_seq_ops = { |
236 | .start = seq_start, |
237 | .next = seq_next, |
238 | .stop = seq_stop, |
239 | .show = gru_seq_show |
240 | }; |
241 | |
242 | static int statistics_open(struct inode *inode, struct file *file) |
243 | { |
244 | return single_open(file, statistics_show, NULL); |
245 | } |
246 | |
247 | static int mcs_statistics_open(struct inode *inode, struct file *file) |
248 | { |
249 | return single_open(file, mcs_statistics_show, NULL); |
250 | } |
251 | |
252 | static int options_open(struct inode *inode, struct file *file) |
253 | { |
254 | return single_open(file, options_show, NULL); |
255 | } |
256 | |
257 | /* *INDENT-OFF* */ |
258 | static const struct proc_ops statistics_proc_ops = { |
259 | .proc_open = statistics_open, |
260 | .proc_read = seq_read, |
261 | .proc_write = statistics_write, |
262 | .proc_lseek = seq_lseek, |
263 | .proc_release = single_release, |
264 | }; |
265 | |
266 | static const struct proc_ops mcs_statistics_proc_ops = { |
267 | .proc_open = mcs_statistics_open, |
268 | .proc_read = seq_read, |
269 | .proc_write = mcs_statistics_write, |
270 | .proc_lseek = seq_lseek, |
271 | .proc_release = single_release, |
272 | }; |
273 | |
274 | static const struct proc_ops options_proc_ops = { |
275 | .proc_open = options_open, |
276 | .proc_read = seq_read, |
277 | .proc_write = options_write, |
278 | .proc_lseek = seq_lseek, |
279 | .proc_release = single_release, |
280 | }; |
281 | |
282 | static struct proc_dir_entry *proc_gru __read_mostly; |
283 | |
284 | int gru_proc_init(void) |
285 | { |
286 | proc_gru = proc_mkdir("sgi_uv/gru" , NULL); |
287 | if (!proc_gru) |
288 | return -1; |
289 | if (!proc_create(name: "statistics" , mode: 0644, parent: proc_gru, proc_ops: &statistics_proc_ops)) |
290 | goto err; |
291 | if (!proc_create(name: "mcs_statistics" , mode: 0644, parent: proc_gru, proc_ops: &mcs_statistics_proc_ops)) |
292 | goto err; |
293 | if (!proc_create(name: "debug_options" , mode: 0644, parent: proc_gru, proc_ops: &options_proc_ops)) |
294 | goto err; |
295 | if (!proc_create_seq("cch_status" , 0444, proc_gru, &cch_seq_ops)) |
296 | goto err; |
297 | if (!proc_create_seq("gru_status" , 0444, proc_gru, &gru_seq_ops)) |
298 | goto err; |
299 | return 0; |
300 | err: |
301 | remove_proc_subtree("sgi_uv/gru" , NULL); |
302 | return -1; |
303 | } |
304 | |
305 | void gru_proc_exit(void) |
306 | { |
307 | remove_proc_subtree("sgi_uv/gru" , NULL); |
308 | } |
309 | |