Implement `timer` command option (namely, `-e`) that allows filtering out all the non-expired timers. This is useful to verify what CPU is blocked due to looping with interrupts disabled or due to lack of resources to run the vCPU on a hypervisor side. Signed-off-by: Oleksandr Natalenko <oleksandr@xxxxxxxxxx> --- help.c | 1 + kernel.c | 213 ++++++++++++++++++++++++++++++++----------------------- 2 files changed, 125 insertions(+), 89 deletions(-) diff --git a/help.c b/help.c index c0c750f..fa856d1 100644 --- a/help.c +++ b/help.c @@ -2886,6 +2886,7 @@ char *help_timer[] = { " chronological order. In the case of the old-style hrtimers, the", " expiration time is a single value; in the new-style hrtimers, the", " expiration time is a range.", +" -e Display expired timers only.", " -C cpu Restrict the output to one or more CPUs, where multiple cpu[s] can", " be specified, for example, as \"1,3,5\", \"1-3\", or \"1,3,5-7,10\".", "\nEXAMPLES", diff --git a/kernel.c b/kernel.c index 22909d2..afc66da 100644 --- a/kernel.c +++ b/kernel.c @@ -38,24 +38,24 @@ static void display_bh_1(void); static void display_bh_2(void); static void display_bh_3(void); static void display_bh_4(void); -static void dump_hrtimer_data(const ulong *cpus); -static void dump_hrtimer_clock_base(const void *, const int); -static void dump_hrtimer_base(const void *, const int); -static void dump_active_timers(const void *, ulonglong); +static void dump_hrtimer_data(const ulong *, int); +static void dump_hrtimer_clock_base(const void *, const int, int); +static void dump_hrtimer_base(const void *, const int, int); +static void dump_active_timers(const void *, ulonglong, int); static int get_expires_len(const int, const ulong *, const int); -static void print_timer(const void *); +static void print_timer(const void *, ulonglong, int); static ulonglong ktime_to_ns(const void *); -static void dump_timer_data(const ulong *cpus); -static void dump_timer_data_tvec_bases_v1(const ulong *cpus); -static void dump_timer_data_tvec_bases_v2(const ulong *cpus); -static void dump_timer_data_tvec_bases_v3(const ulong *cpus); -static void dump_timer_data_timer_bases(const ulong *cpus); +static void dump_timer_data(const ulong *, int); +static void dump_timer_data_tvec_bases_v1(const ulong *, int); +static void dump_timer_data_tvec_bases_v2(const ulong *, int); +static void dump_timer_data_tvec_bases_v3(const ulong *, int); +static void dump_timer_data_timer_bases(const ulong *, int); struct tv_range; static void init_tv_ranges(struct tv_range *, int, int, int); -static int do_timer_list(ulong,int, ulong *, void *,ulong *,struct tv_range *); -static int do_timer_list_v3(ulong, int, ulong *, void *,ulong *); +static int do_timer_list(ulong,int, ulong *, void *,ulong *,struct tv_range *, ulong, int); +static int do_timer_list_v3(ulong, int, ulong *, void *,ulong *, ulong, int); struct timer_bases_data; -static int do_timer_list_v4(struct timer_bases_data *); +static int do_timer_list_v4(struct timer_bases_data *, ulong, int); static int compare_timer_data(const void *, const void *); static void panic_this_kernel(void); static void dump_waitq(ulong, char *); @@ -7402,18 +7402,24 @@ cmd_timer(void) { int c; int rflag; + int eflag; char *cpuspec; ulong *cpus = NULL; rflag = 0; + eflag = 0; - while ((c = getopt(argcnt, args, "rC:")) != EOF) { + while ((c = getopt(argcnt, args, "reC:")) != EOF) { switch(c) { case 'r': rflag = 1; break; + case 'e': + eflag = 1; + break; + case 'C': cpuspec = optarg; cpus = get_cpumask_buf(); @@ -7430,16 +7436,16 @@ cmd_timer(void) cmd_usage(pc->curcmd, SYNOPSIS); if (rflag) - dump_hrtimer_data(cpus); + dump_hrtimer_data(cpus, eflag); else - dump_timer_data(cpus); + dump_timer_data(cpus, eflag); if (cpus) FREEBUF(cpus); } static void -dump_hrtimer_data(const ulong *cpus) +dump_hrtimer_data(const ulong *cpus, int eflag) { int i, j, k = 0; int hrtimer_max_clock_bases, max_hrtimer_bases; @@ -7487,7 +7493,7 @@ dump_hrtimer_data(const ulong *cpus) fprintf(fp, "\n"); dump_hrtimer_clock_base( (void *)(hrtimer_bases->value) + - kt->__per_cpu_offset[i], j); + kt->__per_cpu_offset[i], j, eflag); } } else { fprintf(fp, "\n"); @@ -7496,7 +7502,7 @@ dump_hrtimer_data(const ulong *cpus) fprintf(fp, "\n"); dump_hrtimer_base( (void *)(hrtimer_bases->value) + - kt->__per_cpu_offset[i], j); + kt->__per_cpu_offset[i], j, eflag); } } } @@ -7506,7 +7512,7 @@ static int expires_len = -1; static int softexpires_len = -1; static void -dump_hrtimer_clock_base(const void *hrtimer_bases, const int num) +dump_hrtimer_clock_base(const void *hrtimer_bases, const int num, int eflag) { void *base; ulonglong current_time, now; @@ -7530,11 +7536,11 @@ dump_hrtimer_clock_base(const void *hrtimer_bases, const int num) offset = ktime_to_ns(base + OFFSET(hrtimer_clock_base_offset)); now = current_time * (1000000000LL / machdep->hz) + offset; - dump_active_timers(base, now); + dump_active_timers(base, now, eflag); } static void -dump_hrtimer_base(const void *hrtimer_bases, const int num) +dump_hrtimer_base(const void *hrtimer_bases, const int num, int eflag) { void *base; ulonglong current_time, now; @@ -7552,11 +7558,11 @@ dump_hrtimer_base(const void *hrtimer_bases, const int num) get_uptime(NULL, ¤t_time); now = current_time * (1000000000LL / machdep->hz); - dump_active_timers(base, now); + dump_active_timers(base, now, eflag); } static void -dump_active_timers(const void *base, ulonglong now) +dump_active_timers(const void *base, ulonglong now, int eflag) { int next, i, t; struct rb_node *curr; @@ -7664,7 +7670,7 @@ next_one: else timer = (void *)(timer_list[t] - OFFSET(hrtimer_node)); - print_timer(timer); + print_timer(timer, now, eflag); } } @@ -7715,7 +7721,7 @@ get_expires_len(const int timer_cnt, const ulong *timer_list, const int getsoft) * print hrtimer and its related information */ static void -print_timer(const void *timer) +print_timer(const void *timer, ulonglong now, int eflag) { ulonglong softexpires, expires; @@ -7724,6 +7730,15 @@ print_timer(const void *timer) char buf2[BUFSIZE]; char buf3[BUFSIZE]; + if (VALID_MEMBER(hrtimer_expires)) + expires = ktime_to_ns(timer + OFFSET(hrtimer_expires)); + else + expires = ktime_to_ns(timer + OFFSET(hrtimer_node) + + OFFSET(timerqueue_node_expires)); + + if (eflag && expires >= now) + return; + /* align information */ fprintf(fp, " "); @@ -7732,11 +7747,6 @@ print_timer(const void *timer) return; } - if (VALID_MEMBER(hrtimer_expires)) - expires = ktime_to_ns(timer + OFFSET(hrtimer_expires)); - else - expires = ktime_to_ns(timer + OFFSET(hrtimer_node) + - OFFSET(timerqueue_node_expires)); if (VALID_MEMBER(hrtimer_softexpires)) { softexpires = ktime_to_ns(timer + OFFSET(hrtimer_softexpires)); @@ -7818,7 +7828,7 @@ struct tv_range { #define TVN (6) static void -dump_timer_data(const ulong *cpus) +dump_timer_data(const ulong *cpus, int eflag) { int i; ulong timer_active; @@ -7839,16 +7849,16 @@ dump_timer_data(const ulong *cpus) struct tv_range tv[TVN]; if (kt->flags2 & TIMER_BASES) { - dump_timer_data_timer_bases(cpus); + dump_timer_data_timer_bases(cpus, eflag); return; } else if (kt->flags2 & TVEC_BASES_V3) { - dump_timer_data_tvec_bases_v3(cpus); + dump_timer_data_tvec_bases_v3(cpus, eflag); return; } else if (kt->flags & TVEC_BASES_V2) { - dump_timer_data_tvec_bases_v2(cpus); + dump_timer_data_tvec_bases_v2(cpus, eflag); return; } else if (kt->flags & TVEC_BASES_V1) { - dump_timer_data_tvec_bases_v1(cpus); + dump_timer_data_tvec_bases_v1(cpus, eflag); return; } @@ -7888,23 +7898,24 @@ dump_timer_data(const ulong *cpus) init_tv_ranges(tv, vec_root_size, vec_size, 0); + get_symbol_data("jiffies", sizeof(ulong), &jiffies); + get_symbol_data("timer_jiffies", sizeof(ulong), &timer_jiffies); + count += do_timer_list(symbol_value("tv1") + OFFSET(timer_vec_root_vec), - vec_root_size, vec, NULL, NULL, tv); + vec_root_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(symbol_value("tv2") + OFFSET(timer_vec_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(symbol_value("tv3") + OFFSET(timer_vec_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); td = (struct timer_data *) GETBUF((count*2) * sizeof(struct timer_data)); tdx = 0; - get_symbol_data("jiffies", sizeof(ulong), &jiffies); - get_symbol_data("timer_jiffies", sizeof(ulong), &timer_jiffies); if (old_timers_exist) get_symbol_data("timer_active", sizeof(ulong), &timer_active); @@ -7916,6 +7927,8 @@ dump_timer_data(const ulong *cpus) if (!(mask & timer_active)) continue; + if (eflag && tp->expires >= jiffies) + continue; td[tdx].address = i; td[tdx].expires = tp->expires; @@ -7926,15 +7939,15 @@ dump_timer_data(const ulong *cpus) } do_timer_list(symbol_value("tv1") + OFFSET(timer_vec_root_vec), - vec_root_size, vec, (void *)td, &highest, tv); + vec_root_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(symbol_value("tv2") + OFFSET(timer_vec_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(symbol_value("tv3") + OFFSET(timer_vec_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); tdx = do_timer_list(symbol_value("tv5") + OFFSET(timer_vec_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); qsort(td, tdx, sizeof(struct timer_data), compare_timer_data); @@ -7951,6 +7964,9 @@ dump_timer_data(const ulong *cpus) mkstring(buf, flen, CENTER|LJUST, "EXPIRES")); for (i = 0; i < tdx; i++) { + if (eflag && td[i].expires >= jiffies) + continue; + fprintf(fp, "%s", mkstring(buf, flen, RJUST|LONG_DEC, MKSTR(td[i].expires))); @@ -7990,7 +8006,7 @@ dump_timer_data(const ulong *cpus) */ static void -dump_timer_data_tvec_bases_v1(const ulong *cpus) +dump_timer_data_tvec_bases_v1(const ulong *cpus, int eflag) { int i, cpu, tdx, flen; struct timer_data *td; @@ -8026,34 +8042,35 @@ next_cpu: init_tv_ranges(tv, vec_root_size, vec_size, cpu); + get_symbol_data("jiffies", sizeof(ulong), &jiffies); + count += do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec), - vec_root_size, vec, NULL, NULL, tv); + vec_root_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(tv[2].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(tv[3].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(tv[4].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(tv[5].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); if (count) td = (struct timer_data *) GETBUF((count*2) * sizeof(struct timer_data)); tdx = 0; highest = 0; - get_symbol_data("jiffies", sizeof(ulong), &jiffies); do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec), - vec_root_size, vec, (void *)td, &highest, tv); + vec_root_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(tv[2].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(tv[3].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(tv[4].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); tdx = do_timer_list(tv[5].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); qsort(td, tdx, sizeof(struct timer_data), compare_timer_data); @@ -8110,7 +8127,7 @@ next_cpu: */ static void -dump_timer_data_tvec_bases_v2(const ulong *cpus) +dump_timer_data_tvec_bases_v2(const ulong *cpus, int eflag) { int i, cpu, tdx, flen; struct timer_data *td; @@ -8168,34 +8185,35 @@ next_cpu: init_tv_ranges(tv, vec_root_size, vec_size, cpu); + get_symbol_data("jiffies", sizeof(ulong), &jiffies); + count += do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec), - vec_root_size, vec, NULL, NULL, tv); + vec_root_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(tv[2].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(tv[3].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(tv[4].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); count += do_timer_list(tv[5].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL, tv); + vec_size, vec, NULL, NULL, tv, jiffies, eflag); if (count) td = (struct timer_data *) GETBUF((count*2) * sizeof(struct timer_data)); tdx = 0; highest = 0; - get_symbol_data("jiffies", sizeof(ulong), &jiffies); do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec), - vec_root_size, vec, (void *)td, &highest, tv); + vec_root_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(tv[2].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(tv[3].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); do_timer_list(tv[4].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); tdx = do_timer_list(tv[5].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest, tv); + vec_size, vec, (void *)td, &highest, tv, jiffies, eflag); qsort(td, tdx, sizeof(struct timer_data), compare_timer_data); @@ -8261,7 +8279,7 @@ next_cpu: * Linux 4.2 timers use new tvec_root, tvec and timer_list structures */ static void -dump_timer_data_tvec_bases_v3(const ulong *cpus) +dump_timer_data_tvec_bases_v3(const ulong *cpus, int eflag) { int i, cpu, tdx, flen; struct timer_data *td; @@ -8313,34 +8331,35 @@ next_cpu: BZERO(tv, sizeof(struct tv_range) * TVN); init_tv_ranges(tv, vec_root_size, vec_size, cpu); + get_symbol_data("jiffies", sizeof(ulong), &jiffies); + count += do_timer_list_v3(tv[1].base + OFFSET(tvec_root_s_vec), - vec_root_size, vec, NULL, NULL); + vec_root_size, vec, NULL, NULL, jiffies, eflag); count += do_timer_list_v3(tv[2].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL); + vec_size, vec, NULL, NULL, jiffies, eflag); count += do_timer_list_v3(tv[3].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL); + vec_size, vec, NULL, NULL, jiffies, eflag); count += do_timer_list_v3(tv[4].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL); + vec_size, vec, NULL, NULL, jiffies, eflag); count += do_timer_list_v3(tv[5].base + OFFSET(tvec_s_vec), - vec_size, vec, NULL, NULL); + vec_size, vec, NULL, NULL, jiffies, eflag); if (count) td = (struct timer_data *) GETBUF((count*2) * sizeof(struct timer_data)); tdx = 0; highest = 0; - get_symbol_data("jiffies", sizeof(ulong), &jiffies); do_timer_list_v3(tv[1].base + OFFSET(tvec_root_s_vec), - vec_root_size, vec, (void *)td, &highest); + vec_root_size, vec, (void *)td, &highest, jiffies, eflag); do_timer_list_v3(tv[2].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest); + vec_size, vec, (void *)td, &highest, jiffies, eflag); do_timer_list_v3(tv[3].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest); + vec_size, vec, (void *)td, &highest, jiffies, eflag); do_timer_list_v3(tv[4].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest); + vec_size, vec, (void *)td, &highest, jiffies, eflag); tdx = do_timer_list_v3(tv[5].base + OFFSET(tvec_s_vec), - vec_size, vec, (void *)td, &highest); + vec_size, vec, (void *)td, &highest, jiffies, eflag); qsort(td, tdx, sizeof(struct timer_data), compare_timer_data); @@ -8506,7 +8525,9 @@ do_timer_list(ulong vec_kvaddr, ulong *vec, void *option, ulong *highest, - struct tv_range *tv) + struct tv_range *tv, + ulong jiffies, + int eflag) { int i, t; int count, tdx; @@ -8577,6 +8598,9 @@ do_timer_list(ulong vec_kvaddr, expires = ULONG(timer_list_buf + OFFSET(timer_list_expires)); + if (eflag && expires >= jiffies) + continue; + function = ULONG(timer_list_buf + OFFSET(timer_list_function)); @@ -8641,6 +8665,9 @@ new_timer_list_format: expires = ULONG(timer_list_buf + OFFSET(timer_list_expires)); + if (eflag && expires >= jiffies) + continue; + function = ULONG(timer_list_buf + OFFSET(timer_list_function)); @@ -8666,7 +8693,9 @@ do_timer_list_v3(ulong vec_kvaddr, int size, ulong *vec, void *option, - ulong *highest) + ulong *highest, + ulong jiffies, + int eflag) { int i, t; int count, tdx; @@ -8725,6 +8754,9 @@ do_timer_list_v3(ulong vec_kvaddr, expires = ULONG(timer_list_buf + OFFSET(timer_list_expires)); + if (eflag && expires >= jiffies) + continue; + function = ULONG(timer_list_buf + OFFSET(timer_list_function)); @@ -8755,7 +8787,7 @@ struct timer_bases_data { }; static int -do_timer_list_v4(struct timer_bases_data *data) +do_timer_list_v4(struct timer_bases_data *data, ulong jiffies, int eflag) { int i, t, timer_cnt, found; struct list_data list_data, *ld; @@ -8810,6 +8842,9 @@ do_timer_list_v4(struct timer_bases_data *data) continue; expires = ULONG(timer_list_buf + OFFSET(timer_list_expires)); + if (eflag && expires >= jiffies) + continue; + function = ULONG(timer_list_buf + OFFSET(timer_list_function)); data->timers[data->cnt].address = timer_list[t]; @@ -8839,7 +8874,7 @@ do_timer_list_v4(struct timer_bases_data *data) * Linux 4.8 timers use new timer_bases[][] */ static void -dump_timer_data_timer_bases(const ulong *cpus) +dump_timer_data_timer_bases(const ulong *cpus, int eflag) { int i, cpu, flen, base, nr_bases, found, display, j = 0; struct syment *sp; @@ -8901,7 +8936,7 @@ next_base: data.cnt = 0; data.timer_base = timer_base; - found = do_timer_list_v4(&data); + found = do_timer_list_v4(&data, jiffies, eflag); qsort(data.timers, found, sizeof(struct timer_data), compare_timer_data); -- 2.22.0 -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility