[BACK]Return to kdbm_vm.c CVS log [TXT][DIR] Up to [Development] / linux-2.6-xfs / kdb / modules

File: [Development] / linux-2.6-xfs / kdb / modules / kdbm_vm.c (download)

Revision 1.23, Mon Feb 20 14:29:08 2006 UTC (11 years, 7 months ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
Changes since 1.22: +11 -5 lines

Merge up to 2.6.16-rc4, KDB updated too.
Merge of 2.6.x-xfs-melb:linux:25234a by kenmcd.

/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (c) 1999-2006 Silicon Graphics, Inc.  All Rights Reserved.
 */

#include <linux/blkdev.h>
#include <linux/types.h>
#include <linux/kdb.h>
#include <linux/kdbprivate.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/swapops.h>

#include <scsi.h>
#include <scsi/scsi_host.h>

MODULE_AUTHOR("SGI");
MODULE_DESCRIPTION("Debug VM information");
MODULE_LICENSE("GPL");

struct __vmflags {
	unsigned long mask;
	char *name;
};

static struct __vmflags vmflags[] = {
	{ VM_READ, "READ" },
	{ VM_WRITE, "WRITE" },
	{ VM_EXEC, "EXEC" },
	{ VM_SHARED, "SHARED" },
	{ VM_MAYREAD, "MAYREAD" },
	{ VM_MAYWRITE, "MAYWRITE" },
	{ VM_MAYEXEC, "MAYEXEC" },
	{ VM_MAYSHARE, "MAYSHARE" },
	{ VM_GROWSDOWN, "GROWSDOWN" },
	{ VM_GROWSUP, "GROWSUP" },
	{ VM_SHM, "SHM" },
	{ VM_PFNMAP, "PFNMAP" },
	{ VM_DENYWRITE, "DENYWRITE" },
	{ VM_EXECUTABLE, "EXECUTABLE" },
	{ VM_LOCKED, "LOCKED" },
	{ VM_IO , "IO " },
	{ VM_SEQ_READ , "SEQ_READ " },
	{ VM_RAND_READ , "RAND_READ " },
	{ VM_DONTCOPY , "DONTCOPY " },
	{ VM_DONTEXPAND , "DONTEXPAND " },
	{ VM_RESERVED , "RESERVED " },
	{ VM_ACCOUNT , "ACCOUNT " },
	{ VM_HUGETLB , "HUGETLB " },
	{ VM_NONLINEAR , "NONLINEAR " },
	{ VM_MAPPED_COPY , "MAPPED_COPY " },
	{ VM_INSERTPAGE , "INSERTPAGE " },
	{ 0, "" }
};

static int
kdbm_print_vm(struct vm_area_struct *vp, unsigned long addr, int verbose_flg)
{
	struct __vmflags *tp;

	kdb_printf("struct vm_area_struct at 0x%lx for %d bytes\n",
		   addr, (int) sizeof (struct vm_area_struct));

	kdb_printf("vm_start = 0x%p   vm_end = 0x%p\n", (void *) vp->vm_start,
		   (void *) vp->vm_end);
	kdb_printf("vm_page_prot = 0x%lx\n", pgprot_val(vp->vm_page_prot));

	kdb_printf("vm_flags: ");
	for (tp = vmflags; tp->mask; tp++) {
		if (vp->vm_flags & tp->mask) {
			kdb_printf(" %s", tp->name);
		}
	}
	kdb_printf("\n");

	if (!verbose_flg)
		return 0;

	kdb_printf("vm_mm = 0x%p\n", (void *) vp->vm_mm);
	kdb_printf("vm_next = 0x%p\n", (void *) vp->vm_next);
	kdb_printf("shared.vm_set.list.next = 0x%p\n", (void *) vp->shared.vm_set.list.next);
	kdb_printf("shared.vm_set.list.prev = 0x%p\n", (void *) vp->shared.vm_set.list.prev);
	kdb_printf("shared.vm_set.parent = 0x%p\n", (void *) vp->shared.vm_set.parent);
	kdb_printf("shared.vm_set.head = 0x%p\n", (void *) vp->shared.vm_set.head);
	kdb_printf("anon_vma_node.next = 0x%p\n", (void *) vp->anon_vma_node.next);
	kdb_printf("anon_vma_node.prev = 0x%p\n", (void *) vp->anon_vma_node.prev);
	kdb_printf("vm_ops = 0x%p\n", (void *) vp->vm_ops);
	if (vp->vm_ops != NULL) {
		kdb_printf("vm_ops->open = 0x%p\n", vp->vm_ops->open);
		kdb_printf("vm_ops->close = 0x%p\n", vp->vm_ops->close);
		kdb_printf("vm_ops->nopage = 0x%p\n", vp->vm_ops->nopage);
#ifdef HAVE_VMOP_MPROTECT
		kdb_printf("vm_ops->mprotect = 0x%p\n", vp->vm_ops->mprotect);
#endif
	}
	kdb_printf("vm_pgoff = 0x%lx\n", vp->vm_pgoff);
	kdb_printf("vm_file = 0x%p\n", (void *) vp->vm_file);
	kdb_printf("vm_private_data = 0x%p\n", vp->vm_private_data);

	return 0;
}

static int
kdbm_print_vmp(struct vm_area_struct *vp, int verbose_flg)
{
	struct __vmflags *tp;

	if (verbose_flg) {
		kdb_printf("0x%lx:  ", (unsigned long) vp);
	}

	kdb_printf("0x%p  0x%p ", (void *) vp->vm_start, (void *) vp->vm_end);

	for (tp = vmflags; tp->mask; tp++) {
		if (vp->vm_flags & tp->mask) {
			kdb_printf(" %s", tp->name);
		}
	}
	kdb_printf("\n");

	return 0;
}

/*
 * kdbm_vm
 *
 *     This function implements the 'vm' command.  Print a vm_area_struct.
 *
 *     vm [-v] <address>	Print vm_area_struct at <address>
 *     vmp [-v] <pid>		Print all vm_area_structs for <pid>
 */

static int
kdbm_vm(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	unsigned long addr;
	long offset = 0;
	int nextarg;
	int diag;
	int verbose_flg = 0;

	if (argc == 2) {
		if (strcmp(argv[1], "-v") != 0) {
			return KDB_ARGCOUNT;
		}
		verbose_flg = 1;
	} else if (argc != 1) {
		return KDB_ARGCOUNT;
	}

	if (strcmp(argv[0], "vmp") == 0) {
		struct task_struct *g, *tp;
		struct vm_area_struct *vp;
		pid_t pid;

		if ((diag = kdbgetularg(argv[argc], (unsigned long *) &pid)))
			return diag;

		kdb_do_each_thread(g, tp) {
			if (tp->pid == pid) {
				if (tp->mm != NULL) {
					if (verbose_flg)
						kdb_printf
						    ("vm_area_struct       ");
					kdb_printf
					    ("vm_start            vm_end              vm_flags\n");
					vp = tp->mm->mmap;
					while (vp != NULL) {
						kdbm_print_vmp(vp, verbose_flg);
						vp = vp->vm_next;
					}
				}
				return 0;
			}
		} kdb_while_each_thread(g, tp);

		kdb_printf("No process with pid == %d found\n", pid);

	} else {
		struct vm_area_struct v;

		nextarg = argc;
		if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset,
					  NULL, regs))
		    || (diag = kdb_getarea(v, addr)))
			return (diag);

		kdbm_print_vm(&v, addr, verbose_flg);
	}

	return 0;
}

