>From 57a10fd74d6040b832d26909017a72fb7330b70a Mon Sep 17 00:00:00 2001 From: zhangyanfei Date: Wed, 11 Jan 2012 10:09:03 +0800 Subject: [PATCH 2/2] Add -S and -c option for irq to dump the kernel irq stats in x86 and x86_64. Signed-off-by: zhangyanfei --- defs.h | 6 ++ help.c | 30 ++++++++-- kernel.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- tools.c | 48 ++++++++++++++++ x86.c | 3 + x86_64.c | 17 ++++++ 6 files changed, 278 insertions(+), 7 deletions(-) diff --git a/defs.h b/defs.h index 6f4d63a..1308df0 100755 --- a/defs.h +++ b/defs.h @@ -815,6 +815,7 @@ struct machdep_table { ulong (*get_task_pgd)(ulong); void (*dump_irq)(int); void (*get_irq_affinity)(int); + void (*show_interrupts)(int, ulong *); void (*get_stack_frame)(struct bt_info *, ulong *, ulong *); ulong (*get_stackbase)(ulong); ulong (*get_stacktop)(ulong); @@ -1185,12 +1186,14 @@ struct offset_table { /* stash of commonly-used offsets */ long block_device_bd_inode; long block_device_bd_list; long block_device_bd_disk; + long irq_desc_t_kstat_irqs; long irq_desc_t_status; long irq_desc_t_handler; long irq_desc_t_chip; long irq_desc_t_action; long irq_desc_t_depth; long irq_desc_t_affinity; + long kernel_stat_irqs; long irqdesc_action; long irqdesc_ctl; long irqdesc_level; @@ -1682,6 +1685,7 @@ struct size_table { /* stash of commonly-used sizes */ long resource; long runqueue; long irq_desc_t; + long kernel_stat; long task_union; long thread_union; long prio_array; @@ -3663,6 +3667,7 @@ int calculate(char *, ulong *, ulonglong *, ulong); int endian_mismatch(char *, char, ulong); uint16_t swap16(uint16_t, int); uint32_t swap32(uint32_t, int); +int make_cpumask(char *, ulong *, int, int *); /* * symbols.c @@ -4051,6 +4056,7 @@ int check_specified_module_tree(char *, char *); int is_system_call(char *, ulong); void generic_dump_irq(int); void generic_get_irq_affinity(int); +void generic_show_interrupts(int, ulong *); int generic_dis_filter(ulong, char *, unsigned int); int kernel_BUG_encoding_bytes(void); void display_sys_stats(void); diff --git a/help.c b/help.c index e37c6e0..9ed2057 100755 --- a/help.c +++ b/help.c @@ -2460,21 +2460,26 @@ NULL char *help_irq[] = { "irq", "IRQ data", -"[[[index ...] | -u] | -d | -b | -s]", +"[[[index ...] | -u | -c cpu ] | -d | -b | -s | -S]", " This command collaborates the data in an irq_desc_t, along with its", " associated hw_interrupt_type and irqaction structure data, into a", " consolidated per-IRQ display. For kernel versions 2.6.37 and later", " the display consists of the irq_desc/irq_data address, its irqaction", " address(es), and the irqaction name strings. Alternatively, the", " intel interrupt descriptor table may be dumped, or bottom half data", -" may be displayed, or cpu affinity for in-use irqs may be displayed.", -" If no index value argument(s) nor any options are entered, the", -" IRQ data for all IRQs will be displayed.\n", +" may be displayed, or cpu affinity for in-use irqs may be displayed,", +" or the kernel irq stats may be displayed. If no index value argument(s)", +" nor any options are entered, the IRQ data for all IRQs will be displayed.\n", " index a valid IRQ index.", -" -u dump data for in-use IRQs only.", +" -u dump data for in-use IRQs only.", +" -c cpu only dump the irq stats of the specified cpu. This option must", +" be specified with -S option. cpu can be specified as \"1,3,5\",", +" \"1-3\", or \"1,3,5-7,10\".", " -d dump the intel interrupt descriptor table.", " -b dump bottom half data.", " -s dump cpu affinity for in-use IRQs.", +" -S dump the kernel irq stats. If no cpu specified, the irq stats", +" of all cpus will be displayed.", "\nEXAMPLES", " Display the relevant data for IRQ 18 from a pre-2.6.37 kernel:\n", " %s> irq 18", @@ -2582,7 +2587,20 @@ char *help_irq[] = { " 85 ioat-msix 0-23", " 86 ioat-msix 0-23", " 87 ioat-msix 0-23", -" 88 eth4 0,17", +" 88 eth4 0,17\n", +" Display the kernel irq stats:\n", +" %s>irq -c 0,2 -S", +" CPU0 CPU2 ", +" 0: 2068161471 0 IR-IO-APIC-edge timer", +" 1: 9 0 IR-IO-APIC-edge i8042", +" 8: 1 0 IR-IO-APIC-edge rtc0", +" 9: 0 0 IR-IO-APIC-fasteoi acpi", +" 16: 36 0 IR-IO-APIC-fasteoi ehci_hcd:usb2", +" ...\n", +" 85: 3 0 IR-PCI-MSI-edge ioat-msix", +" 86: 3 0 IR-PCI-MSI-edge ioat-msix", +" 87: 3 0 IR-PCI-MSI-edge ioat-msix", +" 88: 24 295 IR-PCI-MSI-edge eth4", NULL }; diff --git a/kernel.c b/kernel.c index 4fd940a..bd7f919 100755 --- a/kernel.c +++ b/kernel.c @@ -347,6 +347,9 @@ kernel_init() irq_desc_type_name = "irq_desc"; STRUCT_SIZE_INIT(irq_desc_t, irq_desc_type_name); + if (MEMBER_EXISTS(irq_desc_type_name, "kstat_irqs")) + MEMBER_OFFSET_INIT(irq_desc_t_kstat_irqs, irq_desc_type_name, "kstat_irqs"); + MEMBER_OFFSET_INIT(irq_desc_t_name, irq_desc_type_name, "name"); MEMBER_OFFSET_INIT(irq_desc_t_status, irq_desc_type_name, "status"); if (MEMBER_EXISTS(irq_desc_type_name, "handler")) MEMBER_OFFSET_INIT(irq_desc_t_handler, irq_desc_type_name, "handler"); @@ -355,6 +358,10 @@ kernel_init() MEMBER_OFFSET_INIT(irq_desc_t_action, irq_desc_type_name, "action"); MEMBER_OFFSET_INIT(irq_desc_t_depth, irq_desc_type_name, "depth"); MEMBER_OFFSET_INIT(irq_desc_t_affinity, irq_desc_type_name, "affinity"); + + STRUCT_SIZE_INIT(kernel_stat, "kernel_stat"); + MEMBER_OFFSET_INIT(kernel_stat_irqs, "kernel_stat", "irqs"); + if (STRUCT_EXISTS("hw_interrupt_type")) { MEMBER_OFFSET_INIT(hw_interrupt_type_typename, "hw_interrupt_type", "typename"); @@ -4708,8 +4715,17 @@ cmd_irq(void) { int i, c; int nr_irqs; + ulong *cpus; + int len; + int show_intr, choose_cpu; + char buf[10]; + char arg_buf[BUFSIZE]; - while ((c = getopt(argcnt, args, "dbus")) != EOF) { + cpus = NULL; + show_intr = 0; + choose_cpu = 0; + + while ((c = getopt(argcnt, args, "dbusSc:")) != EOF) { switch(c) { case 'd': @@ -4769,6 +4785,21 @@ cmd_irq(void) return; + case 'S': + show_intr = 1; + break; + + case 'c': + if (choose_cpu) { + error(INFO, "only one -c option allowed\n"); + argerrs++; + } else { + choose_cpu = 1; + BZERO(arg_buf, BUFSIZE); + strncpy(arg_buf, optarg, strlen(optarg)); + } + break; + default: argerrs++; break; @@ -4784,6 +4815,39 @@ cmd_irq(void) if ((nr_irqs = machdep->nr_irqs) == 0) error(FATAL, "cannot determine number of IRQs\n"); + if (show_intr) { + if (!(machine_type("X86") || machine_type("X86_64"))) + command_not_supported(); + + if ((len = STRUCT_SIZE("cpumask_t")) < 0) + len = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong); + cpus = (ulong *)GETBUF(len); + + if (choose_cpu) { + make_cpumask(arg_buf, cpus, FAULT_ON_ERROR, NULL); + } else { + for (i = 0; i < kt->cpus; i++) + SET_BIT(cpus, i); + } + + fprintf(fp, " "); + BZERO(buf, 10); + for (i = 0; i < kt->cpus; i++) { + if (NUM_IN_BITMAP(cpus, i)) { + sprintf(buf, "CPU%d", i); + fprintf(fp, "%10s ", buf); + } + } + fprintf(fp, "\n"); + + for (i = 0; i < nr_irqs; i++) + machdep->show_interrupts(i, cpus); + + if (choose_cpu) + FREEBUF(cpus); + return; + } + if (!args[optind]) { for (i = 0; i < nr_irqs; i++) machdep->dump_irq(i); @@ -5479,6 +5543,121 @@ generic_get_irq_affinity(int irq) FREEBUF(affinity); } +void +generic_show_interrupts(int irq, ulong *cpus) +{ + int i; + ulong irq_desc_addr; + ulong handler, action, name; + uint kstat_irq; + uint kstat_irqs[kt->cpus]; + ulong kstat_irqs_ptr; + struct syment *percpu_sp; + ulong tmp, tmp1; + char buf[BUFSIZE]; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char name_buf[BUFSIZE]; + + handler = UNINITIALIZED; + + irq_desc_addr = get_irq_desc_addr(irq); + if (!irq_desc_addr) + return; + + readmem(irq_desc_addr + OFFSET(irq_desc_t_action), KVADDR, + &action, sizeof(long), "irq_desc action", FAULT_ON_ERROR); + + if (!action) + return; + + if (!symbol_exists("kstat_irqs_cpu")) { /* for RHEL5 or earlier */ + if (!(percpu_sp = per_cpu_symbol_search("kstat"))) + return; + + for (i = 0; i < kt->cpus; i++) { + if (!(NUM_IN_BITMAP(cpus, i))) + continue; + + tmp = percpu_sp->value + kt->__per_cpu_offset[i]; + readmem(tmp + OFFSET(kernel_stat_irqs) + sizeof(uint) * irq, + KVADDR, &kstat_irq, sizeof(uint), + "kernel_stat irqs", FAULT_ON_ERROR); + kstat_irqs[i] = kstat_irq; + } + } else { + readmem(irq_desc_addr + OFFSET(irq_desc_t_kstat_irqs), + KVADDR, &kstat_irqs_ptr, sizeof(long), + "irq_desc kstat_irqs", FAULT_ON_ERROR); + readmem(kstat_irqs_ptr, KVADDR, kstat_irqs, sizeof(kstat_irqs), + "kstat_irqs", FAULT_ON_ERROR); + } + if (VALID_MEMBER(irq_desc_t_handler)) + readmem(irq_desc_addr + OFFSET(irq_desc_t_handler), + KVADDR, &handler, sizeof(long), "irq_desc handler", + FAULT_ON_ERROR); + else if (VALID_MEMBER(irq_desc_t_chip)) + readmem(irq_desc_addr + OFFSET(irq_desc_t_chip), KVADDR, + &handler, sizeof(long), "irq_desc chip", + FAULT_ON_ERROR); + + fprintf(fp, "%3d: ", irq); + + for (i = 0; i < kt->cpus; i++) { + if (NUM_IN_BITMAP(cpus, i)) + fprintf(fp, "%10u ", kstat_irqs[i]); + } + + if (handler) { + if (VALID_MEMBER(hw_interrupt_type_typename)) { + readmem(handler+OFFSET(hw_interrupt_type_typename), + KVADDR, &tmp, sizeof(void *), + "hw_interrupt_type typename", FAULT_ON_ERROR); + + BZERO(buf, BUFSIZE); + if (read_string(tmp, buf, BUFSIZE-1)) + fprintf(fp, "%14s", buf); + } + else if (VALID_MEMBER(irq_chip_typename)) { + readmem(handler+OFFSET(irq_chip_typename), + KVADDR, &tmp, sizeof(void *), + "hw_interrupt_type typename", FAULT_ON_ERROR); + + BZERO(buf, BUFSIZE); + if (read_string(tmp, buf, BUFSIZE-1)) + fprintf(fp, "%8s", buf); + BZERO(buf1, BUFSIZE); + if (VALID_MEMBER(irq_desc_t_name)) + readmem(irq_desc_addr+OFFSET(irq_desc_t_name), + KVADDR, &tmp1, sizeof(void *), + "irq_desc name", FAULT_ON_ERROR); + if (read_string(tmp1, buf1, BUFSIZE-1)) + fprintf(fp, "-%-8s", buf1); + } + } + + BZERO(name_buf, BUFSIZE); + + while (action) { + readmem(action+OFFSET(irqaction_name), KVADDR, + &name, sizeof(void *), + "irqaction name", FAULT_ON_ERROR); + BZERO(buf2, BUFSIZE); + if (read_string(name, buf2, BUFSIZE-1)) { + if (strlen(name_buf) != 0) + strncat(name_buf, ",", 2); + strncat(name_buf, buf2, strlen(buf2)); + } + + readmem(action+OFFSET(irqaction_next), KVADDR, + &action, sizeof(void *), + "irqaction dev_id", FAULT_ON_ERROR); + } + + fprintf(fp, " %-20s ", name_buf); + fprintf(fp, "\n"); +} + /* * Dump the earlier 2.2 Linux version's bottom-half essentials. */ diff --git a/tools.c b/tools.c index 5208181..d8c7045 100755 --- a/tools.c +++ b/tools.c @@ -4841,3 +4841,51 @@ swap32(uint32_t val, int swap) else return val; } + +int +make_cpumask(char *s, ulong *mask, int flags, int *errptr) +{ + char *p, *q; + int start, end; + int i; + + if (s == NULL) { + if (!(flags & QUIET)) + error(INFO, "received NULL string\n"); + goto make_cpumask_error; + } + + p = strtok(s, ","); + while (p) { + s = strtok(NULL, ""); + start = end = -1; + q = strtok(p, "-"); + start = dtoi(q, flags, errptr); + if ((q = strtok(NULL, "-"))) + end = dtoi(q, flags, errptr); + + if (end == -1) + end = start; + + for (i = start; i <= end; i++) + SET_BIT(mask, i); + + p = strtok(s, ","); + } + + return TRUE; + +make_cpumask_error: + switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR)) + { + case FAULT_ON_ERROR: + RESTART(); + + case RETURN_ON_ERROR: + if (errptr) + *errptr = TRUE; + break; + } + + return UNUSED; +} diff --git a/x86.c b/x86.c index da5e5ba..6497186 100755 --- a/x86.c +++ b/x86.c @@ -1802,6 +1802,7 @@ x86_init(int when) machdep->get_task_pgd = x86_get_task_pgd; machdep->dump_irq = generic_dump_irq; machdep->get_irq_affinity = generic_get_irq_affinity; + machdep->show_interrupts = generic_show_interrupts; machdep->get_stack_frame = x86_get_stack_frame; machdep->get_stackbase = generic_get_stackbase; machdep->get_stacktop = generic_get_stacktop; @@ -3405,6 +3406,7 @@ x86_dump_machdep_table(ulong arg) fprintf(fp, " get_task_pgd: x86_get_task_pgd()\n"); fprintf(fp, " dump_irq: generic_dump_irq()\n"); fprintf(fp, " get_irq_affinity: generic_get_irq_affinity()\n"); + fprintf(fp, " show_interrupts: generic_show_interrupts()\n"); fprintf(fp, " get_stack_frame: x86_get_stack_frame()\n"); fprintf(fp, " get_stackbase: generic_get_stackbase()\n"); fprintf(fp, " get_stacktop: generic_get_stacktop()\n"); @@ -5343,6 +5345,7 @@ x86_init_hyper(int when) machdep->processor_speed = x86_processor_speed; /* ODA: check */ machdep->dump_irq = generic_dump_irq; /* ODA: check */ machdep->get_irq_affinity = generic_get_irq_affinity; + machdep->show_interrupts = generic_show_interrupts; machdep->get_stack_frame = x86_get_stack_frame_hyper; machdep->get_stackbase = x86_get_stackbase_hyper; machdep->get_stacktop = x86_get_stacktop_hyper; diff --git a/x86_64.c b/x86_64.c index 536f256..a9ec5f5 100755 --- a/x86_64.c +++ b/x86_64.c @@ -57,6 +57,7 @@ static void x86_64_display_full_frame(struct bt_info *, ulong, FILE *); static void x86_64_do_bt_reference_check(struct bt_info *, ulong,char *); static void x86_64_dump_irq(int); static void x86_64_get_irq_affinity(int); +static void x86_64_show_interrupts(int, ulong *); static char *x86_64_extract_idt_function(ulong *, char *, ulong *); static ulong x86_64_get_pc(struct bt_info *); static ulong x86_64_get_sp(struct bt_info *); @@ -473,6 +474,7 @@ x86_64_init(int when) machdep->nr_irqs = 224; /* NR_IRQS (at least) */ machdep->dump_irq = x86_64_dump_irq; machdep->get_irq_affinity = x86_64_get_irq_affinity; + machdep->show_interrupts = x86_64_show_interrupts; if (!machdep->hz) { machdep->hz = HZ; if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) @@ -634,6 +636,7 @@ x86_64_dump_machdep_table(ulong arg) fprintf(fp, " get_task_pgd: x86_64_get_task_pgd()\n"); fprintf(fp, " dump_irq: x86_64_dump_irq()\n"); fprintf(fp, " get_irq_affinity: x86_64_get_irq_affinity()\n"); + fprintf(fp, " show_interrupts: x86_64_show_interrupts()\n"); fprintf(fp, " get_stack_frame: x86_64_get_stack_frame()\n"); fprintf(fp, " get_stackbase: generic_get_stackbase()\n"); fprintf(fp, " get_stacktop: generic_get_stacktop()\n"); @@ -4553,6 +4556,20 @@ x86_64_get_irq_affinity(int irq) "x86_64_get_irq_affinity: irq_desc[] or irq_desc_tree do not exist?\n"); } +static void +x86_64_show_interrupts(int irq, ulong *cpus) +{ + if (symbol_exists("irq_desc") || + kernel_symbol_exists("irq_desc_ptrs") || + kernel_symbol_exists("irq_desc_tree")) { + machdep->show_interrupts = generic_show_interrupts; + return(generic_show_interrupts(irq, cpus)); + } + + error(FATAL, + "x86_64_show_interrupts: irq_desc[] or irq_desc_tree do not exist?\n"); +} + /* * Do the work for irq -d */ -- 1.7.1