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