static int
kdbm_print_pte(pte_t * pte)
{
	kdb_printf("0x%lx (", (unsigned long) pte_val(*pte));

	if (pte_present(*pte)) {
		if (pte_exec(*pte))
			kdb_printf("X");
		if (pte_write(*pte))
			kdb_printf("W");
		if (pte_read(*pte))
			kdb_printf("R");
		if (pte_young(*pte))
			kdb_printf("A");
		if (pte_dirty(*pte))
			kdb_printf("D");

	} else {
		kdb_printf("OFFSET=0x%lx ", swp_offset(pte_to_swp_entry(*pte)));
		kdb_printf("TYPE=0x%ulx", swp_type(pte_to_swp_entry(*pte)));
	}

	kdb_printf(")");

	/* final newline is output by caller of kdbm_print_pte() */

	return 0;
}

/*
 * kdbm_pte
 *
 *     This function implements the 'pte' command.  Print all pte_t structures
 *     that map to the given virtual address range (<address> through <address>
 *     plus <nbytes>) for the given process. The default value for nbytes is
 *     one.
 *
 *     pte -m <mm> <address> [<nbytes>]    Print all pte_t structures for
 *					   virtual <address> in address space
 *					   of <mm> which is a pointer to a
 *					   mm_struct
 *     pte -p <pid> <address> [<nbytes>]   Print all pte_t structures for
 *					   virtual <address> in address space
 *					   of <pid>
 */

