Hi, Dave When we investigate the problems of disk I/O, we want to get the disk's gendisk address and request queue's address easily, and the requests num is also important. Tha attached patch introduce a new command diskio to display such information. Thanks Wen Congyang
>From cc52aa5c945350e0aa6bd3877fd3d1d00c163a30 Mon Sep 17 00:00:00 2001 From: Wen Congyang <wency@xxxxxxxxxxxxxx> Date: Fri, 20 Jan 2012 08:54:50 +0800 Subject: [PATCH] implement new command diskio Signed-off-by: Wen Congyang <wency@xxxxxxxxxxxxxx> --- defs.h | 27 ++++ global_data.c | 1 + help.c | 24 +++ kernel.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 506 insertions(+), 1 deletions(-) diff --git a/defs.h b/defs.h index 82d51e5..f95f443 100644 --- a/defs.h +++ b/defs.h @@ -1631,6 +1631,27 @@ struct offset_table { /* stash of commonly-used offsets */ long sched_entity_my_q; long sched_entity_on_rq; long task_struct_on_rq; + + long class_devices; + long class_p; + long class_private_devices; + long device_knode_class; + long device_node; + long gendisk_dev; + long gendisk_kobj; + long gendisk_part0; + long gendisk_queue; + long hd_struct_dev; + long klist_k_list; + long klist_node_n_klist; + long klist_node_n_node; + long kobject_entry; + long kset_list; + long request_list_count; + long request_queue_in_flight; + long request_queue_rq; + long subsys_private_klist_devices; + long subsystem_kset; }; struct size_table { /* stash of commonly-used sizes */ @@ -1752,6 +1773,10 @@ struct size_table { /* stash of commonly-used sizes */ long s390_stack_frame; long percpu_data; long sched_entity; + long subsystem; + long class_private; + long rq_in_flight; + long class_private_devices; }; struct array_table { @@ -3472,6 +3497,7 @@ void cmd_kmem(void); /* memory.c */ void cmd_search(void); /* memory.c */ void cmd_swap(void); /* memory.c */ void cmd_pte(void); /* memory.c */ +void cmd_diskio(void); /* memory.c */ void cmd_ps(void); /* task.c */ void cmd_task(void); /* task.c */ void cmd_foreach(void); /* task.c */ @@ -3933,6 +3959,7 @@ extern char *help_bt[]; extern char *help_btop[]; extern char *help_dev[]; extern char *help_dis[]; +extern char *help_diskio[]; extern char *help_eval[]; extern char *help_exit[]; extern char *help_extend[]; diff --git a/global_data.c b/global_data.c index 98a5a79..2e7228d 100644 --- a/global_data.c +++ b/global_data.c @@ -76,6 +76,7 @@ struct command_table_entry linux_command_table[] = { {"btop", cmd_btop, help_btop, 0}, {"dev", cmd_dev, help_dev, 0}, {"dis", cmd_dis, help_dis, 0}, + {"diskio", cmd_diskio, help_diskio, 0}, {"eval", cmd_eval, help_eval, 0}, {"exit", cmd_quit, help_exit, 0}, {"extend", cmd_extend, help_extend, 0}, diff --git a/help.c b/help.c index adaaea7..d94ca19 100644 --- a/help.c +++ b/help.c @@ -5423,6 +5423,30 @@ NULL }; +char *help_diskio[] = { +"diskio", +"disk I/O statistics", +" ", +" This command dumps I/O statistics of all disks:", +" TOTAL: The total requests that have not been ended", +" READ: The total read requests that have not been ended", +" WRITE: The total write requests that have not been ended", +" DRV: The total requests that have been in the driver, but not end", +"", +" Note: some kernel does not contain read/write requests, and the command", +" will output '-----'" +"\nEXAMPLES", +" %s> diskio", +" MAJOR GENDISK NAME RUQUEST QUEUE TOTAL READ WRITE DRV", +" 008 0xe00000010773ea80 sda 0x3000000109c9fbf0 12 0 12 0", +" 008 0xe00000010773e680 sdb 0x3000000109c9f8a0 2 2 0 0", +" 008 0xe000000107781d80 sdc 0x300000010c268050 6 0 6 6", +" 008 0xe00000010773e080 sdd 0x300000010c26bbf0 0 0 0 0", +" 008 0xe00000010773dc80 sde 0x300000010c3dd780 0 0 0 0", +NULL +}; + + /* * Find out what the help request is looking for, accepting aliases as well, * and print the relevant strings from the appropriate help table. diff --git a/kernel.c b/kernel.c index dccef2c..12d0aea 100644 --- a/kernel.c +++ b/kernel.c @@ -582,7 +582,41 @@ kernel_init() STRUCT_SIZE_INIT(mem_section, "mem_section"); BUG_bytes_init(); - + + MEMBER_OFFSET_INIT(class_devices, "class", "class_devices"); + if (INVALID_MEMBER(class_devices)) + MEMBER_OFFSET_INIT(class_devices, "class", "devices"); + MEMBER_OFFSET_INIT(class_p, "class", "p"); + MEMBER_OFFSET_INIT(class_private_devices, "class_private", + "class_devices"); + MEMBER_OFFSET_INIT(device_knode_class, "device", "knode_class"); + MEMBER_OFFSET_INIT(device_node, "device", "node"); + MEMBER_OFFSET_INIT(device_type, "device", "type"); + MEMBER_OFFSET_INIT(gendisk_dev, "gendisk", "dev"); + if (INVALID_MEMBER(gendisk_dev)) + MEMBER_OFFSET_INIT(gendisk_dev, "gendisk", "__dev"); + MEMBER_OFFSET_INIT(gendisk_kobj, "gendisk", "kobj"); + MEMBER_OFFSET_INIT(gendisk_part0, "gendisk", "part0"); + MEMBER_OFFSET_INIT(gendisk_queue, "gendisk", "queue"); + MEMBER_OFFSET_INIT(hd_struct_dev, "hd_struct", "__dev"); + MEMBER_OFFSET_INIT(klist_k_list, "klist", "k_list"); + MEMBER_OFFSET_INIT(klist_node_n_klist, "klist_node", "n_klist"); + MEMBER_OFFSET_INIT(klist_node_n_node, "klist_node", "n_node"); + MEMBER_OFFSET_INIT(kobject_entry, "kobject", "entry"); + MEMBER_OFFSET_INIT(kset_list, "kset", "list"); + MEMBER_OFFSET_INIT(request_list_count, "request_list", "count"); + MEMBER_OFFSET_INIT(request_queue_in_flight, "request_queue", + "in_flight"); + MEMBER_OFFSET_INIT(request_queue_rq, "request_queue", "rq"); + MEMBER_OFFSET_INIT(subsys_private_klist_devices, "subsys_private", + "klist_devices"); + MEMBER_OFFSET_INIT(subsystem_kset, "subsystem", "kset"); + STRUCT_SIZE_INIT(subsystem, "subsystem"); + STRUCT_SIZE_INIT(class_private, "class_private"); + MEMBER_SIZE_INIT(rq_in_flight, "request_queue", "in_flight"); + MEMBER_SIZE_INIT(class_private_devices, "class_private", + "class_devices"); + kt->flags &= ~PRE_KERNEL_INIT; } @@ -7389,3 +7423,422 @@ paravirt_init(void) kt->flags |= ARCH_PVOPS; } } + +/* + * If the disk's name is started with these strings, we will skip it and do not + * display its statistics. + */ +static char *skipped_disk_name[] = { + "ram", + "loop", + NULL +}; + +static int is_skipped_disk(char *name) +{ + char **p = skipped_disk_name; + + while (*p) { + if (strncmp(name, *p, strlen(*p)) == 0) + return TRUE; + p++; + } + + return FALSE; +} + +struct diskio { + int read; + int write; +}; + +struct iter { + /* If the kernel uses klist, the address should be klist.k_list */ + long head_address; + long current_address; + long type_address; /* the address of symbol "disk_type" */ + + /* + * If it is true, it means request_list.count[2] contains async/sync + * requests. + */ + int sync_count; + int diskname_len; + + unsigned long (*next_disk)(struct iter *); + + /* + * The argument is the address of request_queue, and the function + * returns the total requests in the driver(not ended) + */ + unsigned int (*get_in_flight)(unsigned long); + + /* + * this function reads request_list.count[2], and the first argument + * is the address of request_queue. + */ + void (*get_diskio)(unsigned long , struct diskio *); + + /* + * check if device.type == &disk_type + * + * old kernel(version <= 2.6.24) does not have the symbol "disk_type", + * and this callback should be null. + */ + int (*match)(struct iter *, unsigned long); + + /* + * If the kernel uses list, the argument is the address of list_head, + * otherwise, the argument is the address of klist_node. + */ + unsigned long (*get_gendisk)(unsigned long); +}; + +/* kernel version <= 2.6.24 */ +static unsigned long get_gendisk_1(unsigned long entry) +{ + return entry - OFFSET(kobject_entry) - OFFSET(gendisk_kobj); +} + +/* 2.6.24 < kernel version <= 2.6.27 */ +static unsigned long get_gendisk_2(unsigned long entry) +{ + return entry - OFFSET(device_node) - OFFSET(gendisk_dev); +} + +/* kernel version > 2.6.27 && struct gendisk contains dev/__dev */ +static unsigned long get_gendisk_3(unsigned long entry) +{ + return entry - OFFSET(device_knode_class) - OFFSET(gendisk_dev); +} + +/* kernel version > 2.6.27 && struct gendisk does not contain dev/__dev */ +static unsigned long get_gendisk_4(unsigned long entry) +{ + return entry - OFFSET(device_knode_class) - OFFSET(hd_struct_dev) - + OFFSET(gendisk_part0); +} + +/* 2.6.24 < kernel version <= 2.6.27 */ +static int match_list(struct iter *i, unsigned long entry) +{ + unsigned long device_address; + unsigned long device_type; + + device_address = entry - OFFSET(device_node); + readmem(device_address + OFFSET(device_type), KVADDR, &device_type, + sizeof(device_type), "device.type", FAULT_ON_ERROR); + if (device_type != i->type_address) + return FALSE; + + return TRUE; +} + +/* kernel version > 2.6.27 */ +static int match_klist(struct iter *i, unsigned long entry) +{ + unsigned long device_address; + unsigned long device_type; + + device_address = entry - OFFSET(device_knode_class); + readmem(device_address + OFFSET(device_type), KVADDR, &device_type, + sizeof(device_type), "device.type", FAULT_ON_ERROR); + if (device_type != i->type_address) + return FALSE; + + return TRUE; +} + +/* old kernel(version <= 2.6.27): list */ +static unsigned long next_disk_list(struct iter *i) +{ + unsigned long list_head_address, next_address; + + if (i->current_address) { + list_head_address = i->current_address; + } else { + list_head_address = i->head_address; + } + +again: + /* read list_head.next */ + readmem(list_head_address + OFFSET(list_head_next), KVADDR, + &next_address, sizeof(next_address), "list_head.next", + FAULT_ON_ERROR); + + if (next_address == i->head_address) + return 0; + if (i->match && !i->match(i, next_address)) + goto again; + + i->current_address = next_address; + return i->get_gendisk(next_address); +} + +/* new kernel(version > 2.6.27): klist */ +static unsigned long next_disk_klist(struct iter* i) +{ + unsigned long klist_node_address, list_head_address, next_address; + unsigned long n_klist; + + if (i->current_address) { + list_head_address = i->current_address; + } else { + list_head_address = i->head_address; + } + +again: + /* read list_head.next */ + readmem(list_head_address + OFFSET(list_head_next), KVADDR, + &next_address, sizeof(next_address), "list_head.next", + FAULT_ON_ERROR); + + /* skip dead klist_node */ + while(next_address != i->head_address) { + klist_node_address = next_address - OFFSET(klist_node_n_node); + readmem(klist_node_address + OFFSET(klist_node_n_klist), KVADDR, + &n_klist, sizeof(n_klist), "klist_node.n_klist", + FAULT_ON_ERROR); + if (!(n_klist & 1)) + break; + + /* the klist_node is dead, skip to next klist_node */ + readmem(next_address + OFFSET(list_head_next), KVADDR, + &next_address, sizeof(next_address), "list_head.next", + FAULT_ON_ERROR); + } + + if (next_address == i->head_address) + return 0; + + if (i->match && !i->match(i, klist_node_address)) { + list_head_address = next_address; + goto again; + } + + i->current_address = next_address; + return i->get_gendisk(klist_node_address); +} + +/* read request_queue.rq.count[2] */ +static void get_diskio_1(unsigned long rq, struct diskio *io) +{ + int count[2]; + + readmem(rq + OFFSET(request_queue_rq) + OFFSET(request_list_count), + KVADDR, count, sizeof(int) * 2, "request_list.count", + FAULT_ON_ERROR); + + io->read = count[0]; + io->write = count[1]; +} + +/* request_queue.in_flight contains total requests */ +static unsigned int get_in_flight_1(unsigned long rq) +{ + unsigned int in_flight; + + readmem(rq+ OFFSET(request_queue_in_flight), KVADDR, &in_flight, + sizeof(uint), "request_queue.in_flight", FAULT_ON_ERROR); + return in_flight; +} + +/* request_queue.in_flight[2] contains read/write requests */ +static unsigned int get_in_flight_2(unsigned long rq) +{ + unsigned int in_flight[2]; + + readmem(rq+ OFFSET(request_queue_in_flight), KVADDR, in_flight, + sizeof(uint) * 2, "request_queue.in_flight", FAULT_ON_ERROR); + return in_flight[0] + in_flight[1]; +} + +static void init_iter(struct iter *i) +{ + ARRAY_LENGTH_INIT(i->diskname_len, gendisk.disk_name, + "gendisk.disk_name", NULL, sizeof(char)); + if (i->diskname_len < 0 || i->diskname_len > BUFSIZE) { + error(FATAL, "invalid diskname's length\n"); + return; + } + + i->current_address = 0; + + /* check whether BLK_RW_SYNC exists */ + i->sync_count = + get_symbol_type("BLK_RW_SYNC", NULL, NULL) == TYPE_CODE_ENUM; + + if (SIZE(rq_in_flight) == sizeof(int)) { + i->get_in_flight = get_in_flight_1; + } else if (SIZE(rq_in_flight) == sizeof(int) * 2) { + i->get_in_flight = get_in_flight_2; + } else { + error(FATAL, "invalid request_queue.in_flight's size\n"); + return; + } + i->get_diskio = get_diskio_1; + + if (symbol_exists("block_subsys") || symbol_exists("block_kset")) { + /* kernel version <= 2.6.24 */ + unsigned long block_subsys_addr; + + if (symbol_exists("block_subsys")) + block_subsys_addr = symbol_value("block_subsys"); + else + block_subsys_addr = symbol_value("block_kset"); + if (VALID_STRUCT(subsystem)) + i->head_address = block_subsys_addr + + OFFSET(subsystem_kset) + OFFSET(kset_list); + else + i->head_address = block_subsys_addr + OFFSET(kset_list); + i->type_address = 0; + i->next_disk = next_disk_list; + i->match = NULL; + i->get_gendisk = get_gendisk_1; + } else if (symbol_exists("block_class")) { + unsigned long block_class_addr = symbol_value("block_class"); + + i->type_address = symbol_value("disk_type"); + if (VALID_MEMBER(class_devices) || + (VALID_MEMBER(class_private_devices) && + SIZE(class_private_devices) == SIZE(list_head))) { + /* 2.6.24 < kernel version <= 2.6.27, list */ + if (!VALID_STRUCT(class_private)) { + /* 2.6.24 < kernel version <= 2.6.26 */ + i->head_address = block_class_addr + + OFFSET(class_devices); + } else { + /* kernel version is 2.6.27 */ + unsigned long class_private_addr; + + readmem(block_class_addr + OFFSET(class_p), + KVADDR, &class_private_addr, + sizeof(class_private_addr), "class.p", + FAULT_ON_ERROR); + i->head_address = class_private_addr + + OFFSET(class_private_devices); + } + i->next_disk = next_disk_list; + i->match = match_list; + i->get_gendisk = get_gendisk_2; + } else { + /* kernel version > 2.6.27, klist */ + unsigned long class_private_addr; + readmem(block_class_addr + OFFSET(class_p), KVADDR, + &class_private_addr, sizeof(class_private_addr), + "class.p", FAULT_ON_ERROR); + + if (VALID_STRUCT(class_private)) { + /* 2.6.27 < kernel version <= 2.6.37-rc2 */ + i->head_address = class_private_addr + + OFFSET(class_private_devices); + } else { + /* kernel version > 2.6.37-rc2 */ + i->head_address = class_private_addr + + OFFSET(subsys_private_klist_devices); + } + i->head_address += OFFSET(klist_k_list); + i->next_disk = next_disk_klist; + i->match = match_klist; + if (VALID_MEMBER(gendisk_dev)) + i->get_gendisk = get_gendisk_3; + else + i->get_gendisk = get_gendisk_4; + } + } else { + error(FATAL, "unsupported kernel version\n"); + return; + } +} + +static void display_one_diskio(struct iter *i, unsigned long gendisk) +{ + char disk_name[BUFSIZE + 1]; + char buf0[BUFSIZE]; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + char buf4[BUFSIZE]; + char buf5[BUFSIZE]; + int major; + unsigned long queue_addr; + unsigned int in_flight; + struct diskio io; + + memset(disk_name, 0, BUFSIZE + 1); + readmem(gendisk + OFFSET(gendisk_disk_name), KVADDR, disk_name, + i->diskname_len, "gen_disk.disk_name", FAULT_ON_ERROR); + if (is_skipped_disk(disk_name)) + return; + + readmem(gendisk + OFFSET(gendisk_queue), KVADDR, &queue_addr, + sizeof(ulong), "gen_disk.queue", FAULT_ON_ERROR); + readmem(gendisk + OFFSET(gendisk_major), KVADDR, &major, sizeof(int), + "gen_disk.major", FAULT_ON_ERROR); + i->get_diskio(queue_addr, &io); + in_flight = i->get_in_flight(queue_addr); + + fprintf(fp, "%s%s0x%s%s%s%s0x%s%s%5d%s%s%s%s%s%5u\n", + mkstring(buf0, 5, RJUST|INT_DEC, (char *)(unsigned long)major), + space(MINSPACE), + mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX, (char *)gendisk), + space(MINSPACE), + mkstring(buf2, 4, RJUST, disk_name), + space(MINSPACE), + mkstring(buf3, VADDR_PRLEN <= 11 ? 11 : VADDR_PRLEN, + LJUST|LONG_HEX, (char *)queue_addr), + space(MINSPACE), + io.read + io.write, + space(MINSPACE), + i->sync_count ? "-----" : mkstring(buf4, 5, RJUST|INT_DEC, + (char *)(unsigned long)io.read), + space(MINSPACE), + i->sync_count ? "-----" : mkstring(buf5, 5, RJUST|INT_DEC, + (char *)(unsigned long)io.write), + space(MINSPACE), + in_flight); +} + +static void display_all_diskio(void) +{ + struct iter i; + unsigned long gendisk; + char buf0[BUFSIZE]; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + char buf4[BUFSIZE]; + char buf5[BUFSIZE]; + + init_iter(&i); + + fprintf(fp, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + "MAJOR", + space(MINSPACE), + mkstring(buf0, VADDR_PRLEN + 2, LJUST, "GENDISK"), + space(MINSPACE), + "NAME", + space(MINSPACE), + mkstring(buf1, VADDR_PRLEN <= 11 ? 13 : VADDR_PRLEN + 2, LJUST, + "RUQUEST QUEUE"), + space(MINSPACE), + mkstring(buf2, 5, RJUST, "TOTAL"), + space(MINSPACE), + mkstring(buf3, 5, RJUST, "READ"), + space(MINSPACE), + mkstring(buf4, 5, RJUST, "WRITE"), + space(MINSPACE), + mkstring(buf5, 5, RJUST, "DRV")); + + while((gendisk = i.next_disk(&i)) != 0) { + display_one_diskio(&i, gendisk); + } +} + +void cmd_diskio(void) +{ + if (argcnt == 1) + display_all_diskio(); + else + cmd_usage(pc->curcmd, SYNOPSIS); +} -- 1.7.1
-- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility