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 {