static int
kdbm_pte(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	unsigned long addr;
	long offset = 0;
	int nextarg;
	unsigned long nbytes = 1;
	long npgs;
	int diag;
	int found;
	pid_t pid;
	struct task_struct *tp;
	struct mm_struct *mm, copy_of_mm;
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;

	if (argc < 3 || argc > 4) {
		return KDB_ARGCOUNT;
	}

	 if (strcmp(argv[1], "-p") == 0) {
		if ((diag = kdbgetularg(argv[2], (unsigned long *) &pid))) {
			return diag;
		}

		found = 0;
		for_each_process(tp) {
			if (tp->pid == pid) {
				if (tp->mm != NULL) {
					found = 1;
					break;
				}
				kdb_printf("task structure's mm field is NULL\n");
				return 0;
			}
		}

		if (!found) {
			kdb_printf("No process with pid == %d found\n", pid);
			return 0;
		}
		mm = tp->mm;
	} else if (strcmp(argv[1], "-m") == 0) {


		nextarg = 2;
		if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset,
					  NULL, regs))
		    || (diag = kdb_getarea(copy_of_mm, addr)))
			return (diag);
		mm = &copy_of_mm;
	} else {
		return KDB_ARGCOUNT;
	}

	if ((diag = kdbgetularg(argv[3], &addr))) {
		return diag;
	}

	if (argc == 4) {
		if ((diag = kdbgetularg(argv[4], &nbytes))) {
			return diag;
		}
	}

	kdb_printf("vaddr              pte\n");

	npgs = ((((addr & ~PAGE_MASK) + nbytes) + ~PAGE_MASK) >> PAGE_SHIFT);
	while (npgs-- > 0) {

		kdb_printf("0x%p ", (void *) (addr & PAGE_MASK));

		pgd = pgd_offset(mm, addr);
		if (pgd_present(*pgd)) {
			pud = pud_offset(pgd, addr);
			if (pud_present(*pud)) {
				pmd = pmd_offset(pud, addr);
				if (pmd_present(*pmd)) {
					pte = pte_offset_map(pmd, addr);
					if (pte_present(*pte)) {
						kdbm_print_pte(pte);
					}
				}
			}
		}

		kdb_printf("\n");
		addr += PAGE_SIZE;
	}

	return 0;
}

/*
 * kdbm_rpte
 *
 *     This function implements the 'rpte' command.  Print all pte_t structures
 *     that contain the given physical page range (<pfn> through <pfn>
 *     plus <npages>) for the given process. The default value for npages is
 *     one.
 *
 *     rpte -m <mm> <pfn> [<npages>]	   Print all pte_t structures for
 *					   physical page <pfn> in address space
 *					   of <mm> which is a pointer to a
 *					   mm_struct
 *     rpte -p <pid> <pfn> [<npages>]	   Print all pte_t structures for
 *					   physical page <pfn> in address space
 *					   of <pid>
 */

