diff options
author | Nathan Zimmer <nzimmer@sgi.com> | 2013-08-28 19:35:14 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-08-28 22:26:38 -0400 |
commit | 84a78a6504f5c5394a8e558702e5b54131f01d14 (patch) | |
tree | bd56909861d573e35ef940182891fe1eb210fec8 | |
parent | fa8218def1b1a16f0a410e2c1c767b4738cc81fa (diff) |
timer_list: correct the iterator for timer_list
Correct an issue with /proc/timer_list reported by Holger.
When reading from the proc file with a sufficiently small buffer, 2k so
not really that small, there was one could get hung trying to read the
file a chunk at a time.
The timer_list_start function failed to account for the possibility that
the offset was adjusted outside the timer_list_next.
Signed-off-by: Nathan Zimmer <nzimmer@sgi.com>
Reported-by: Holger Hans Peter Freyther <holger@freyther.de>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Berke Durak <berke.durak@xiphos.com>
Cc: Jeff Layton <jlayton@redhat.com>
Tested-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: <stable@vger.kernel.org> # 3.10.x
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | kernel/time/timer_list.c | 41 |
1 files changed, 24 insertions, 17 deletions
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c index 3bdf28323012..61ed862cdd37 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c | |||
@@ -265,10 +265,9 @@ static inline void timer_list_header(struct seq_file *m, u64 now) | |||
265 | static int timer_list_show(struct seq_file *m, void *v) | 265 | static int timer_list_show(struct seq_file *m, void *v) |
266 | { | 266 | { |
267 | struct timer_list_iter *iter = v; | 267 | struct timer_list_iter *iter = v; |
268 | u64 now = ktime_to_ns(ktime_get()); | ||
269 | 268 | ||
270 | if (iter->cpu == -1 && !iter->second_pass) | 269 | if (iter->cpu == -1 && !iter->second_pass) |
271 | timer_list_header(m, now); | 270 | timer_list_header(m, iter->now); |
272 | else if (!iter->second_pass) | 271 | else if (!iter->second_pass) |
273 | print_cpu(m, iter->cpu, iter->now); | 272 | print_cpu(m, iter->cpu, iter->now); |
274 | #ifdef CONFIG_GENERIC_CLOCKEVENTS | 273 | #ifdef CONFIG_GENERIC_CLOCKEVENTS |
@@ -298,33 +297,41 @@ void sysrq_timer_list_show(void) | |||
298 | return; | 297 | return; |
299 | } | 298 | } |
300 | 299 | ||
301 | static void *timer_list_start(struct seq_file *file, loff_t *offset) | 300 | static void *move_iter(struct timer_list_iter *iter, loff_t offset) |
302 | { | 301 | { |
303 | struct timer_list_iter *iter = file->private; | 302 | for (; offset; offset--) { |
304 | 303 | iter->cpu = cpumask_next(iter->cpu, cpu_online_mask); | |
305 | if (!*offset) { | 304 | if (iter->cpu >= nr_cpu_ids) { |
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 | 305 | #ifdef CONFIG_GENERIC_CLOCKEVENTS |
310 | if (!iter->second_pass) { | 306 | if (!iter->second_pass) { |
311 | iter->cpu = -1; | 307 | iter->cpu = -1; |
312 | iter->second_pass = true; | 308 | iter->second_pass = true; |
313 | } else | 309 | } else |
314 | return NULL; | 310 | return NULL; |
315 | #else | 311 | #else |
316 | return NULL; | 312 | return NULL; |
317 | #endif | 313 | #endif |
314 | } | ||
318 | } | 315 | } |
319 | return iter; | 316 | return iter; |
320 | } | 317 | } |
321 | 318 | ||
319 | static void *timer_list_start(struct seq_file *file, loff_t *offset) | ||
320 | { | ||
321 | struct timer_list_iter *iter = file->private; | ||
322 | |||
323 | if (!*offset) | ||
324 | iter->now = ktime_to_ns(ktime_get()); | ||
325 | iter->cpu = -1; | ||
326 | iter->second_pass = false; | ||
327 | return move_iter(iter, *offset); | ||
328 | } | ||
329 | |||
322 | static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset) | 330 | static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset) |
323 | { | 331 | { |
324 | struct timer_list_iter *iter = file->private; | 332 | struct timer_list_iter *iter = file->private; |
325 | iter->cpu = cpumask_next(iter->cpu, cpu_online_mask); | ||
326 | ++*offset; | 333 | ++*offset; |
327 | return timer_list_start(file, offset); | 334 | return move_iter(iter, 1); |
328 | } | 335 | } |
329 | 336 | ||
330 | static void timer_list_stop(struct seq_file *seq, void *v) | 337 | static void timer_list_stop(struct seq_file *seq, void *v) |