Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * List pending timers
4 : : *
5 : : * Copyright(C) 2006, Red Hat, Inc., Ingo Molnar
6 : : */
7 : :
8 : : #include <linux/proc_fs.h>
9 : : #include <linux/module.h>
10 : : #include <linux/spinlock.h>
11 : : #include <linux/sched.h>
12 : : #include <linux/seq_file.h>
13 : : #include <linux/kallsyms.h>
14 : : #include <linux/nmi.h>
15 : :
16 : : #include <linux/uaccess.h>
17 : :
18 : : #include "tick-internal.h"
19 : :
20 : : struct timer_list_iter {
21 : : int cpu;
22 : : bool second_pass;
23 : : u64 now;
24 : : };
25 : :
26 : : /*
27 : : * This allows printing both to /proc/timer_list and
28 : : * to the console (on SysRq-Q):
29 : : */
30 : : __printf(2, 3)
31 : 0 : static void SEQ_printf(struct seq_file *m, const char *fmt, ...)
32 : : {
33 : 0 : va_list args;
34 : :
35 : 0 : va_start(args, fmt);
36 : :
37 [ # # ]: 0 : if (m)
38 : 0 : seq_vprintf(m, fmt, args);
39 : : else
40 : 0 : vprintk(fmt, args);
41 : :
42 : 0 : va_end(args);
43 : 0 : }
44 : :
45 : 0 : static void print_name_offset(struct seq_file *m, void *sym)
46 : : {
47 : 0 : char symname[KSYM_NAME_LEN];
48 : :
49 [ # # ]: 0 : if (lookup_symbol_name((unsigned long)sym, symname) < 0)
50 : 0 : SEQ_printf(m, "<%pK>", sym);
51 : : else
52 : 0 : SEQ_printf(m, "%s", symname);
53 : 0 : }
54 : :
55 : : static void
56 : 0 : print_timer(struct seq_file *m, struct hrtimer *taddr, struct hrtimer *timer,
57 : : int idx, u64 now)
58 : : {
59 : 0 : SEQ_printf(m, " #%d: ", idx);
60 : 0 : print_name_offset(m, taddr);
61 : 0 : SEQ_printf(m, ", ");
62 : 0 : print_name_offset(m, timer->function);
63 : 0 : SEQ_printf(m, ", S:%02x", timer->state);
64 : 0 : SEQ_printf(m, "\n");
65 : 0 : SEQ_printf(m, " # expires at %Lu-%Lu nsecs [in %Ld to %Ld nsecs]\n",
66 : : (unsigned long long)ktime_to_ns(hrtimer_get_softexpires(timer)),
67 : : (unsigned long long)ktime_to_ns(hrtimer_get_expires(timer)),
68 : 0 : (long long)(ktime_to_ns(hrtimer_get_softexpires(timer)) - now),
69 : 0 : (long long)(ktime_to_ns(hrtimer_get_expires(timer)) - now));
70 : 0 : }
71 : :
72 : : static void
73 : 0 : print_active_timers(struct seq_file *m, struct hrtimer_clock_base *base,
74 : : u64 now)
75 : : {
76 : 0 : struct hrtimer *timer, tmp;
77 : 0 : unsigned long next = 0, i;
78 : 0 : struct timerqueue_node *curr;
79 : 0 : unsigned long flags;
80 : :
81 : 0 : next_one:
82 : 0 : i = 0;
83 : :
84 : 0 : touch_nmi_watchdog();
85 : :
86 : 0 : raw_spin_lock_irqsave(&base->cpu_base->lock, flags);
87 : :
88 : 0 : curr = timerqueue_getnext(&base->active);
89 : : /*
90 : : * Crude but we have to do this O(N*N) thing, because
91 : : * we have to unlock the base when printing:
92 : : */
93 [ # # ]: 0 : while (curr && i < next) {
94 : 0 : curr = timerqueue_iterate_next(curr);
95 : 0 : i++;
96 : : }
97 : :
98 [ # # ]: 0 : if (curr) {
99 : :
100 : 0 : timer = container_of(curr, struct hrtimer, node);
101 : 0 : tmp = *timer;
102 : 0 : raw_spin_unlock_irqrestore(&base->cpu_base->lock, flags);
103 : :
104 : 0 : print_timer(m, timer, &tmp, i, now);
105 : 0 : next++;
106 : 0 : goto next_one;
107 : : }
108 : 0 : raw_spin_unlock_irqrestore(&base->cpu_base->lock, flags);
109 : 0 : }
110 : :
111 : : static void
112 : 0 : print_base(struct seq_file *m, struct hrtimer_clock_base *base, u64 now)
113 : : {
114 : 0 : SEQ_printf(m, " .base: %pK\n", base);
115 : 0 : SEQ_printf(m, " .index: %d\n", base->index);
116 : :
117 : 0 : SEQ_printf(m, " .resolution: %u nsecs\n", hrtimer_resolution);
118 : :
119 : 0 : SEQ_printf(m, " .get_time: ");
120 : 0 : print_name_offset(m, base->get_time);
121 : 0 : SEQ_printf(m, "\n");
122 : : #ifdef CONFIG_HIGH_RES_TIMERS
123 : 0 : SEQ_printf(m, " .offset: %Lu nsecs\n",
124 : 0 : (unsigned long long) ktime_to_ns(base->offset));
125 : : #endif
126 : 0 : SEQ_printf(m, "active timers:\n");
127 : 0 : print_active_timers(m, base, now + ktime_to_ns(base->offset));
128 : 0 : }
129 : :
130 : 0 : static void print_cpu(struct seq_file *m, int cpu, u64 now)
131 : : {
132 : 0 : struct hrtimer_cpu_base *cpu_base = &per_cpu(hrtimer_bases, cpu);
133 : 0 : int i;
134 : :
135 : 0 : SEQ_printf(m, "cpu: %d\n", cpu);
136 [ # # ]: 0 : for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
137 : 0 : SEQ_printf(m, " clock %d:\n", i);
138 : 0 : print_base(m, cpu_base->clock_base + i, now);
139 : : }
140 : : #define P(x) \
141 : : SEQ_printf(m, " .%-15s: %Lu\n", #x, \
142 : : (unsigned long long)(cpu_base->x))
143 : : #define P_ns(x) \
144 : : SEQ_printf(m, " .%-15s: %Lu nsecs\n", #x, \
145 : : (unsigned long long)(ktime_to_ns(cpu_base->x)))
146 : :
147 : : #ifdef CONFIG_HIGH_RES_TIMERS
148 : 0 : P_ns(expires_next);
149 : 0 : P(hres_active);
150 : 0 : P(nr_events);
151 : 0 : P(nr_retries);
152 : 0 : P(nr_hangs);
153 : 0 : P(max_hang_time);
154 : : #endif
155 : : #undef P
156 : : #undef P_ns
157 : :
158 : : #ifdef CONFIG_TICK_ONESHOT
159 : : # define P(x) \
160 : : SEQ_printf(m, " .%-15s: %Lu\n", #x, \
161 : : (unsigned long long)(ts->x))
162 : : # define P_ns(x) \
163 : : SEQ_printf(m, " .%-15s: %Lu nsecs\n", #x, \
164 : : (unsigned long long)(ktime_to_ns(ts->x)))
165 : : {
166 : 0 : struct tick_sched *ts = tick_get_tick_sched(cpu);
167 : 0 : P(nohz_mode);
168 : 0 : P_ns(last_tick);
169 : 0 : P(tick_stopped);
170 : 0 : P(idle_jiffies);
171 : 0 : P(idle_calls);
172 : 0 : P(idle_sleeps);
173 : 0 : P_ns(idle_entrytime);
174 : 0 : P_ns(idle_waketime);
175 : 0 : P_ns(idle_exittime);
176 : 0 : P_ns(idle_sleeptime);
177 : 0 : P_ns(iowait_sleeptime);
178 : 0 : P(last_jiffies);
179 : 0 : P(next_timer);
180 : 0 : P_ns(idle_expires);
181 : 0 : SEQ_printf(m, "jiffies: %Lu\n",
182 : : (unsigned long long)jiffies);
183 : : }
184 : : #endif
185 : :
186 : : #undef P
187 : : #undef P_ns
188 : 0 : SEQ_printf(m, "\n");
189 : 0 : }
190 : :
191 : : #ifdef CONFIG_GENERIC_CLOCKEVENTS
192 : : static void
193 : : print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
194 : : {
195 : : struct clock_event_device *dev = td->evtdev;
196 : :
197 : : touch_nmi_watchdog();
198 : :
199 : : SEQ_printf(m, "Tick Device: mode: %d\n", td->mode);
200 : : if (cpu < 0)
201 : : SEQ_printf(m, "Broadcast device\n");
202 : : else
203 : : SEQ_printf(m, "Per CPU device: %d\n", cpu);
204 : :
205 : : SEQ_printf(m, "Clock Event Device: ");
206 : : if (!dev) {
207 : : SEQ_printf(m, "<NULL>\n");
208 : : return;
209 : : }
210 : : SEQ_printf(m, "%s\n", dev->name);
211 : : SEQ_printf(m, " max_delta_ns: %llu\n",
212 : : (unsigned long long) dev->max_delta_ns);
213 : : SEQ_printf(m, " min_delta_ns: %llu\n",
214 : : (unsigned long long) dev->min_delta_ns);
215 : : SEQ_printf(m, " mult: %u\n", dev->mult);
216 : : SEQ_printf(m, " shift: %u\n", dev->shift);
217 : : SEQ_printf(m, " mode: %d\n", clockevent_get_state(dev));
218 : : SEQ_printf(m, " next_event: %Ld nsecs\n",
219 : : (unsigned long long) ktime_to_ns(dev->next_event));
220 : :
221 : : SEQ_printf(m, " set_next_event: ");
222 : : print_name_offset(m, dev->set_next_event);
223 : : SEQ_printf(m, "\n");
224 : :
225 : : if (dev->set_state_shutdown) {
226 : : SEQ_printf(m, " shutdown: ");
227 : : print_name_offset(m, dev->set_state_shutdown);
228 : : SEQ_printf(m, "\n");
229 : : }
230 : :
231 : : if (dev->set_state_periodic) {
232 : : SEQ_printf(m, " periodic: ");
233 : : print_name_offset(m, dev->set_state_periodic);
234 : : SEQ_printf(m, "\n");
235 : : }
236 : :
237 : : if (dev->set_state_oneshot) {
238 : : SEQ_printf(m, " oneshot: ");
239 : : print_name_offset(m, dev->set_state_oneshot);
240 : : SEQ_printf(m, "\n");
241 : : }
242 : :
243 : : if (dev->set_state_oneshot_stopped) {
244 : : SEQ_printf(m, " oneshot stopped: ");
245 : : print_name_offset(m, dev->set_state_oneshot_stopped);
246 : : SEQ_printf(m, "\n");
247 : : }
248 : :
249 : : if (dev->tick_resume) {
250 : : SEQ_printf(m, " resume: ");
251 : : print_name_offset(m, dev->tick_resume);
252 : : SEQ_printf(m, "\n");
253 : : }
254 : :
255 : : SEQ_printf(m, " event_handler: ");
256 : : print_name_offset(m, dev->event_handler);
257 : : SEQ_printf(m, "\n");
258 : : SEQ_printf(m, " retries: %lu\n", dev->retries);
259 : : SEQ_printf(m, "\n");
260 : : }
261 : :
262 : 0 : static void timer_list_show_tickdevices_header(struct seq_file *m)
263 : : {
264 : : #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
265 : 0 : print_tickdevice(m, tick_get_broadcast_device(), -1);
266 : 0 : SEQ_printf(m, "tick_broadcast_mask: %*pb\n",
267 : 0 : cpumask_pr_args(tick_get_broadcast_mask()));
268 : : #ifdef CONFIG_TICK_ONESHOT
269 : 0 : SEQ_printf(m, "tick_broadcast_oneshot_mask: %*pb\n",
270 : 0 : cpumask_pr_args(tick_get_broadcast_oneshot_mask()));
271 : : #endif
272 : 0 : SEQ_printf(m, "\n");
273 : : #endif
274 : 0 : }
275 : : #endif
276 : :
277 : 0 : static inline void timer_list_header(struct seq_file *m, u64 now)
278 : : {
279 : 0 : SEQ_printf(m, "Timer List Version: v0.8\n");
280 : 0 : SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
281 : 0 : SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
282 : 0 : SEQ_printf(m, "\n");
283 : 0 : }
284 : :
285 : 0 : void sysrq_timer_list_show(void)
286 : : {
287 : 0 : u64 now = ktime_to_ns(ktime_get());
288 : 0 : int cpu;
289 : :
290 : 0 : timer_list_header(NULL, now);
291 : :
292 [ # # ]: 0 : for_each_online_cpu(cpu)
293 : 0 : print_cpu(NULL, cpu, now);
294 : :
295 : : #ifdef CONFIG_GENERIC_CLOCKEVENTS
296 : 0 : timer_list_show_tickdevices_header(NULL);
297 [ # # ]: 0 : for_each_online_cpu(cpu)
298 : 0 : print_tickdevice(NULL, tick_get_device(cpu), cpu);
299 : : #endif
300 : 0 : return;
301 : : }
302 : :
303 : : #ifdef CONFIG_PROC_FS
304 : 0 : static int timer_list_show(struct seq_file *m, void *v)
305 : : {
306 : 0 : struct timer_list_iter *iter = v;
307 : :
308 [ # # # # ]: 0 : if (iter->cpu == -1 && !iter->second_pass)
309 : 0 : timer_list_header(m, iter->now);
310 [ # # ]: 0 : else if (!iter->second_pass)
311 : 0 : print_cpu(m, iter->cpu, iter->now);
312 : : #ifdef CONFIG_GENERIC_CLOCKEVENTS
313 [ # # ]: 0 : else if (iter->cpu == -1 && iter->second_pass)
314 : 0 : timer_list_show_tickdevices_header(m);
315 : : else
316 : 0 : print_tickdevice(m, tick_get_device(iter->cpu), iter->cpu);
317 : : #endif
318 : 0 : return 0;
319 : : }
320 : :
321 : 0 : static void *move_iter(struct timer_list_iter *iter, loff_t offset)
322 : : {
323 [ # # ]: 0 : for (; offset; offset--) {
324 : 0 : iter->cpu = cpumask_next(iter->cpu, cpu_online_mask);
325 [ # # ]: 0 : if (iter->cpu >= nr_cpu_ids) {
326 : : #ifdef CONFIG_GENERIC_CLOCKEVENTS
327 [ # # ]: 0 : if (!iter->second_pass) {
328 : 0 : iter->cpu = -1;
329 : 0 : iter->second_pass = true;
330 : : } else
331 : : return NULL;
332 : : #else
333 : : return NULL;
334 : : #endif
335 : : }
336 : : }
337 : : return iter;
338 : : }
339 : :
340 : 0 : static void *timer_list_start(struct seq_file *file, loff_t *offset)
341 : : {
342 : 0 : struct timer_list_iter *iter = file->private;
343 : :
344 [ # # ]: 0 : if (!*offset)
345 : 0 : iter->now = ktime_to_ns(ktime_get());
346 : 0 : iter->cpu = -1;
347 : 0 : iter->second_pass = false;
348 : 0 : return move_iter(iter, *offset);
349 : : }
350 : :
351 : 0 : static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset)
352 : : {
353 : 0 : struct timer_list_iter *iter = file->private;
354 : 0 : ++*offset;
355 : 0 : return move_iter(iter, 1);
356 : : }
357 : :
358 : 0 : static void timer_list_stop(struct seq_file *seq, void *v)
359 : : {
360 : 0 : }
361 : :
362 : : static const struct seq_operations timer_list_sops = {
363 : : .start = timer_list_start,
364 : : .next = timer_list_next,
365 : : .stop = timer_list_stop,
366 : : .show = timer_list_show,
367 : : };
368 : :
369 : 78 : static int __init init_timer_list_procfs(void)
370 : : {
371 : 78 : struct proc_dir_entry *pe;
372 : :
373 : 78 : pe = proc_create_seq_private("timer_list", 0400, NULL, &timer_list_sops,
374 : : sizeof(struct timer_list_iter), NULL);
375 [ - + ]: 78 : if (!pe)
376 : 0 : return -ENOMEM;
377 : : return 0;
378 : : }
379 : : __initcall(init_timer_list_procfs);
380 : : #endif
|