static int
kdbm_rpte(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	unsigned long addr;
	unsigned long pfn;
	long offset = 0;
	int nextarg;
	unsigned long npages = 1;
	int diag;
	int found;
	pid_t pid;
	struct task_struct *tp;
	struct mm_struct *mm, copy_of_mm;
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	unsigned long g, u, m, t;

	if (argc < 3 || argc > 4) {
		return KDB_ARGCOUNT;
	}

	 if (strcmp(argv[1], "-p") == 0) {
		if ((diag = kdbgetularg(argv[2], (unsigned long *) &pid))) {
			return diag;
		}

		found = 0;
		for_each_process(tp) {
			if (tp->pid == pid) {
				if (tp->mm != NULL) {
					found = 1;
					break;
				}
				kdb_printf("task structure's mm field is NULL\n");
				return 0;
			}
		}

		if (!found) {
			kdb_printf("No process with pid == %d found\n", pid);
			return 0;
		}
		mm = tp->mm;
	} else if (strcmp(argv[1], "-m") == 0) {


		nextarg = 2;
		if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset,
					  NULL, regs))
		    || (diag = kdb_getarea(copy_of_mm, addr)))
			return (diag);
		mm = &copy_of_mm;
	} else {
		return KDB_ARGCOUNT;
	}

	if ((diag = kdbgetularg(argv[3], &pfn))) {
		return diag;
	}

	if (argc == 4) {
		if ((diag = kdbgetularg(argv[4], &npages))) {
			return diag;
		}
	}

	/* spaces after vaddr depends on sizeof(unsigned long) */
	kdb_printf("pfn              vaddr%*s pte\n",
		   (int)(2*sizeof(unsigned long) + 2 - 5), " ");

	for (g = 0, pgd = pgd_offset(mm, 0UL); g < PTRS_PER_PGD; ++g, ++pgd) {
		if (pgd_none(*pgd) || pgd_bad(*pgd))
			continue;
		for (u = 0, pud = pud_offset(pgd, 0UL); u < PTRS_PER_PUD; ++u, ++pud) {
			if (pud_none(*pud) || pud_bad(*pud))
				continue;
			for (m = 0, pmd = pmd_offset(pud, 0UL); m < PTRS_PER_PMD; ++m, ++pmd) {
				if (pmd_none(*pmd) || pmd_bad(*pmd))
					continue;
				for (t = 0, pte = pte_offset_map(pmd, 0UL); t < PTRS_PER_PTE; ++t, ++pte) {
					if (pte_none(*pte))
						continue;
					if (pte_pfn(*pte) < pfn || pte_pfn(*pte) >= (pfn + npages))
						continue;
					addr = g << PGDIR_SHIFT;
#ifdef __ia64__
					/* IA64 plays tricks with the pgd mapping to save space.
					 * This reverses pgd_index().
					 */
					{
						unsigned long region = g >> (PAGE_SHIFT - 6);
						unsigned long l1index = g - (region << (PAGE_SHIFT - 6));
						addr = (region << 61) + (l1index << PGDIR_SHIFT);
					}
#endif
					addr += (m << PMD_SHIFT) + (t << PAGE_SHIFT);
					kdb_printf("0x%-14lx " kdb_bfd_vma_fmt0 " ",
						   pte_pfn(*pte), addr);
					kdbm_print_pte(pte);
					kdb_printf("\n");
				}
			}
		}
	}

	return 0;
}

static int
kdbm_print_dentry(unsigned long daddr)
{
	struct dentry d;
	int diag;
	char buf[256];

	kdb_printf("Dentry at 0x%lx\n", daddr);
	if ((diag = kdb_getarea(d, (unsigned long)daddr)))
		return diag;

	if ((d.d_name.len > sizeof(buf)) || (diag = kdb_getarea_size(buf, (unsigned long)(d.d_name.name), d.d_name.len)))
		kdb_printf(" d_name.len = %d d_name.name = 0x%p\n",
					d.d_name.len, d.d_name.name);
	else
		kdb_printf(" d_name.len = %d d_name.name = 0x%p <%.*s>\n",
					d.d_name.len, d.d_name.name,
					(int)(d.d_name.len), d.d_name.name);

	kdb_printf(" d_count = %d d_flags = 0x%x d_inode = 0x%p\n",
					atomic_read(&d.d_count), d.d_flags, d.d_inode);

	kdb_printf(" d_parent = 0x%p\n", d.d_parent);

	kdb_printf(" d_hash.nxt = 0x%p d_hash.prv = 0x%p\n",
					d.d_hash.next, d.d_hash.pprev);

	kdb_printf(" d_lru.nxt = 0x%p d_lru.prv = 0x%p\n",
					d.d_lru.next, d.d_lru.prev);

	kdb_printf(" d_child.nxt = 0x%p d_child.prv = 0x%p\n",
					d.d_u.d_child.next, d.d_u.d_child.prev);

	kdb_printf(" d_subdirs.nxt = 0x%p d_subdirs.prv = 0x%p\n",
					d.d_subdirs.next, d.d_subdirs.prev);

	kdb_printf(" d_alias.nxt = 0x%p d_alias.prv = 0x%p\n",
					d.d_alias.next, d.d_alias.prev);

	kdb_printf(" d_op = 0x%p d_sb = 0x%p d_fsdata = 0x%p\n",
					d.d_op, d.d_sb, d.d_fsdata);

	kdb_printf(" d_iname = %s\n",
					d.d_iname);

	if (d.d_inode) {
		struct inode i;
		kdb_printf("\nInode Entry at 0x%p\n", d.d_inode);
		if ((diag = kdb_getarea(i, (unsigned long)d.d_inode)))
			return diag;
		kdb_printf(" i_mode = 0%o  i_nlink = %d  i_rdev = 0x%x\n",
						i.i_mode, i.i_nlink, i.i_rdev);

		kdb_printf(" i_ino = %ld i_count = %d\n",
						i.i_ino, atomic_read(&i.i_count));

		kdb_printf(" i_hash.nxt = 0x%p i_hash.prv = 0x%p\n",
						i.i_hash.next, i.i_hash.pprev);

		kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n",
						i.i_list.next, i.i_list.prev);

		kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n",
						i.i_dentry.next, i.i_dentry.prev);

	}
	kdb_printf("\n");
	return 0;
}

