Bob Montgomery wrote:
With respect to character devices:
> ... [snip] ...
I can come up with believable file_ops values for (most? all?) of the others. And this leads me to wonder if crash shouldn't be collecting this info in a similar manner to fill in the other OPERATIONS fields. But now I'm quite a bit past what I know about how the character device stuff works. Thanks, Bob Montgomery Working at HP
I've attached what I'm going with. I've added the capability of getting the file_operations from the cdev_map when necessary. The block device code was also suffering from bit-rot as well, and so I put in a new collector function that uses the bdev_map as well. Thanks, Dave
kernel.c dev.c symbols.c defs.h --- crash-4.0-8.10/kernel.c 2009-06-04 17:13:29.000000000 -0400 +++ crash-4.0-8.10p1/kernel.c 2009-06-03 15:23:08.000000000 -0400 @@ -463,7 +463,26 @@ "char_device_struct", "fops"); MEMBER_OFFSET_INIT(char_device_struct_major, "char_device_struct", "major"); - } + MEMBER_OFFSET_INIT(char_device_struct_baseminor, + "char_device_struct", "baseminor"); + MEMBER_OFFSET_INIT(char_device_struct_cdev, + "char_device_struct", "cdev"); + } + + STRUCT_SIZE_INIT(cdev, "cdev"); + if (VALID_STRUCT(cdev)) + MEMBER_OFFSET_INIT(cdev_ops, "cdev", "ops"); + + STRUCT_SIZE_INIT(probe, "probe"); + if (VALID_STRUCT(probe)) { + MEMBER_OFFSET_INIT(probe_next, "probe", "next"); + MEMBER_OFFSET_INIT(probe_dev, "probe", "dev"); + MEMBER_OFFSET_INIT(probe_data, "probe", "data"); + } + + STRUCT_SIZE_INIT(kobj_map, "kobj_map"); + if (VALID_STRUCT(kobj_map)) + MEMBER_OFFSET_INIT(kobj_map_probes, "kobj_map", "probes"); MEMBER_OFFSET_INIT(module_kallsyms_start, "module", "kallsyms_start"); --- crash-4.0-8.10/dev.c 2009-06-04 17:13:29.000000000 -0400 +++ crash-4.0-8.10p1/dev.c 2009-06-05 10:06:46.000000000 -0400 @@ -20,6 +20,9 @@ static void dump_blkdevs(ulong); static void dump_chrdevs(ulong); static void dump_blkdevs_v2(ulong); +static void dump_blkdevs_v3(ulong); +static ulong search_cdev_map_probes(char *, int, int); +static ulong search_bdev_map_probes(char *, int, int); static void do_pci(void); static void do_io(void); static void do_resource_list(ulong, char *, int); @@ -87,7 +90,7 @@ flags = 0; - while ((c = getopt(argcnt, args, "pi")) != EOF) { + while ((c = getopt(argcnt, args, "pif")) != EOF) { switch(c) { case 'i': @@ -102,6 +105,10 @@ do_pci(); return; + case 'f': + flags = VERBOSE; + break; + default: argerrs++; break; @@ -111,15 +118,19 @@ if (argerrs) cmd_usage(pc->curcmd, SYNOPSIS); - if (argcnt == 1) { - dump_chrdevs(flags); - fprintf(fp, "\n"); - dump_blkdevs(flags); - } + dump_chrdevs(flags); + fprintf(fp, "\n"); + dump_blkdevs(flags); } #define MAX_DEV (255) +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) + +#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) +#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) + char *chrdev_hdr = "CHRDEV NAME "; char *blkdev_hdr = "BLKDEV NAME "; @@ -139,8 +150,8 @@ } chrdevs[MAX_DEV], *cp; ulong *cdp; char *char_device_struct_buf; - ulong next, savenext, name, fops; - int major; + ulong next, savenext, name, fops, cdev; + int major, minor; int name_typecode; size_t name_size; @@ -217,33 +228,57 @@ break; } - fops = ULONG(char_device_struct_buf + - OFFSET(char_device_struct_fops)); major = INT(char_device_struct_buf + OFFSET(char_device_struct_major)); + minor = INT(char_device_struct_buf + + OFFSET(char_device_struct_baseminor)); + + fops = 0; + if (VALID_MEMBER(char_device_struct_cdev) && + VALID_STRUCT(cdev)) { + cdev = ULONG(char_device_struct_buf + + OFFSET(char_device_struct_cdev)); + if (cdev) { + addr = cdev + OFFSET(cdev_ops); + readmem(addr, KVADDR, &fops, + sizeof(void *), + "cdev ops", FAULT_ON_ERROR); + } + } else { + fops = ULONG(char_device_struct_buf + + OFFSET(char_device_struct_fops)); + } + + if (!fops) + fops = search_cdev_map_probes(buf, major, minor); - fprintf(fp, " %3d ", major); - fprintf(fp, "%-12s ", buf); if (!fops) { - sprintf(buf2, "%s%%%ds ", - strlen("OPERATIONS") < VADDR_PRLEN ? " " : " ", - VADDR_PRLEN); - fprintf(fp, buf2, "(none)"); + if (flags == VERBOSE) { + fprintf(fp, " %3d ", major); + fprintf(fp, "%-12s ", buf); + sprintf(buf2, "%s%%%ds ", + strlen("OPERATIONS") < VADDR_PRLEN ? + " " : " ", VADDR_PRLEN); + fprintf(fp, buf2, "(none)"); + fprintf(fp, "\n"); + } } else { - sprintf(buf2, "%s%%%dlx ", - strlen("OPERATIONS") < VADDR_PRLEN ? " " : " ", - VADDR_PRLEN); - fprintf(fp, buf2, fops); - value_to_symstr(fops, buf2, 0); - if (strlen(buf2)) - fprintf(fp, "<%s>", buf2); + fprintf(fp, " %3d ", major); + fprintf(fp, "%-12s ", buf); + sprintf(buf2, "%s%%%dlx ", + strlen("OPERATIONS") < VADDR_PRLEN ? " " : " ", + VADDR_PRLEN); + fprintf(fp, buf2, fops); + value_to_symstr(fops, buf2, 0); + if (strlen(buf2)) + fprintf(fp, "<%s>", buf2); + fprintf(fp, "\n"); } - fprintf(fp, "\n"); if (CRASHDEBUG(1)) fprintf(fp, - "%lx: major: %d name: %s next: %lx fops: %lx\n", - *cdp, major, buf, next, fops); + "%lx: major: %d minor: %d name: %s next: %lx fops: %lx\n", + *cdp, major, minor, buf, next, fops); while (next) { readmem(savenext = next, KVADDR, char_device_struct_buf, @@ -267,39 +302,114 @@ break; } - fops = ULONG(char_device_struct_buf + - OFFSET(char_device_struct_fops)); major = INT(char_device_struct_buf + OFFSET(char_device_struct_major)); + minor = INT(char_device_struct_buf + + OFFSET(char_device_struct_baseminor)); + + fops = 0; + if (VALID_MEMBER(char_device_struct_cdev) && + VALID_STRUCT(cdev)) { + cdev = ULONG(char_device_struct_buf + + OFFSET(char_device_struct_cdev)); + if (cdev) { + addr = cdev + OFFSET(cdev_ops); + readmem(addr, KVADDR, &fops, + sizeof(void *), + "cdev ops", FAULT_ON_ERROR); + } + } else { + fops = ULONG(char_device_struct_buf + + OFFSET(char_device_struct_fops)); + } + + if (!fops) + fops = search_cdev_map_probes(buf, major, minor); - fprintf(fp, " %3d ", major); - fprintf(fp, "%-12s ", buf); if (!fops) { - sprintf(buf2, "%s%%%ds ", - strlen("OPERATIONS") < VADDR_PRLEN ? - " " : " ", VADDR_PRLEN); - fprintf(fp, buf2, "(none)"); + if (flags == VERBOSE) { + fprintf(fp, " %3d ", major); + fprintf(fp, "%-12s ", buf); + sprintf(buf2, "%s%%%ds ", + strlen("OPERATIONS") < VADDR_PRLEN ? + " " : " ", VADDR_PRLEN); + fprintf(fp, buf2, "(none)"); + fprintf(fp, "\n"); + } } else { - sprintf(buf2, "%s%%%dlx ", - strlen("OPERATIONS") < VADDR_PRLEN ? + fprintf(fp, " %3d ", major); + fprintf(fp, "%-12s ", buf); + sprintf(buf2, "%s%%%dlx ", + strlen("OPERATIONS") < VADDR_PRLEN ? " " : " ", VADDR_PRLEN); - fprintf(fp, buf2, fops); - value_to_symstr(fops, buf2, 0); - if (strlen(buf2)) - fprintf(fp, "<%s>", buf2); + fprintf(fp, buf2, fops); + value_to_symstr(fops, buf2, 0); + if (strlen(buf2)) + fprintf(fp, "<%s>", buf2); + fprintf(fp, "\n"); } - fprintf(fp, "\n"); if (CRASHDEBUG(1)) fprintf(fp, - "%lx: major: %d name: %s next: %lx fops: %lx\n", - savenext, major, buf, next, fops); + "%lx: major: %d minor: %d name: %s next: %lx fops: %lx\n", + savenext, major, minor, buf, next, fops); } } FREEBUF(char_device_struct_buf); } +/* + * Search for a major/minor match by following the list headed + * by the kobj_map.probes[major] array entry. The "data" member + * points to a cdev structure containing the file_operations + * pointer. + */ +static ulong +search_cdev_map_probes(char *name, int major, int minor) +{ + char *probe_buf; + ulong probes[MAX_DEV]; + ulong cdev_map, addr, next, ops, probe_data; + uint probe_dev; + + if (kernel_symbol_exists("cdev_map")) + get_symbol_data("cdev_map", sizeof(ulong), &cdev_map); + else + return 0; + + addr = cdev_map + OFFSET(kobj_map_probes); + if (!readmem(addr, KVADDR, &probes[0], sizeof(void *) * MAX_DEV, + "cdev_map.probes[]", QUIET|RETURN_ON_ERROR)) + return 0; + + ops = 0; + probe_buf = GETBUF(SIZE(probe)); + next = probes[major]; + + while (next) { + if (!readmem(next, KVADDR, probe_buf, SIZE(probe), + "struct probe", QUIET|RETURN_ON_ERROR)) + break; + + probe_dev = UINT(probe_buf + OFFSET(probe_dev)); + + if ((MAJOR(probe_dev) == major) && + (MINOR(probe_dev) == minor)) { + probe_data = ULONG(probe_buf + OFFSET(probe_data)); + addr = probe_data + OFFSET(cdev_ops); + if (!readmem(addr, KVADDR, &ops, sizeof(void *), + "cdev ops", QUIET|RETURN_ON_ERROR)) + ops = 0; + break; + } + + next = ULONG(probe_buf + OFFSET(probe_next)); + } + + FREEBUF(probe_buf); + return ops; +} /* * Dump the block device data. @@ -315,6 +425,12 @@ ulong ops; } blkdevs[MAX_DEV], *bp; + if (kernel_symbol_exists("major_names") && + kernel_symbol_exists("bdev_map")) { + dump_blkdevs_v3(flags); + return; + } + if (symbol_exists("all_bdevs")) { dump_blkdevs_v2(flags); return; @@ -524,6 +640,131 @@ FREEBUF(blk_major_name_buf); } +static void +dump_blkdevs_v3(ulong flags) +{ + int i, len; + ulong blk_major_name; + char *blk_major_name_buf; + char buf[BUFSIZE]; + char buf2[BUFSIZE]; + uint major; + ulong addr, next, fops; + + if (!(len = get_array_length("major_names", NULL, 0))) + len = MAX_DEV; + + fprintf(fp, "%s%s\n", blkdev_hdr, + mkstring(buf, VADDR_PRLEN, CENTER, "OPERATIONS")); + + blk_major_name_buf = GETBUF(SIZE(blk_major_name)); + + for (i = 0; i < len; i++) { + addr = symbol_value("major_names") + (i * sizeof(void *)); + readmem(addr, KVADDR, &blk_major_name, sizeof(void *), + "major_names[] entry", FAULT_ON_ERROR); + + if (!blk_major_name) + continue; + + readmem(blk_major_name, KVADDR, blk_major_name_buf, + SIZE(blk_major_name), "blk_major_name", FAULT_ON_ERROR); + + major = UINT(blk_major_name_buf + + OFFSET(blk_major_name_major)); + buf[0] = NULLCHAR; + strncpy(buf, blk_major_name_buf + + OFFSET(blk_major_name_name), 16); + next = ULONG(blk_major_name_buf + + OFFSET(blk_major_name_next)); + + if (major != i) + continue; + + fops = search_bdev_map_probes(buf, major, UNUSED); + + if (CRASHDEBUG(1)) + fprintf(fp, "[%lx] block major: %d name: %s\n", + blk_major_name, major, buf); + + if (!fops) { + if (flags == VERBOSE) { + fprintf(fp, " %3d ", major); + fprintf(fp, "%-12s ", + strlen(buf) ? buf : "(unknown)"); + sprintf(buf2, "%s%%%ds ", + strlen("OPERATIONS") < VADDR_PRLEN ? + " " : " ", VADDR_PRLEN); + fprintf(fp, buf2, "(none)"); + fprintf(fp, "\n"); + } + continue; + } + + fprintf(fp, " %3d ", major); + fprintf(fp, "%-12s ", strlen(buf) ? buf : "(unknown)"); + sprintf(buf, "%s%%%dlx ", + strlen("OPERATIONS") < VADDR_PRLEN ? " " : " ", + VADDR_PRLEN); + fprintf(fp, buf, fops); + value_to_symstr(fops, buf, 0); + if (strlen(buf)) + fprintf(fp, "<%s>", buf); + fprintf(fp, "\n"); + } +} + +static ulong +search_bdev_map_probes(char *name, int major, int minor) +{ + char *probe_buf, *gendisk_buf; + ulong probes[MAX_DEV]; + ulong bdev_map, addr, next, probe_data, fops; + uint probe_dev; + + get_symbol_data("bdev_map", sizeof(ulong), &bdev_map); + + addr = bdev_map + OFFSET(kobj_map_probes); + if (!readmem(addr, KVADDR, &probes[0], sizeof(void *) * MAX_DEV, + "bdev_map.probes[]", QUIET|RETURN_ON_ERROR)) + return 0; + + probe_buf = GETBUF(SIZE(probe)); + gendisk_buf = GETBUF(SIZE(gendisk)); + + fops = 0; + + for (next = probes[major]; next; + next = ULONG(probe_buf + OFFSET(probe_next))) { + + if (!readmem(next, KVADDR, probe_buf, SIZE(probe), + "struct probe", QUIET|RETURN_ON_ERROR)) + break; + + probe_data = ULONG(probe_buf + OFFSET(probe_data)); + if (!probe_data) + continue; + + probe_dev = UINT(probe_buf + OFFSET(probe_dev)); + if (MAJOR(probe_dev) != major) + continue; + + if (!readmem(probe_data, KVADDR, gendisk_buf, + SIZE(gendisk), "gendisk buffer", + QUIET|RETURN_ON_ERROR)) + break; + + fops = ULONG(gendisk_buf + OFFSET(gendisk_fops)); + + if (fops) + break; + } + + FREEBUF(probe_buf); + FREEBUF(gendisk_buf); + return fops; +} + void dump_dev_table(void) { --- crash-4.0-8.10/symbols.c 2009-06-04 17:13:28.000000000 -0400 +++ crash-4.0-8.10p1/symbols.c 2009-06-03 15:21:50.000000000 -0400 @@ -7024,6 +7024,21 @@ OFFSET(char_device_struct_fops)); fprintf(fp, " char_device_struct_major: %ld\n", OFFSET(char_device_struct_major)); + fprintf(fp, " char_device_struct_baseminor: %ld\n", + OFFSET(char_device_struct_baseminor)); + fprintf(fp, " char_device_struct_cdev: %ld\n", + OFFSET(char_device_struct_cdev)); + + fprintf(fp, " cdev_ops: %ld\n", OFFSET(cdev_ops)); + + fprintf(fp, " probe_next: %ld\n", + OFFSET(probe_next)); + fprintf(fp, " probe_dev: %ld\n", + OFFSET(probe_dev)); + fprintf(fp, " probe_data: %ld\n", + OFFSET(probe_data)); + fprintf(fp, " kobj_map_probes: %ld\n", + OFFSET(kobj_map_probes)); fprintf(fp, " blk_major_name_next: %ld\n", OFFSET(blk_major_name_next)); @@ -7266,6 +7281,12 @@ SIZE(pcpu_info)); fprintf(fp, " vcpu_struct: %ld\n", SIZE(vcpu_struct)); + fprintf(fp, " cdev: %ld\n", + SIZE(cdev)); + fprintf(fp, " probe: %ld\n", + SIZE(probe)); + fprintf(fp, " kobj_map: %ld\n", + SIZE(kobj_map)); fprintf(fp, "\n array_table:\n"); /* --- crash-4.0-8.10/defs.h 2009-06-04 17:13:29.000000000 -0400 +++ crash-4.0-8.10p1/defs.h 2009-06-05 09:50:19.000000000 -0400 @@ -1466,6 +1466,13 @@ long sched_info_last_arrival; long page_objects; long kmem_cache_oo; + long char_device_struct_cdev; + long char_device_struct_baseminor; + long cdev_ops; + long probe_next; + long probe_dev; + long probe_data; + long kobj_map_probes; }; struct size_table { /* stash of commonly-used sizes */ @@ -1571,6 +1578,9 @@ long cfs_rq; long pcpu_info; long vcpu_struct; + long cdev; + long probe; + long kobj_map; }; struct array_table {
-- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility