diff options
author | Nathan Zimmer <nzimmer@sgi.com> | 2013-03-26 20:56:30 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2013-04-17 14:51:02 -0400 |
commit | b3956a896ea57f25cacd74708b8fab611543a81d (patch) | |
tree | b80f2805317d0faa493086d89abaaccfe6143a69 | |
parent | 60cf7ea849e77c8782dee147cfb8c38d1984236e (diff) |
timer_list: Convert timer list to be a proper seq_file
When running with 4096 cores attemping to read /proc/timer_list will fail
with an ENOMEM condition. On a sufficantly large systems the total amount
of data is more then 4mb, so it won't fit into a single buffer. The
failure can also occur on smaller systems when memory fragmentation is
high as reported by Dave Jones.
Convert /proc/timer_list to a proper seq_file with its own iterator. This
is a little more complex given that we have to make two passes with two
separate headers.
sysrq_timer_list_show also needed to be updated to reflect the fact that
now timer_list_show only does one cpu at at time.
Signed-off-by: Nathan Zimmer <nzimmer@sgi.com>
Reported-by: Dave Jones <davej@redhat.com>
Cc: John Stultz <johnstul@us.ibm.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Link: http://lkml.kernel.org/r/1364345790-14577-3-git-send-email-nzimmer@sgi.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | kernel/time/timer_list.c | 89 |
1 files changed, 77 insertions, 12 deletions
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c index 380a58977490..3bdf28323012 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c | |||
@@ -20,6 +20,13 @@ | |||
20 | 20 | ||
21 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
22 | 22 | ||
23 | |||
24 | struct timer_list_iter { | ||
25 | int cpu; | ||
26 | bool second_pass; | ||
27 | u64 now; | ||
28 | }; | ||
29 | |||
23 | typedef void (*print_fn_t)(struct seq_file *m, unsigned int *classes); | 30 | typedef void (*print_fn_t)(struct seq_file *m, unsigned int *classes); |
24 | 31 | ||
25 | DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases); | 32 | DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases); |
@@ -247,43 +254,101 @@ static void timer_list_show_tickdevices_header(struct seq_file *m) | |||
247 | } | 254 | } |
248 | #endif | 255 | #endif |
249 | 256 | ||
250 | static int timer_list_show(struct seq_file *m, void *v) | 257 | static inline void timer_list_header(struct seq_file *m, u64 now) |
251 | { | 258 | { |
252 | u64 now = ktime_to_ns(ktime_get()); | ||
253 | int cpu; | ||
254 | |||
255 | SEQ_printf(m, "Timer List Version: v0.7\n"); | 259 | SEQ_printf(m, "Timer List Version: v0.7\n"); |
256 | SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES); | 260 | SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES); |
257 | SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now); | 261 | SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now); |
258 | SEQ_printf(m, "\n"); | 262 | SEQ_printf(m, "\n"); |
263 | } | ||
264 | |||
265 | static int timer_list_show(struct seq_file *m, void *v) | ||
266 | { | ||
267 | struct timer_list_iter *iter = v; | ||
268 | u64 now = ktime_to_ns(ktime_get()); | ||
269 | |||
270 | if (iter->cpu == -1 && !iter->second_pass) | ||
271 | timer_list_header(m, now); | ||
272 | else if (!iter->second_pass) | ||
273 | print_cpu(m, iter->cpu, iter->now); | ||
274 | #ifdef CONFIG_GENERIC_CLOCKEVENTS | ||
275 | else if (iter->cpu == -1 && iter->second_pass) | ||
276 | timer_list_show_tickdevices_header(m); | ||
277 | else | ||
278 | print_tickdevice(m, tick_get_device(iter->cpu), iter->cpu); | ||
279 | #endif | ||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | void sysrq_timer_list_show(void) | ||
284 | { | ||
285 | u64 now = ktime_to_ns(ktime_get()); | ||
286 | int cpu; | ||
287 | |||
288 | timer_list_header(NULL, now); | ||
259 | 289 | ||
260 | for_each_online_cpu(cpu) | 290 | for_each_online_cpu(cpu) |
261 | print_cpu(m, cpu, now); | 291 | print_cpu(NULL, cpu, now); |
262 | 292 | ||
263 | #ifdef CONFIG_GENERIC_CLOCKEVENTS | 293 | #ifdef CONFIG_GENERIC_CLOCKEVENTS |
264 | timer_list_show_tickdevices_header(m); | 294 | timer_list_show_tickdevices_header(NULL); |
265 | for_each_online_cpu(cpu) | 295 | for_each_online_cpu(cpu) |
266 | print_tickdevice(m, tick_get_device(cpu), cpu); | 296 | print_tickdevice(NULL, tick_get_device(cpu), cpu); |
267 | #endif | 297 | #endif |
298 | return; | ||
299 | } | ||
268 | 300 | ||
269 | return 0; | 301 | static void *timer_list_start(struct seq_file *file, loff_t *offset) |
302 | { | ||
303 | struct timer_list_iter *iter = file->private; | ||
304 | |||
305 | if (!*offset) { | ||
306 | iter->cpu = -1; | ||
307 | iter->now = ktime_to_ns(ktime_get()); | ||
308 | } else if (iter->cpu >= nr_cpu_ids) { | ||
309 | #ifdef CONFIG_GENERIC_CLOCKEVENTS | ||
310 | if (!iter->second_pass) { | ||
311 | iter->cpu = -1; | ||
312 | iter->second_pass = true; | ||
313 | } else | ||
314 | return NULL; | ||
315 | #else | ||
316 | return NULL; | ||
317 | #endif | ||
318 | } | ||
319 | return iter; | ||
270 | } | 320 | } |
271 | 321 | ||
272 | void sysrq_timer_list_show(void) | 322 | static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset) |
273 | { | 323 | { |
274 | timer_list_show(NULL, NULL); | 324 | struct timer_list_iter *iter = file->private; |
325 | iter->cpu = cpumask_next(iter->cpu, cpu_online_mask); | ||
326 | ++*offset; | ||
327 | return timer_list_start(file, offset); | ||
275 | } | 328 | } |
276 | 329 | ||
330 | static void timer_list_stop(struct seq_file *seq, void *v) | ||
331 | { | ||
332 | } | ||
333 | |||
334 | static const struct seq_operations timer_list_sops = { | ||
335 | .start = timer_list_start, | ||
336 | .next = timer_list_next, | ||
337 | .stop = timer_list_stop, | ||
338 | .show = timer_list_show, | ||
339 | }; | ||
340 | |||
277 | static int timer_list_open(struct inode *inode, struct file *filp) | 341 | static int timer_list_open(struct inode *inode, struct file *filp) |
278 | { | 342 | { |
279 | return single_open(filp, timer_list_show, NULL); | 343 | return seq_open_private(filp, &timer_list_sops, |
344 | sizeof(struct timer_list_iter)); | ||
280 | } | 345 | } |
281 | 346 | ||
282 | static const struct file_operations timer_list_fops = { | 347 | static const struct file_operations timer_list_fops = { |
283 | .open = timer_list_open, | 348 | .open = timer_list_open, |
284 | .read = seq_read, | 349 | .read = seq_read, |
285 | .llseek = seq_lseek, | 350 | .llseek = seq_lseek, |
286 | .release = single_release, | 351 | .release = seq_release_private, |
287 | }; | 352 | }; |
288 | 353 | ||
289 | static int __init init_timer_list_procfs(void) | 354 | static int __init init_timer_list_procfs(void) |