static int
kdbm_filp(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	struct file   f;
	int nextarg;
	unsigned long addr;
	long offset;
	int diag;

	if (argc != 1)
		return KDB_ARGCOUNT;

	nextarg = 1;
	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)) ||
	    (diag = kdb_getarea(f, addr)))
		return diag;

	kdb_printf("File Pointer at 0x%lx\n", addr);

	kdb_printf(" fu_list.nxt = 0x%p fu_list.prv = 0x%p\n",
					f.f_u.fu_list.next, f.f_u.fu_list.prev);

	kdb_printf(" f_dentry = 0x%p f_vfsmnt = 0x%p f_op = 0x%p\n",
					f.f_dentry, f.f_vfsmnt, f.f_op);

	kdb_printf(" f_count = %d f_flags = 0x%x f_mode = 0x%x\n",
					f.f_count.counter, f.f_flags, f.f_mode);

	kdb_printf(" f_pos = %Ld security = 0x%p\n",
					f.f_pos, f.f_security);

	kdb_printf(" private_data = 0x%p f_mapping = 0x%p\n\n",
					f.private_data, f.f_mapping);

	return kdbm_print_dentry((unsigned long)f.f_dentry);
}

static int
kdbm_fl(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	struct file_lock fl;
	int nextarg;
	unsigned long addr;
	long offset;
	int diag;


	if (argc != 1)
		return KDB_ARGCOUNT;

	nextarg = 1;
	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)) ||
		(diag = kdb_getarea(fl, addr)))
			return diag;

	kdb_printf("File_lock at 0x%lx\n", addr);

	kdb_printf(" fl_next = 0x%p fl_link.nxt = 0x%p fl_link.prv = 0x%p\n",
			fl.fl_next, fl.fl_link.next, fl.fl_link.prev);
	kdb_printf(" fl_block.nxt = 0x%p fl_block.prv = 0x%p\n",
			fl.fl_block.next, fl.fl_block.prev);
	kdb_printf(" fl_owner = 0x%p fl_pid = %d fl_wait = 0x%p\n",
			fl.fl_owner, fl.fl_pid, &fl.fl_wait);
	kdb_printf(" fl_file = 0x%p fl_flags = 0x%x\n",
			fl.fl_file, fl.fl_flags);
	kdb_printf(" fl_type = %d fl_start = 0x%llx fl_end = 0x%llx\n",
			fl.fl_type, fl.fl_start, fl.fl_end);

	kdb_printf(" file_lock_operations");
	if (fl.fl_ops)
		kdb_printf("\n   fl_insert = 0x%p fl_remove = 0x%p fl_copy_lock = 0x%p fl_release_private = 0x%p\n",
			fl.fl_ops->fl_insert, fl.fl_ops->fl_remove,
			fl.fl_ops->fl_copy_lock, fl.fl_ops->fl_release_private);
	else
		kdb_printf("   empty\n");

	kdb_printf(" lock_manager_operations");
	if (fl.fl_lmops)
		kdb_printf("\n   fl_compare_owner = 0x%p fl_notify = 0x%p\n",
			fl.fl_lmops->fl_compare_owner, fl.fl_lmops->fl_notify);
	else
		kdb_printf("   empty\n");

	kdb_printf(" fl_fasync = 0x%p fl_break 0x%lx\n",
			fl.fl_fasync, fl.fl_break_time);

	return 0;
}


static int
kdbm_dentry(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	int nextarg;
	unsigned long addr;
	long offset;
	int diag;

	if (argc != 1)
		return KDB_ARGCOUNT;

	nextarg = 1;
	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)))
		return diag;

	return kdbm_print_dentry(addr);
}

static int
kdbm_kobject(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	struct kobject k;
	int nextarg;
	unsigned long addr;
	long offset;
	int diag;

	if (argc != 1)
		return KDB_ARGCOUNT;

	nextarg = 1;
	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)) ||
	    (diag = kdb_getarea(k, addr)))
		return diag;


	kdb_printf("kobject at 0x%lx\n", addr);

	if (k.k_name) {
		char c;
		kdb_printf(" k_name 0x%p", k.k_name);
		if (kdb_getarea(c, (unsigned long)k.k_name) == 0)
			kdb_printf(" '%s'", k.k_name);
		kdb_printf("\n");
	}

	if (k.k_name != ((struct kobject *)addr)->name)
		kdb_printf(" name '%." __stringify(KOBJ_NAME_LEN) "s'\n", k.k_name);

	kdb_printf(" kref.refcount %d'\n", atomic_read(&k.kref.refcount));

	kdb_printf(" entry.next = 0x%p entry.prev = 0x%p\n",
					k.entry.next, k.entry.prev);

	kdb_printf(" parent = 0x%p kset = 0x%p ktype = 0x%p dentry = 0x%p\n",
					k.parent, k.kset, k.ktype, k.dentry);

	return 0;
}

static int
kdbm_sh(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	int diag;
	int nextarg;
	unsigned long addr;
	long offset = 0L;
	struct Scsi_Host sh;

	if (argc != 1)
		return KDB_ARGCOUNT;

	nextarg = 1;
	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)) ||
	    (diag = kdb_getarea(sh, addr)))
		return diag;

	kdb_printf("Scsi_Host at 0x%lx\n", addr);
	kdb_printf("host_queue = 0x%p\n", sh.__devices.next);
	kdb_printf("ehandler = 0x%p eh_action = 0x%p\n",
		   sh.ehandler, sh.eh_action);
	kdb_printf("host_wait = 0x%p hostt = 0x%p\n",
		   &sh.host_wait, sh.hostt);
	kdb_printf("host_failed = %d  host_no = %d resetting = %d\n",
		   sh.host_failed, sh.host_no, sh.resetting);
	kdb_printf("max id/lun/channel = [%d/%d/%d]  this_id = %d\n",
		   sh.max_id, sh.max_lun, sh.max_channel, sh.this_id);
	kdb_printf("can_queue = %d cmd_per_lun = %d  sg_tablesize = %d u_isa_dma = %d\n",
		   sh.can_queue, sh.cmd_per_lun, sh.sg_tablesize, sh.unchecked_isa_dma);
	kdb_printf("host_blocked = %d  reverse_ordering = %d \n",
		   sh.host_blocked, sh.reverse_ordering);

	return 0;
}

static int
kdbm_sd(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	int diag;
	int nextarg;
	unsigned long addr;
	long offset = 0L;
	struct scsi_device *sd = NULL;

	if (argc != 1)
		return KDB_ARGCOUNT;

	nextarg = 1;
	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)))
		goto out;
	if (!(sd = kmalloc(sizeof(*sd), GFP_ATOMIC))) {
		kdb_printf("kdbm_sd: cannot kmalloc sd\n");
		goto out;
	}
	if ((diag = kdb_getarea(*sd, addr)))
		goto out;

	kdb_printf("scsi_device at 0x%lx\n", addr);
	kdb_printf("next = 0x%p   prev = 0x%p  host = 0x%p\n",
		   sd->siblings.next, sd->siblings.prev, sd->host);
	kdb_printf("device_busy = %d   current_cmnd 0x%p\n",
		   sd->device_busy, sd->current_cmnd);
	kdb_printf("id/lun/chan = [%d/%d/%d]  single_lun = %d  device_blocked = %d\n",
		   sd->id, sd->lun, sd->channel, sd->single_lun, sd->device_blocked);
	kdb_printf("queue_depth = %d current_tag = %d  scsi_level = %d\n",
		   sd->queue_depth, sd->current_tag, sd->scsi_level);
	kdb_printf("%8.8s %16.16s %4.4s\n", sd->vendor, sd->model, sd->rev);
out:
	if (sd)
		kfree(sd);
	return diag;
}

static int
kdbm_sc(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
	int diag;
	int nextarg;
	unsigned long addr;
	long offset = 0L;
	struct scsi_cmnd *sc = NULL;

	if (argc != 1)
		return KDB_ARGCOUNT;

	nextarg = 1;
	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)))
		goto out;
	if (!(sc = kmalloc(sizeof(*sc), GFP_ATOMIC))) {
		kdb_printf("kdbm_sc: cannot kmalloc sc\n");
		goto out;
	}
	if ((diag = kdb_getarea(*sc, addr)))
		goto out;

	kdb_printf("scsi_cmnd at 0x%lx\n", addr);
	kdb_printf("device = 0x%p  next = 0x%p  done = 0x%p\n",
		   sc->device, sc->list.next, sc->done);
	kdb_printf("serial_number = %ld  retries = %d\n",
		   sc->serial_number, sc->retries);
	kdb_printf("cmd_len = %d  old_cmd_len = %d\n",
		   sc->cmd_len, sc->old_cmd_len);
	kdb_printf("cmnd = [%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x]\n",
		   sc->cmnd[0], sc->cmnd[1], sc->cmnd[2], sc->cmnd[3], sc->cmnd[4],
		   sc->cmnd[5], sc->cmnd[6], sc->cmnd[7], sc->cmnd[8], sc->cmnd[9],
		   sc->cmnd[10], sc->cmnd[11]);
	kdb_printf("data_cmnd = [%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x]\n",
		   sc->data_cmnd[0], sc->data_cmnd[1], sc->data_cmnd[2], sc->data_cmnd[3], sc->data_cmnd[4],
		   sc->data_cmnd[5], sc->data_cmnd[6], sc->data_cmnd[7], sc->data_cmnd[8], sc->data_cmnd[9],
		   sc->data_cmnd[10], sc->data_cmnd[11]);
	kdb_printf("request_buffer = 0x%p  request_bufflen = %d\n",
		   sc->request_buffer, sc->request_bufflen);
	kdb_printf("use_sg = %d  old_use_sg = %d sglist_len = %d\n",
		   sc->use_sg, sc->old_use_sg, sc->sglist_len);
	kdb_printf("bufflen = %d  buffer = 0x%p  underflow = %d transfersize = %d\n",
		   sc->bufflen, sc->buffer, sc->underflow, sc->transfersize);
	kdb_printf("tag = %d pid = %ld\n",
		   sc->tag, sc->pid);

out:
	if (sc)
		kfree(sc);
	return diag;
}

static int __init kdbm_vm_init(void)
{
	kdb_register("vm", kdbm_vm, "[-v] <vaddr>", "Display vm_area_struct", 0);
	kdb_register("vmp", kdbm_vm, "[-v] <pid>", "Display all vm_area_struct for <pid>", 0);
	kdb_register("pte", kdbm_pte, "( -m <mm> | -p <pid> ) <vaddr> [<nbytes>]", "Display pte_t for mm_struct or pid", 0);
	kdb_register("rpte", kdbm_rpte, "( -m <mm> | -p <pid> ) <pfn> [<npages>]", "Find pte_t containing pfn for mm_struct or pid", 0);
	kdb_register("dentry", kdbm_dentry, "<dentry>", "Display interesting dentry stuff", 0);
	kdb_register("kobject", kdbm_kobject, "<kobject>", "Display interesting kobject stuff", 0);
	kdb_register("filp", kdbm_filp, "<filp>", "Display interesting filp stuff", 0);
	kdb_register("fl", kdbm_fl, "<fl>", "Display interesting file_lock stuff", 0);
	kdb_register("sh", kdbm_sh, "<vaddr>", "Show scsi_host", 0);
	kdb_register("sd", kdbm_sd, "<vaddr>", "Show scsi_device", 0);
	kdb_register("sc", kdbm_sc, "<vaddr>", "Show scsi_cmnd", 0);

	return 0;
}

static void __exit kdbm_vm_exit(void)
{
	kdb_unregister("vm");
	kdb_unregister("vmp");
	kdb_unregister("pte");
	kdb_unregister("rpte");
	kdb_unregister("dentry");
	kdb_unregister("kobject");
	kdb_unregister("filp");
	kdb_unregister("fl");
	kdb_unregister("sh");
	kdb_unregister("sd");
	kdb_unregister("sc");
}

module_init(kdbm_vm_init)
module_exit(kdbm_vm_exit)