diff -pruN linux-2.6.9.orig/Makefile linux-2.6.9.debug/Makefile
--- linux-2.6.9.orig/Makefile	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/Makefile	2006-12-20 17:05:54.000000000 +0300
@@ -445,7 +445,7 @@ scripts_basic: include/linux/autoconf.h
 
 # Objects we will link into vmlinux / subdirs we need to visit
 init-y		:= init/
-drivers-y	:= drivers/ sound/
+drivers-y	:= drivers/ sound/ cluster/
 net-y		:= net/
 libs-y		:= lib/
 core-y		:= usr/
diff -pruN linux-2.6.9.orig/arch/alpha/Kconfig linux-2.6.9.debug/arch/alpha/Kconfig
--- linux-2.6.9.orig/arch/alpha/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/alpha/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -600,3 +600,4 @@ source "crypto/Kconfig"
 
 source "lib/Kconfig"
 
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/arm/Kconfig linux-2.6.9.debug/arch/arm/Kconfig
--- linux-2.6.9.orig/arch/arm/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/arm/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -690,3 +690,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/arm26/Kconfig linux-2.6.9.debug/arch/arm26/Kconfig
--- linux-2.6.9.orig/arch/arm26/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/arm26/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -222,3 +222,4 @@ source "crypto/Kconfig"
 
 source "lib/Kconfig"
 
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/cris/Kconfig linux-2.6.9.debug/arch/cris/Kconfig
--- linux-2.6.9.orig/arch/cris/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/cris/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -174,3 +174,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/i386/Kconfig linux-2.6.9.debug/arch/i386/Kconfig
--- linux-2.6.9.orig/arch/i386/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/i386/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -1305,6 +1305,8 @@ source "lib/Kconfig"
 
 source "kernel/ub/Kconfig"
 
+source "cluster/Kconfig"
+
 config X86_SMP
 	bool
 	depends on SMP && !X86_VOYAGER
diff -pruN linux-2.6.9.orig/arch/ia64/Kconfig linux-2.6.9.debug/arch/ia64/Kconfig
--- linux-2.6.9.orig/arch/ia64/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/ia64/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -428,4 +428,6 @@ source "security/Kconfig"
 
 source "crypto/Kconfig"
 
+source "cluster/Kconfig"
+
 source "kernel/ub/Kconfig"
diff -pruN linux-2.6.9.orig/arch/m68k/Kconfig linux-2.6.9.debug/arch/m68k/Kconfig
--- linux-2.6.9.orig/arch/m68k/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/m68k/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -655,3 +655,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/mips/Kconfig linux-2.6.9.debug/arch/mips/Kconfig
--- linux-2.6.9.orig/arch/mips/Kconfig	2006-12-19 14:10:35.000000000 +0300
+++ linux-2.6.9.debug/arch/mips/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -1563,3 +1563,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/parisc/Kconfig linux-2.6.9.debug/arch/parisc/Kconfig
--- linux-2.6.9.orig/arch/parisc/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/parisc/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -195,3 +195,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/ppc/Kconfig linux-2.6.9.debug/arch/ppc/Kconfig
--- linux-2.6.9.orig/arch/ppc/Kconfig	2006-12-19 14:10:35.000000000 +0300
+++ linux-2.6.9.debug/arch/ppc/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -1231,3 +1231,5 @@ source "arch/ppc/Kconfig.debug"
 source "security/Kconfig"
 
 source "crypto/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/ppc64/Kconfig linux-2.6.9.debug/arch/ppc64/Kconfig
--- linux-2.6.9.orig/arch/ppc64/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/ppc64/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -357,3 +357,5 @@ config KEYS_COMPAT
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/s390/Kconfig linux-2.6.9.debug/arch/s390/Kconfig
--- linux-2.6.9.orig/arch/s390/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/s390/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -473,3 +473,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/sh/Kconfig linux-2.6.9.debug/arch/sh/Kconfig
--- linux-2.6.9.orig/arch/sh/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/sh/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -748,3 +748,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/sparc/Kconfig linux-2.6.9.debug/arch/sparc/Kconfig
--- linux-2.6.9.orig/arch/sparc/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/sparc/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -386,3 +386,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/sparc64/Kconfig linux-2.6.9.debug/arch/sparc64/Kconfig
--- linux-2.6.9.orig/arch/sparc64/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/sparc64/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -613,3 +613,5 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+source "cluster/Kconfig"
diff -pruN linux-2.6.9.orig/arch/um/Kconfig linux-2.6.9.debug/arch/um/Kconfig
--- linux-2.6.9.orig/arch/um/Kconfig	2006-12-19 14:10:34.000000000 +0300
+++ linux-2.6.9.debug/arch/um/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -225,6 +225,8 @@ source "crypto/Kconfig"
 
 source "lib/Kconfig"
 
+source "cluster/Kconfig"
+
 menu "SCSI support"
 depends on BROKEN
 
diff -pruN linux-2.6.9.orig/arch/x86_64/Kconfig linux-2.6.9.debug/arch/x86_64/Kconfig
--- linux-2.6.9.orig/arch/x86_64/Kconfig	2006-12-19 14:10:35.000000000 +0300
+++ linux-2.6.9.debug/arch/x86_64/Kconfig	2006-12-20 17:05:54.000000000 +0300
@@ -502,4 +502,6 @@ source "crypto/Kconfig"
 
 source "lib/Kconfig"
 
+source "cluster/Kconfig"
+
 source "kernel/ub/Kconfig"
diff -pruN linux-2.6.9.orig/cluster/Kconfig linux-2.6.9.debug/cluster/Kconfig
--- linux-2.6.9.orig/cluster/Kconfig	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/cluster/Kconfig	2006-12-20 17:06:53.000000000 +0300
@@ -0,0 +1,31 @@
+menu "Cluster Support"
+
+config CLUSTER
+	tristate "Cluster support"
+	---help---
+	Enable clustering support. This is not the high-performance clustering
+	made famous by beowulf. It is a high-availability cluster often using
+	shared storage. 
+	The cluster manager is the heart(beat) of the cluster system. It is
+	needed by all the other components. It provides membership services
+	for those other subsystems.
+
+config CLUSTER_DLM
+	tristate "Distributed Lock Manager"
+	depends on CLUSTER
+	---help---
+	A fully distributed lock manager, providing cluster-wide locking services
+	and protected lock namespaces for kernel and userland applications.
+
+config CLUSTER_DLM_PROCLOCKS
+       boolean "/proc/locks support for DLM"
+       depends on CLUSTER_DLM
+       depends on PROC_FS
+       ---help---
+       If this option is enabled a file will appear in /proc/cluster/dlm_locks.
+       write into this "file" the name of a lockspace known to the DLM and then
+       read out a list of all the resources and locks in that lockspace that are
+       known to the local node. Note because the DLM is distributed this may not
+       be the full lock picture.
+
+endmenu
diff -pruN linux-2.6.9.orig/cluster/Makefile linux-2.6.9.debug/cluster/Makefile
--- linux-2.6.9.orig/cluster/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/cluster/Makefile	2006-12-20 17:06:53.000000000 +0300
@@ -0,0 +1,4 @@
+obj-y	:= nocluster.o
+
+obj-$(CONFIG_CLUSTER)         += cman/
+obj-$(CONFIG_CLUSTER_DLM)     += dlm/
diff -pruN linux-2.6.9.orig/cluster/cman/Makefile linux-2.6.9.debug/cluster/cman/Makefile
diff -pruN linux-2.6.9.orig/cluster/cman/cnxman-private.h linux-2.6.9.debug/cluster/cman/cnxman-private.h
diff -pruN linux-2.6.9.orig/cluster/cman/cnxman.c linux-2.6.9.debug/cluster/cman/cnxman.c
diff -pruN linux-2.6.9.orig/cluster/cman/config.c linux-2.6.9.debug/cluster/cman/config.c
diff -pruN linux-2.6.9.orig/cluster/cman/config.h linux-2.6.9.debug/cluster/cman/config.h
diff -pruN linux-2.6.9.orig/cluster/cman/kjoin.c linux-2.6.9.debug/cluster/cman/kjoin.c
diff -pruN linux-2.6.9.orig/cluster/cman/membership.c linux-2.6.9.debug/cluster/cman/membership.c
diff -pruN linux-2.6.9.orig/cluster/cman/proc.c linux-2.6.9.debug/cluster/cman/proc.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm.h linux-2.6.9.debug/cluster/cman/sm.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_barrier.c linux-2.6.9.debug/cluster/cman/sm_barrier.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_barrier.h linux-2.6.9.debug/cluster/cman/sm_barrier.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_control.c linux-2.6.9.debug/cluster/cman/sm_control.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_control.h linux-2.6.9.debug/cluster/cman/sm_control.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_daemon.c linux-2.6.9.debug/cluster/cman/sm_daemon.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_daemon.h linux-2.6.9.debug/cluster/cman/sm_daemon.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_internal.h linux-2.6.9.debug/cluster/cman/sm_internal.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_joinleave.c linux-2.6.9.debug/cluster/cman/sm_joinleave.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_joinleave.h linux-2.6.9.debug/cluster/cman/sm_joinleave.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_membership.c linux-2.6.9.debug/cluster/cman/sm_membership.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_membership.h linux-2.6.9.debug/cluster/cman/sm_membership.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_message.c linux-2.6.9.debug/cluster/cman/sm_message.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_message.h linux-2.6.9.debug/cluster/cman/sm_message.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_misc.c linux-2.6.9.debug/cluster/cman/sm_misc.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_misc.h linux-2.6.9.debug/cluster/cman/sm_misc.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_recover.c linux-2.6.9.debug/cluster/cman/sm_recover.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_recover.h linux-2.6.9.debug/cluster/cman/sm_recover.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_services.c linux-2.6.9.debug/cluster/cman/sm_services.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_services.h linux-2.6.9.debug/cluster/cman/sm_services.h
diff -pruN linux-2.6.9.orig/cluster/cman/sm_user.c linux-2.6.9.debug/cluster/cman/sm_user.c
diff -pruN linux-2.6.9.orig/cluster/cman/sm_user.h linux-2.6.9.debug/cluster/cman/sm_user.h
diff -pruN linux-2.6.9.orig/cluster/dlm/Makefile linux-2.6.9.debug/cluster/dlm/Makefile
diff -pruN linux-2.6.9.orig/cluster/dlm/ast.c linux-2.6.9.debug/cluster/dlm/ast.c
diff -pruN linux-2.6.9.orig/cluster/dlm/ast.h linux-2.6.9.debug/cluster/dlm/ast.h
diff -pruN linux-2.6.9.orig/cluster/dlm/config.c linux-2.6.9.debug/cluster/dlm/config.c
diff -pruN linux-2.6.9.orig/cluster/dlm/config.h linux-2.6.9.debug/cluster/dlm/config.h
diff -pruN linux-2.6.9.orig/cluster/dlm/device.c linux-2.6.9.debug/cluster/dlm/device.c
diff -pruN linux-2.6.9.orig/cluster/dlm/device.h linux-2.6.9.debug/cluster/dlm/device.h
diff -pruN linux-2.6.9.orig/cluster/dlm/dir.c linux-2.6.9.debug/cluster/dlm/dir.c
diff -pruN linux-2.6.9.orig/cluster/dlm/dir.h linux-2.6.9.debug/cluster/dlm/dir.h
diff -pruN linux-2.6.9.orig/cluster/dlm/dlm_internal.h linux-2.6.9.debug/cluster/dlm/dlm_internal.h
diff -pruN linux-2.6.9.orig/cluster/dlm/lkb.c linux-2.6.9.debug/cluster/dlm/lkb.c
diff -pruN linux-2.6.9.orig/cluster/dlm/lkb.h linux-2.6.9.debug/cluster/dlm/lkb.h
diff -pruN linux-2.6.9.orig/cluster/dlm/locking.c linux-2.6.9.debug/cluster/dlm/locking.c
diff -pruN linux-2.6.9.orig/cluster/dlm/locking.h linux-2.6.9.debug/cluster/dlm/locking.h
diff -pruN linux-2.6.9.orig/cluster/dlm/lockqueue.c linux-2.6.9.debug/cluster/dlm/lockqueue.c
diff -pruN linux-2.6.9.orig/cluster/dlm/lockqueue.h linux-2.6.9.debug/cluster/dlm/lockqueue.h
diff -pruN linux-2.6.9.orig/cluster/dlm/lockspace.c linux-2.6.9.debug/cluster/dlm/lockspace.c
diff -pruN linux-2.6.9.orig/cluster/dlm/lockspace.h linux-2.6.9.debug/cluster/dlm/lockspace.h
diff -pruN linux-2.6.9.orig/cluster/dlm/lowcomms.c linux-2.6.9.debug/cluster/dlm/lowcomms.c
diff -pruN linux-2.6.9.orig/cluster/dlm/lowcomms.h linux-2.6.9.debug/cluster/dlm/lowcomms.h
diff -pruN linux-2.6.9.orig/cluster/dlm/main.c linux-2.6.9.debug/cluster/dlm/main.c
diff -pruN linux-2.6.9.orig/cluster/dlm/memory.c linux-2.6.9.debug/cluster/dlm/memory.c
diff -pruN linux-2.6.9.orig/cluster/dlm/memory.h linux-2.6.9.debug/cluster/dlm/memory.h
diff -pruN linux-2.6.9.orig/cluster/dlm/midcomms.c linux-2.6.9.debug/cluster/dlm/midcomms.c
diff -pruN linux-2.6.9.orig/cluster/dlm/midcomms.h linux-2.6.9.debug/cluster/dlm/midcomms.h
diff -pruN linux-2.6.9.orig/cluster/dlm/nodes.c linux-2.6.9.debug/cluster/dlm/nodes.c
diff -pruN linux-2.6.9.orig/cluster/dlm/nodes.h linux-2.6.9.debug/cluster/dlm/nodes.h
diff -pruN linux-2.6.9.orig/cluster/dlm/proc.c linux-2.6.9.debug/cluster/dlm/proc.c
diff -pruN linux-2.6.9.orig/cluster/dlm/queries.c linux-2.6.9.debug/cluster/dlm/queries.c
diff -pruN linux-2.6.9.orig/cluster/dlm/queries.h linux-2.6.9.debug/cluster/dlm/queries.h
diff -pruN linux-2.6.9.orig/cluster/dlm/rebuild.c linux-2.6.9.debug/cluster/dlm/rebuild.c
diff -pruN linux-2.6.9.orig/cluster/dlm/rebuild.h linux-2.6.9.debug/cluster/dlm/rebuild.h
diff -pruN linux-2.6.9.orig/cluster/dlm/reccomms.c linux-2.6.9.debug/cluster/dlm/reccomms.c
diff -pruN linux-2.6.9.orig/cluster/dlm/reccomms.h linux-2.6.9.debug/cluster/dlm/reccomms.h
diff -pruN linux-2.6.9.orig/cluster/dlm/recover.c linux-2.6.9.debug/cluster/dlm/recover.c
diff -pruN linux-2.6.9.orig/cluster/dlm/recover.h linux-2.6.9.debug/cluster/dlm/recover.h
diff -pruN linux-2.6.9.orig/cluster/dlm/recoverd.c linux-2.6.9.debug/cluster/dlm/recoverd.c
diff -pruN linux-2.6.9.orig/cluster/dlm/recoverd.h linux-2.6.9.debug/cluster/dlm/recoverd.h
diff -pruN linux-2.6.9.orig/cluster/dlm/rsb.c linux-2.6.9.debug/cluster/dlm/rsb.c
diff -pruN linux-2.6.9.orig/cluster/dlm/rsb.h linux-2.6.9.debug/cluster/dlm/rsb.h
diff -pruN linux-2.6.9.orig/cluster/dlm/util.c linux-2.6.9.debug/cluster/dlm/util.c
diff -pruN linux-2.6.9.orig/cluster/dlm/util.h linux-2.6.9.debug/cluster/dlm/util.h
diff -pruN linux-2.6.9.orig/cluster/nocluster.c linux-2.6.9.debug/cluster/nocluster.c
--- linux-2.6.9.orig/cluster/nocluster.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/cluster/nocluster.c	2006-12-20 17:05:54.000000000 +0300
@@ -0,0 +1,20 @@
+/*
+ * cluster/nocluster.c
+ *
+ * Copy from net/nonet.c
+ * Dummy functions to allow us to configure cluster support entirely
+ * out of the kernel.
+ *
+ * Distributed under the terms of the GNU GPL version 2.
+ * Copyright (c) Matthew Wilcox 2003
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+void __init nocluster_init(void)
+{
+}
diff -pruN linux-2.6.9.orig/fs/Kconfig linux-2.6.9.debug/fs/Kconfig
--- linux-2.6.9.orig/fs/Kconfig	2006-12-19 14:10:36.000000000 +0300
+++ linux-2.6.9.debug/fs/Kconfig	2006-12-20 17:07:36.000000000 +0300
@@ -283,13 +283,13 @@ config JFS_STATISTICS
 	  to be made available to the user in the /proc/fs/jfs/ directory.
 
 config FS_POSIX_ACL
-# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs)
+# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs/GFS)
 #
 # NOTE: you can implement Posix ACLs without these helpers (XFS does).
 # 	Never use this symbol for ifdefs.
 #
 	bool
-	depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL || REISERFS_FS_POSIX_ACL || NFSD_V4
+	depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL || REISERFS_FS_POSIX_ACL || NFSD_V4 || GFS_FS
 	default y
 
 config XFS_FS
@@ -1918,6 +1918,46 @@ config AFS_FS
 config RXRPC
 	tristate
 
+config LOCK_HARNESS
+	tristate "GFS Lock Harness"
+	help
+	  The module that connects GFS to the modules that provide
+	  locking for GFS.
+
+	  If you want to use GFS (a cluster filesystem) say Y here.
+
+config GFS_FS
+	tristate "GFS file system support"
+	depends on LOCK_HARNESS
+	help
+	  A cluster filesystem.
+
+	  Allows a cluster of computers to simultaneously use a block device
+	  that is shared between them (with FC, iSCSI, NBD, etc...).  GFS reads
+	  and writes to the block device like a local filesystem, but also uses
+	  a lock module to allow the computers coordinate their I/O so
+	  filesystem consistency is maintained.  One of the nifty features of
+	  GFS is perfect consistency -- changes made to the filesystem on one
+	  machine show up immediately on all other machines in the cluster.
+
+config LOCK_NOLOCK
+	tristate "Lock Nolock"
+	depends on LOCK_HARNESS
+	help
+	  A "fake" lock module that allows GFS to run as a local filesystem.
+
+config LOCK_DLM
+	tristate "Lock DLM"
+	depends on LOCK_HARNESS
+	help
+	  A lock module that allows GFS to use a Distributed Lock Manager.
+
+config LOCK_GULM
+	tristate "Lock GULM"
+	depends on LOCK_HARNESS
+	help
+	  A lock module that allows GFS to use a Failover Lock Manager.
+
 endmenu
 
 menu "Partition Types"
diff -pruN linux-2.6.9.orig/fs/Makefile linux-2.6.9.debug/fs/Makefile
--- linux-2.6.9.orig/fs/Makefile	2006-12-19 14:10:35.000000000 +0300
+++ linux-2.6.9.debug/fs/Makefile	2006-12-20 17:07:24.000000000 +0300
@@ -101,5 +101,7 @@ obj-$(CONFIG_AFS_FS)		+= afs/
 obj-$(CONFIG_BEFS_FS)		+= befs/
 obj-$(CONFIG_HOSTFS)		+= hostfs/
 obj-$(CONFIG_HPPFS)		+= hppfs/
+obj-$(CONFIG_LOCK_HARNESS)	+= gfs_locking/
+obj-$(CONFIG_GFS_FS)		+= gfs/
 obj-$(CONFIG_RELAYFS_FS)	+= relayfs/
 
diff -pruN linux-2.6.9.orig/fs/gfs/Makefile linux-2.6.9.debug/fs/gfs/Makefile
--- linux-2.6.9.orig/fs/gfs/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/Makefile	2006-12-20 17:07:24.000000000 +0300
@@ -0,0 +1,53 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+##
+##  This copyrighted material is made available to anyone wishing to use,
+##  modify, copy, or redistribute it subject to the terms and conditions
+##  of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+obj-$(CONFIG_GFS_FS) += gfs.o
+
+gfs-y	:=	acl.o \
+		bits.o \
+		bmap.o \
+		daemon.o \
+		diaper.o \
+		dio.o \
+		dir.o \
+		eaops.o \
+		eattr.o \
+		file.o \
+		glock.o \
+		glops.o \
+		inode.o \
+		ioctl.o \
+		lm.o \
+		log.o \
+		lops.o \
+		lvb.o \
+		main.o \
+		mount.o \
+		ondisk.o \
+		ops_address.o \
+		ops_dentry.o \
+		ops_export.o \
+		ops_file.o \
+		ops_fstype.o \
+		ops_inode.o \
+		ops_super.o \
+		ops_vm.o \
+		page.o \
+		proc.o \
+		quota.o \
+		recovery.o \
+		rgrp.o \
+		super.o \
+		trans.o \
+		unlinked.o \
+		util.o
+
diff -pruN linux-2.6.9.orig/fs/gfs/acl.c linux-2.6.9.debug/fs/gfs/acl.c
--- linux-2.6.9.orig/fs/gfs/acl.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/acl.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,383 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
+#include <linux/xattr_acl.h>
+
+#include "gfs.h"
+#include "acl.h"
+#include "eattr.h"
+#include "inode.h"
+
+/**
+ * gfs_acl_validate_set -
+ * @ip:
+ * @access:
+ * @er:
+ * @mode:
+ * @remove:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_acl_validate_set(struct gfs_inode *ip, int access,
+		     struct gfs_ea_request *er,
+		     int *remove, mode_t *mode)
+{
+	struct posix_acl *acl;
+	int error;
+
+	error = gfs_acl_validate_remove(ip, access);
+	if (error)
+		return error;
+
+	if (!er->er_data)
+		return -EINVAL;
+
+	acl = posix_acl_from_xattr(er->er_data, er->er_data_len);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (!acl) {
+		*remove = TRUE;
+		return 0;
+	}
+
+	error = posix_acl_valid(acl);
+	if (error)
+		goto out;
+
+	if (access) {
+		error = posix_acl_equiv_mode(acl, mode);
+		if (!error)
+			*remove = TRUE;
+		else
+			error = 0;
+	}
+
+ out:
+	posix_acl_release(acl);
+
+	return error;
+}
+
+/**
+ * gfs_acl_validate_remove -
+ * @ip:
+ * @access:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_acl_validate_remove(struct gfs_inode *ip, int access)
+{
+	if (!ip->i_sbd->sd_args.ar_posix_acls)
+		return -EOPNOTSUPP;
+	if (current->fsuid != ip->i_di.di_uid && !capable(CAP_FOWNER))
+		return -EPERM;
+	if (ip->i_di.di_type == GFS_FILE_LNK)
+		return -EOPNOTSUPP;
+	if (!access && ip->i_di.di_type != GFS_FILE_DIR)
+		return -EACCES;
+
+	return 0;
+}
+
+/**
+ * gfs_acl_get -
+ * @ip:
+ * @access:
+ * @acl:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_acl_get(struct gfs_inode *ip, int access, struct posix_acl **acl)
+{
+	struct gfs_ea_request er;
+	struct gfs_ea_location el;
+	int error;
+
+	if (!ip->i_di.di_eattr)
+		return 0;
+
+	memset(&er, 0, sizeof(struct gfs_ea_request));
+	if (access) {
+		er.er_name = GFS_POSIX_ACL_ACCESS;
+		er.er_name_len = GFS_POSIX_ACL_ACCESS_LEN;
+	} else {
+		er.er_name = GFS_POSIX_ACL_DEFAULT;
+		er.er_name_len = GFS_POSIX_ACL_DEFAULT_LEN;
+	}
+	er.er_type = GFS_EATYPE_SYS;
+
+	error = gfs_ea_find(ip, &er, &el);
+	if (error)
+		return error;
+	if (!el.el_ea)
+		return 0;
+	if (!GFS_EA_DATA_LEN(el.el_ea))
+		goto out;
+
+	er.er_data = kmalloc(GFS_EA_DATA_LEN(el.el_ea), GFP_KERNEL);
+	error = -ENOMEM;
+	if (!er.er_data)
+		goto out;
+
+	error = gfs_ea_get_copy(ip, &el, er.er_data);
+	if (error)
+		goto out_kfree;
+
+	*acl = posix_acl_from_xattr(er.er_data, GFS_EA_DATA_LEN(el.el_ea));
+	if (IS_ERR(*acl))
+		error = PTR_ERR(*acl);
+
+ out_kfree:
+	kfree(er.er_data);
+
+ out:
+	brelse(el.el_bh);
+
+	return error;
+}
+
+/**
+ * gfs_acl_new_prep - 
+ * @dip:
+ * @type:
+ * @mode:
+ * @a_acl:
+ * @d_acl:
+ * @blocks:
+ * @data:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_acl_new_prep(struct gfs_inode *dip,
+		 unsigned int type, mode_t *mode,
+		 void **a_data, void **d_data,
+		 unsigned int *size,
+		 unsigned int *blocks)
+{
+	struct posix_acl *acl = NULL;
+	int set_a = FALSE, set_d = FALSE;
+	int error;
+
+	if (!dip->i_sbd->sd_args.ar_posix_acls)
+		return 0;
+	if (type == GFS_FILE_LNK)
+		return 0;
+
+	error = gfs_acl_get(dip, FALSE, &acl);
+	if (error)
+		return error;
+	if (!acl) {
+		(*mode) &= ~current->fs->umask;
+		return 0;
+	}
+
+	{
+		struct posix_acl *clone = posix_acl_clone(acl, GFP_KERNEL);
+		error = -ENOMEM;
+		if (!clone)
+			goto out;
+		posix_acl_release(acl);
+		acl = clone;
+	}
+
+	error = posix_acl_create_masq(acl, mode);
+	if (error < 0)
+		goto out;
+	if (error > 0) {
+		set_a = TRUE;
+		error = 0;
+	}
+	if (type == GFS_FILE_DIR)
+		set_d = TRUE;
+
+	if (set_a || set_d) {
+		struct gfs_ea_request er;
+		void *d;
+		unsigned int s = posix_acl_xattr_size(acl->a_count);
+		unsigned int b;
+
+		memset(&er, 0, sizeof(struct gfs_ea_request));
+		er.er_name_len = GFS_POSIX_ACL_DEFAULT_LEN;
+		er.er_data_len = s;
+		error = gfs_ea_check_size(dip->i_sbd, &er);
+		if (error)
+			goto out;
+
+		b = DIV_RU(er.er_data_len, dip->i_sbd->sd_jbsize);
+		if (set_a && set_d)
+			b *= 2;
+		b++;
+
+		d = kmalloc(s, GFP_KERNEL);
+		error = -ENOMEM;
+		if (!d)
+			goto out;
+		posix_acl_to_xattr(acl, d, s);
+
+		if (set_a)
+			*a_data = d;
+		if (set_d)
+			*d_data = d;
+		*size = s;
+		*blocks = b;
+
+		error = 0;
+	}
+
+ out:
+	posix_acl_release(acl);
+
+	return error;
+}
+
+/**
+ * gfs_acl_new_init - 
+ * @dip:
+ * @ip:
+ * @a_data:
+ * @d_data:
+ * @size:
+ *
+ * Returns: errno
+ */
+
+int gfs_acl_new_init(struct gfs_inode *dip, struct gfs_inode *ip,
+		     void *a_data, void *d_data, unsigned int size)
+{
+	void *data = (a_data) ? a_data : d_data;
+	unsigned int x;
+	int error = 0;
+
+	ip->i_alloc = dip->i_alloc; /* Cheesy, but it works. */
+
+	for (x = 0; x < 2; x++) {
+		struct gfs_ea_request er;
+
+		memset(&er, 0, sizeof(struct gfs_ea_request));
+		if (x) {
+			if (!a_data)
+				continue;
+			er.er_name = GFS_POSIX_ACL_ACCESS;
+			er.er_name_len = GFS_POSIX_ACL_ACCESS_LEN;
+		} else {
+			if (!d_data)
+				continue;
+			er.er_name = GFS_POSIX_ACL_DEFAULT;
+			er.er_name_len = GFS_POSIX_ACL_DEFAULT_LEN;
+		}
+		er.er_data = data;
+		er.er_data_len = size;
+		er.er_type = GFS_EATYPE_SYS;
+
+		error = gfs_ea_acl_init(ip, &er);
+		if (error)
+			break;
+	}	
+
+	ip->i_alloc = NULL;
+
+	kfree(data);
+
+	return error;
+}
+
+/**
+ * gfs_acl_chmod -
+ * @ip:
+ * @attr:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_acl_chmod(struct gfs_inode *ip, struct iattr *attr)
+{
+	struct gfs_ea_request er;
+	struct gfs_ea_location el;
+	struct posix_acl *acl;
+	int error;
+
+	if (!ip->i_di.di_eattr)
+		goto simple;
+
+	memset(&er, 0, sizeof(struct gfs_ea_request));
+	er.er_name = GFS_POSIX_ACL_ACCESS;
+	er.er_name_len = GFS_POSIX_ACL_ACCESS_LEN;
+	er.er_type = GFS_EATYPE_SYS;
+
+	error = gfs_ea_find(ip, &er, &el);
+	if (error)
+		return error;
+	if (!el.el_ea)
+		goto simple;
+	if (!GFS_EA_DATA_LEN(el.el_ea))
+		goto simple;
+
+	er.er_data = kmalloc(GFS_EA_DATA_LEN(el.el_ea), GFP_KERNEL);
+	error = -ENOMEM;
+	if (!er.er_data)
+		goto out;
+
+	error = gfs_ea_get_copy(ip, &el, er.er_data);
+	if (error)
+		goto out_kfree;
+
+	acl = posix_acl_from_xattr(er.er_data, GFS_EA_DATA_LEN(el.el_ea));
+	if (IS_ERR(acl)) {
+		error = PTR_ERR(acl);
+		goto out_kfree;
+	} else if (!acl) {
+		kfree(er.er_data);
+		brelse(el.el_bh);
+		goto simple;
+	}
+
+	error = posix_acl_chmod_masq(acl, attr->ia_mode);
+	if (error)
+		goto out_acl;
+
+	posix_acl_to_xattr(acl, er.er_data, GFS_EA_DATA_LEN(el.el_ea));
+
+	error = gfs_ea_acl_chmod(ip, &el, attr, er.er_data);
+
+ out_acl:
+	posix_acl_release(acl);
+
+ out_kfree:
+	kfree(er.er_data);
+
+ out:
+	brelse(el.el_bh);
+
+	return error;
+
+ simple:
+	return gfs_setattr_simple(ip, attr);
+}
diff -pruN linux-2.6.9.orig/fs/gfs/acl.h linux-2.6.9.debug/fs/gfs/acl.h
--- linux-2.6.9.orig/fs/gfs/acl.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/acl.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,46 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __ACL_DOT_H__
+#define __ACL_DOT_H__
+
+#define GFS_POSIX_ACL_ACCESS  "posix_acl_access"
+#define GFS_POSIX_ACL_ACCESS_LEN (16)
+#define GFS_POSIX_ACL_DEFAULT "posix_acl_default"
+#define GFS_POSIX_ACL_DEFAULT_LEN (17)
+
+#define GFS_ACL_IS_ACCESS(name, len) \
+         ((len) == GFS_POSIX_ACL_ACCESS_LEN && \
+         !memcmp(GFS_POSIX_ACL_ACCESS, (name), (len)))
+
+#define GFS_ACL_IS_DEFAULT(name, len) \
+         ((len) == GFS_POSIX_ACL_DEFAULT_LEN && \
+         !memcmp(GFS_POSIX_ACL_DEFAULT, (name), (len)))
+
+struct gfs_ea_request;
+
+int gfs_acl_validate_set(struct gfs_inode *ip, int access,
+			 struct gfs_ea_request *er,
+			 int *remove, mode_t *mode);
+int gfs_acl_validate_remove(struct gfs_inode *ip, int access);
+int gfs_acl_get(struct gfs_inode *ip, int access, struct posix_acl **acl);
+int gfs_acl_new_prep(struct gfs_inode *dip,
+		     unsigned int type, mode_t *mode,
+		     void **a_data, void **d_data,
+		     unsigned int *size,
+		     unsigned int *blocks);
+int gfs_acl_new_init(struct gfs_inode *dip, struct gfs_inode *ip,
+		     void *a_data, void *d_data, unsigned int size);
+int gfs_acl_chmod(struct gfs_inode *ip, struct iattr *attr);
+
+#endif /* __ACL_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/bits.c linux-2.6.9.debug/fs/gfs/bits.c
--- linux-2.6.9.orig/fs/gfs/bits.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/bits.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,189 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+ * These routines are used by the resource group routines (rgrp.c)
+ * to keep track of block allocation.  Each block is represented by two
+ * bits.  One bit indicates whether or not the block is used.  (1=used,
+ * 0=free)  The other bit indicates whether or not the block contains a
+ * dinode or not.  (1=dinode, 0=data block) So, each byte represents
+ * GFS_NBBY (i.e. 4) blocks.  
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "bits.h"
+
+static const char valid_change[16] = {
+	        /* current */
+	/* n */ 0, 1, 1, 1,
+	/* e */ 1, 0, 0, 0,
+	/* w */ 1, 0, 0, 1,
+	        0, 0, 1, 0
+};
+
+/**
+ * gfs_setbit - Set a bit in the bitmaps
+ * @buffer: the buffer that holds the bitmaps
+ * @buflen: the length (in bytes) of the buffer
+ * @block: the block to set
+ * @new_state: the new state of the block
+ *
+ */
+
+void
+gfs_setbit(struct gfs_rgrpd *rgd,
+	   unsigned char *buffer, unsigned int buflen,
+	   uint32_t block, unsigned char new_state)
+{
+	unsigned char *byte, *end, cur_state;
+	unsigned int bit;
+
+	byte = buffer + (block / GFS_NBBY);
+	bit = (block % GFS_NBBY) * GFS_BIT_SIZE;
+	end = buffer + buflen;
+
+	gfs_assert(rgd->rd_sbd, byte < end,);
+
+	cur_state = (*byte >> bit) & GFS_BIT_MASK;
+
+	if (valid_change[new_state * 4 + cur_state]) {
+		*byte ^= cur_state << bit;
+		*byte |= new_state << bit;
+	} else
+		gfs_consist_rgrpd(rgd);
+}
+
+/**
+ * gfs_testbit - test a bit in the bitmaps
+ * @buffer: the buffer that holds the bitmaps
+ * @buflen: the length (in bytes) of the buffer
+ * @block: the block to read
+ *
+ */
+
+unsigned char
+gfs_testbit(struct gfs_rgrpd *rgd,
+	    unsigned char *buffer, unsigned int buflen, uint32_t block)
+{
+	unsigned char *byte, *end, cur_state;
+	unsigned int bit;
+
+	byte = buffer + (block / GFS_NBBY);
+	bit = (block % GFS_NBBY) * GFS_BIT_SIZE;
+	end = buffer + buflen;
+
+        gfs_assert(rgd->rd_sbd, byte < end,);
+
+	cur_state = (*byte >> bit) & GFS_BIT_MASK;
+
+	return cur_state;
+}
+
+/**
+ * gfs_bitfit - Search an rgrp's bitmap buffer to find a bit-pair representing
+ *       a block in a given allocation state.
+ * @buffer: the buffer that holds the bitmaps
+ * @buflen: the length (in bytes) of the buffer
+ * @goal: start search at this block's bit-pair (within @buffer)
+ * @old_state: GFS_BLKST_XXX the state of the block we're looking for;
+ *       bit 0 = alloc(1)/free(0), bit 1 = meta(1)/data(0)
+ * 
+ * Scope of @goal and returned block number is only within this bitmap buffer,
+ *   not entire rgrp or filesystem.
+ * @buffer will be offset from the actual beginning of a bitmap block buffer,
+ *   skipping any header structures.
+ *
+ * Return: the block number (bitmap buffer scope) that was found
+ */
+
+uint32_t
+gfs_bitfit(struct gfs_rgrpd *rgd,
+	   unsigned char *buffer, unsigned int buflen,
+	   uint32_t goal, unsigned char old_state)
+{
+	unsigned char *byte, *end, alloc;
+	uint32_t blk = goal;
+	unsigned int bit;
+
+	byte = buffer + (goal / GFS_NBBY);
+	bit = (goal % GFS_NBBY) * GFS_BIT_SIZE;
+	end = buffer + buflen;
+	alloc = (old_state & 1) ? 0 : 0x55;
+
+	while (byte < end) {
+		if ((*byte & 0x55) == alloc) {
+			blk += (8 - bit) >> 1;
+
+			bit = 0;
+			byte++;
+
+			continue;
+		}
+
+		if (((*byte >> bit) & GFS_BIT_MASK) == old_state)
+			return blk;
+
+		bit += GFS_BIT_SIZE;
+		if (bit >= 8) {
+			bit = 0;
+			byte++;
+		}
+
+		blk++;
+	}
+
+	return BFITNOENT;
+}
+
+/**
+ * gfs_bitcount - count the number of bits in a certain state
+ * @buffer: the buffer that holds the bitmaps
+ * @buflen: the length (in bytes) of the buffer
+ * @state: the state of the block we're looking for
+ *
+ * Returns: The number of bits
+ */
+
+uint32_t
+gfs_bitcount(struct gfs_rgrpd *rgd,
+	     unsigned char *buffer, unsigned int buflen,
+	     unsigned char state)
+{
+	unsigned char *byte = buffer;
+	unsigned char *end = buffer + buflen;
+	unsigned char state1 = state << 2;
+	unsigned char state2 = state << 4;
+	unsigned char state3 = state << 6;
+	uint32_t count = 0;
+
+	for (; byte < end; byte++) {
+		if (((*byte) & 0x03) == state)
+			count++;
+		if (((*byte) & 0x0C) == state1)
+			count++;
+		if (((*byte) & 0x30) == state2)
+			count++;
+		if (((*byte) & 0xC0) == state3)
+			count++;
+	}
+
+	return count;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/bits.h linux-2.6.9.debug/fs/gfs/bits.h
--- linux-2.6.9.orig/fs/gfs/bits.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/bits.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,32 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __BITS_DOT_H__
+#define __BITS_DOT_H__
+
+#define BFITNOENT (0xFFFFFFFF)
+
+void gfs_setbit(struct gfs_rgrpd *rgd,
+		unsigned char *buffer, unsigned int buflen,
+		uint32_t block, unsigned char new_state);
+unsigned char gfs_testbit(struct gfs_rgrpd *rgd,
+			  unsigned char *buffer, unsigned int buflen,
+			  uint32_t block);
+uint32_t gfs_bitfit(struct gfs_rgrpd *rgd,
+		    unsigned char *buffer, unsigned int buflen,
+		    uint32_t goal, unsigned char old_state);
+uint32_t gfs_bitcount(struct gfs_rgrpd *rgd,
+		      unsigned char *buffer, unsigned int buflen,
+		      unsigned char state);
+
+#endif /* __BITS_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/bmap.c linux-2.6.9.debug/fs/gfs/bmap.c
--- linux-2.6.9.orig/fs/gfs/bmap.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/bmap.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1406 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "bmap.h"
+#include "dio.h"
+#include "glock.h"
+#include "inode.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "trans.h"
+
+struct metapath {
+	unsigned int mp_list[GFS_MAX_META_HEIGHT];
+};
+
+typedef int (*block_call_t) (struct gfs_inode *ip, struct buffer_head *dibh,
+			     struct buffer_head *bh, uint64_t *top,
+			     uint64_t *bottom, unsigned int height,
+			     void *data);
+
+struct strip_mine {
+	int sm_first;
+	unsigned int sm_height;
+};
+
+/**
+ * gfs_unstuffer_sync - unstuff a dinode synchronously
+ * @ip: the inode
+ * @dibh: the dinode buffer
+ * @block: the block number that was allocated
+ * @private: not used
+ *
+ * Returns: errno
+ */
+
+int
+gfs_unstuffer_sync(struct gfs_inode *ip, struct buffer_head *dibh,
+		   uint64_t block, void *private)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *bh;
+	int error;
+
+	error = gfs_get_data_buffer(ip, block, TRUE, &bh);
+	if (error)
+		return error;
+
+	gfs_buffer_copy_tail(bh, 0, dibh, sizeof(struct gfs_dinode));
+
+	error = gfs_dwrite(sdp, bh, DIO_DIRTY | DIO_START | DIO_WAIT);
+
+	brelse(bh);
+
+	return error;
+}
+
+/**
+ * gfs_unstuffer_async - unstuff a dinode asynchronously
+ * @ip: the inode
+ * @dibh: the dinode buffer
+ * @block: the block number that was allocated
+ * @private: not used
+ *
+ * Returns: errno
+ */
+
+int
+gfs_unstuffer_async(struct gfs_inode *ip, struct buffer_head *dibh,
+		    uint64_t block, void *private)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *bh;
+	int error;
+
+	error = gfs_get_data_buffer(ip, block, TRUE, &bh);
+	if (error)
+		return error;
+
+	gfs_buffer_copy_tail(bh, 0, dibh, sizeof(struct gfs_dinode));
+
+	error = gfs_dwrite(sdp, bh, DIO_DIRTY);
+
+	brelse(bh);
+
+	return error;
+}
+
+/**
+ * gfs_unstuff_dinode - Unstuff a dinode when the data has grown too big
+ * @ip: The GFS inode to unstuff
+ * @unstuffer: the routine that handles unstuffing a non-zero length file
+ * @private: private data for the unstuffer
+ *
+ * This routine unstuffs a dinode and returns it to a "normal" state such 
+ * that the height can be grown in the traditional way.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_unstuff_dinode(struct gfs_inode *ip, gfs_unstuffer_t unstuffer,
+		   void *private)
+{
+	struct buffer_head *bh, *dibh;
+	uint64_t block = 0;
+	int journaled = gfs_is_jdata(ip);
+	int error;
+
+ 	down_write(&ip->i_rw_mutex);
+ 
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		goto out;
+
+	if (ip->i_di.di_size) {
+		/* Get a free block, fill it with the stuffed data,
+		   and write it out to disk */
+
+		if (journaled) {
+			error = gfs_metaalloc(ip, &block);
+			if (error)
+				goto out_brelse;
+
+			error = gfs_get_data_buffer(ip, block, TRUE, &bh);
+			if (error)
+				goto out_brelse;
+
+			gfs_buffer_copy_tail(bh, sizeof(struct gfs_meta_header),
+					     dibh, sizeof(struct gfs_dinode));
+
+			brelse(bh);
+		} else {
+			gfs_blkalloc(ip, &block);
+
+			error = unstuffer(ip, dibh, block, private);
+			if (error)
+				goto out_brelse;
+		}
+	}
+
+	/*  Set up the pointer to the new block  */
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+
+	gfs_buffer_clear_tail(dibh, sizeof(struct gfs_dinode));
+
+	if (ip->i_di.di_size) {
+		*(uint64_t *)(dibh->b_data + sizeof(struct gfs_dinode)) = cpu_to_gfs64(block);
+		ip->i_di.di_blocks++;
+	}
+
+	ip->i_di.di_height = 1;
+
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+
+ out_brelse:
+	brelse(dibh);
+
+ out:
+ 	up_write(&ip->i_rw_mutex);
+
+	return error;
+}
+
+/**
+ * calc_tree_height - Calculate the height of a metadata tree
+ * @ip: The GFS inode
+ * @size: The proposed size of the file
+ *
+ * Work out how tall a metadata tree needs to be in order to accommodate a
+ * file of a particular size. If size is less than the current size of
+ * the inode, then the current size of the inode is used instead of the
+ * supplied one.
+ *
+ * Returns: the height the tree should be
+ */
+
+static unsigned int
+calc_tree_height(struct gfs_inode *ip, uint64_t size)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	uint64_t *arr;
+	unsigned int max, height;
+
+	if (ip->i_di.di_size > size)
+		size = ip->i_di.di_size;
+
+	if (gfs_is_jdata(ip)) {
+		arr = sdp->sd_jheightsize;
+		max = sdp->sd_max_jheight;
+	} else {
+		arr = sdp->sd_heightsize;
+		max = sdp->sd_max_height;
+	}
+
+	for (height = 0; height < max; height++)
+		if (arr[height] >= size)
+			break;
+
+	return height;
+}
+
+/**
+ * build_height - Build a metadata tree of the requested height
+ * @ip: The GFS inode
+ * @height: The height to build to
+ *
+ * This routine makes sure that the metadata tree is tall enough to hold
+ * "size" bytes of data.
+ *
+ * Returns: errno
+ */
+
+static int
+build_height(struct gfs_inode *ip, int height)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *bh, *dibh;
+	uint64_t block, *bp;
+	unsigned int x;
+	int new_block;
+	int error;
+
+	while (ip->i_di.di_height < height) {
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (error)
+			return error;
+
+		new_block = FALSE;
+		bp = (uint64_t *)(dibh->b_data + sizeof(struct gfs_dinode));
+		for (x = 0; x < sdp->sd_diptrs; x++, bp++)
+			if (*bp) {
+				new_block = TRUE;
+				break;
+			}
+
+		if (new_block) {
+			/*  Get a new block, fill it with the old direct pointers,
+			    and write it out  */
+
+			error = gfs_metaalloc(ip, &block);
+			if (error)
+				goto fail;
+
+			error = gfs_dread(ip->i_gl, block,
+					  DIO_NEW | DIO_START | DIO_WAIT, &bh);
+			if (error)
+				goto fail;
+
+			gfs_trans_add_bh(ip->i_gl, bh);
+			gfs_metatype_set(bh,
+					 GFS_METATYPE_IN,
+					 GFS_FORMAT_IN);
+			memset(bh->b_data + sizeof(struct gfs_meta_header),
+			       0,
+			       sizeof(struct gfs_indirect) -
+			       sizeof(struct gfs_meta_header));
+			gfs_buffer_copy_tail(bh, sizeof(struct gfs_indirect),
+					     dibh, sizeof(struct gfs_dinode));
+
+			brelse(bh);
+		}
+
+		/*  Set up the new direct pointer and write it out to disk  */
+
+		gfs_trans_add_bh(ip->i_gl, dibh);
+
+		gfs_buffer_clear_tail(dibh, sizeof(struct gfs_dinode));
+
+		if (new_block) {
+			*(uint64_t *)(dibh->b_data + sizeof(struct gfs_dinode)) = cpu_to_gfs64(block);
+			ip->i_di.di_blocks++;
+		}
+
+		ip->i_di.di_height++;
+
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	return 0;
+
+ fail:
+	brelse(dibh);
+
+	return error;
+}
+
+/**
+ * find_metapath - Find path through the metadata tree
+ * @ip: The inode pointer
+ * @mp: The metapath to return the result in
+ * @block: The disk block to look up
+ *
+ *   This routine returns a struct metapath structure that defines a path through
+ *   the metadata of inode "ip" to get to block "block".
+ *
+ *   Example:
+ *   Given:  "ip" is a height 3 file, "offset" is 101342453, and this is a
+ *   filesystem with a blocksize of 4096.
+ *
+ *   find_metapath() would return a struct metapath structure set to:
+ *   mp_offset = 101342453, mp_height = 3, mp_list[0] = 0, mp_list[1] = 48,
+ *   and mp_list[2] = 165.
+ *
+ *   That means that in order to get to the block containing the byte at
+ *   offset 101342453, we would load the indirect block pointed to by pointer
+ *   0 in the dinode.  We would then load the indirect block pointed to by
+ *   pointer 48 in that indirect block.  We would then load the data block
+ *   pointed to by pointer 165 in that indirect block.
+ *
+ *             ----------------------------------------
+ *             | Dinode |                             |
+ *             |        |                            4|
+ *             |        |0 1 2 3 4 5                 9|
+ *             |        |                            6|
+ *             ----------------------------------------
+ *                       |
+ *                       |
+ *                       V
+ *             ----------------------------------------
+ *             | Indirect Block                       |
+ *             |                                     5|
+ *             |            4 4 4 4 4 5 5            1|
+ *             |0           5 6 7 8 9 0 1            2|
+ *             ----------------------------------------
+ *                                |
+ *                                |
+ *                                V
+ *             ----------------------------------------
+ *             | Indirect Block                       |
+ *             |                         1 1 1 1 1   5|
+ *             |                         6 6 6 6 6   1|
+ *             |0                        3 4 5 6 7   2|
+ *             ----------------------------------------
+ *                                           |
+ *                                           |
+ *                                           V
+ *             ----------------------------------------
+ *             | Data block containing offset         |
+ *             |            101342453                 |
+ *             |                                      |
+ *             |                                      |
+ *             ----------------------------------------
+ *
+ */
+
+static struct metapath *
+find_metapath(struct gfs_inode *ip, uint64_t block)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct metapath *mp;
+	uint64_t b = block;
+	unsigned int i;
+
+	mp = gmalloc(sizeof(struct metapath));
+	memset(mp, 0, sizeof(struct metapath));
+
+	for (i = ip->i_di.di_height; i--;)
+		mp->mp_list[i] = do_div(b, sdp->sd_inptrs);
+
+	return mp;
+}
+
+/**
+ * metapointer - Return pointer to start of metadata in a buffer
+ * @bh: The buffer
+ * @height: The metadata height (0 = dinode)
+ * @mp: The metapath 
+ *
+ * Return a pointer to the block number of the next height of the metadata
+ * tree given a buffer containing the pointer to the current height of the
+ * metadata tree.
+ */
+
+static __inline__ uint64_t *
+metapointer(struct buffer_head *bh, unsigned int height, struct metapath *mp)
+{
+	unsigned int head_size = (height > 0) ?
+		sizeof(struct gfs_indirect) : sizeof(struct gfs_dinode);
+
+	return ((uint64_t *)(bh->b_data + head_size)) + mp->mp_list[height];
+}
+
+/**
+ * get_metablock - Get the next metadata block in metadata tree
+ * @ip: The GFS inode
+ * @bh: Buffer containing the pointers to metadata blocks
+ * @height: The height of the tree (0 = dinode)
+ * @mp: The metapath
+ * @create: Non-zero if we may create a new meatdata block
+ * @new: Used to indicate if we did create a new metadata block
+ * @block: the returned disk block number
+ *
+ * Given a metatree, complete to a particular height, checks to see if the next
+ * height of the tree exists. If not the next height of the tree is created.
+ * The block number of the next height of the metadata tree is returned.
+ *
+ * Returns: errno
+ */
+
+static int
+get_metablock(struct gfs_inode *ip,
+	      struct buffer_head *bh, unsigned int height, struct metapath *mp,
+	      int create, int *new, uint64_t *block)
+{
+	uint64_t *ptr = metapointer(bh, height, mp);
+	int error;
+
+	if (*ptr) {
+		*block = gfs64_to_cpu(*ptr);
+		return 0;
+	}
+
+	*block = 0;
+
+	if (!create)
+		return 0;
+
+	error = gfs_metaalloc(ip, block);
+	if (error)
+		return error;
+
+	gfs_trans_add_bh(ip->i_gl, bh);
+
+	*ptr = cpu_to_gfs64(*block);
+	ip->i_di.di_blocks++;
+
+	*new = 1;
+
+	return 0;
+}
+
+/**
+ * get_datablock - Get datablock number from metadata block
+ * @ip: The GFS inode
+ * @bh: The buffer containing pointers to datablocks
+ * @mp: The metapath
+ * @create: Non-zero if we may create a new data block
+ * @new: Used to indicate if we created a new data block
+ * @block: the returned disk block number
+ *
+ * Given a fully built metadata tree, checks to see if a particular data
+ * block exists. It is created if it does not exist and the block number
+ * on disk is returned.
+ *
+ * Returns: errno
+ */
+
+static int
+get_datablock(struct gfs_inode *ip,
+	      struct buffer_head *bh, struct metapath *mp,
+	      int create, int *new, uint64_t *block)
+{
+	uint64_t *ptr = metapointer(bh, ip->i_di.di_height - 1, mp);
+
+	if (*ptr) {
+		*block = gfs64_to_cpu(*ptr);
+		return 0;
+	}
+
+	*block = 0;
+
+	if (!create)
+		return 0;
+
+	if (gfs_is_jdata(ip)) {
+		int error;
+		error = gfs_metaalloc(ip, block);
+		if (error)
+			return error;
+	} else
+		gfs_blkalloc(ip, block);
+
+	gfs_trans_add_bh(ip->i_gl, bh);
+
+	*ptr = cpu_to_gfs64(*block);
+	ip->i_di.di_blocks++;
+
+	*new = 1;
+
+	return 0;
+}
+
+/**
+ * gfs_block_map - Map a block from an inode to a disk block
+ * @ip: The GFS inode
+ * @lblock: The logical block number
+ * @new: Value/Result argument (1 = may create/did create new blocks)
+ * @dblock: the disk block number of the start of an extent
+ * @extlen: the size of the extent
+ *
+ * Find the block number on the current device which corresponds to an
+ * inode's block. If the block had to be created, "new" will be set.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_block_map(struct gfs_inode *ip,
+	      uint64_t lblock, int *new,
+	      uint64_t *dblock, uint32_t *extlen)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *bh;
+	struct metapath *mp;
+	int create = *new;
+	unsigned int bsize;
+	unsigned int height;
+	unsigned int end_of_metadata;
+	unsigned int x;
+	int error = 0;
+
+	*new = 0;
+	*dblock = 0;
+	if (extlen)
+		*extlen = 0;
+
+	if (create)
+		down_write(&ip->i_rw_mutex);
+	else
+		down_read(&ip->i_rw_mutex);
+
+	if (gfs_assert_warn(sdp, !gfs_is_stuffed(ip)))
+		goto out;
+
+	bsize = (gfs_is_jdata(ip)) ? sdp->sd_jbsize : sdp->sd_sb.sb_bsize;
+
+	height = calc_tree_height(ip, (lblock + 1) * bsize);
+	if (ip->i_di.di_height < height) {
+		if (!create)
+			goto out;
+
+		error = build_height(ip, height);
+		if (error)
+			goto out;
+	}
+
+	mp = find_metapath(ip, lblock);
+	end_of_metadata = ip->i_di.di_height - 1;
+
+	error = gfs_get_inode_buffer(ip, &bh);
+	if (error)
+		goto out_kfree;
+
+	for (x = 0; x < end_of_metadata; x++) {
+		error = get_metablock(ip, bh, x, mp, create, new, dblock);
+		brelse(bh);
+		if (error || !*dblock)
+			goto out_kfree;
+
+		error = gfs_get_meta_buffer(ip, x + 1, *dblock, *new, &bh);
+		if (error)
+			goto out_kfree;
+	}
+
+	error = get_datablock(ip, bh, mp, create, new, dblock);
+	if (error) {
+		brelse(bh);
+		goto out_kfree;
+	}
+
+	if (extlen && *dblock) {
+		*extlen = 1;
+
+		if (!*new) {
+			uint64_t tmp_dblock;
+			int tmp_new;
+			unsigned int nptrs;
+
+			nptrs = (end_of_metadata) ? sdp->sd_inptrs : sdp->sd_diptrs;
+
+			while (++mp->mp_list[end_of_metadata] < nptrs) {
+				get_datablock(ip, bh, mp,
+					      FALSE, &tmp_new,
+					      &tmp_dblock);
+
+				if (*dblock + *extlen != tmp_dblock)
+					break;
+
+				(*extlen)++;
+			}
+		}
+	}
+
+	brelse(bh);
+
+	if (*new) {
+		error = gfs_get_inode_buffer(ip, &bh);
+		if (!error) {
+			gfs_trans_add_bh(ip->i_gl, bh);
+			gfs_dinode_out(&ip->i_di, bh->b_data);
+			brelse(bh);
+		}
+	}
+
+ out_kfree:
+	kfree(mp);
+
+ out:
+	if (create)
+		up_write(&ip->i_rw_mutex);
+	else
+		up_read(&ip->i_rw_mutex);
+ 
+	return error;
+}
+
+/**
+ * do_grow - Make a file look bigger than it is
+ * @ip: the inode
+ * @size: the size to set the file to
+ *
+ * Called with an exclusive lock on @ip.
+ *
+ * Returns: errno
+ */
+
+static int
+do_grow(struct gfs_inode *ip, uint64_t size)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al;
+	struct buffer_head *dibh;
+	unsigned int h;
+	int journaled = gfs_is_jdata(ip);
+	int error;
+
+	al = gfs_alloc_get(ip);
+
+	error = gfs_quota_lock_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto out;
+
+	error = gfs_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid);
+	if (error)
+		goto out_gunlock_q;
+
+	if (journaled)
+		al->al_requested_meta = sdp->sd_max_height + 1;
+	else {
+		al->al_requested_meta = sdp->sd_max_height;
+		al->al_requested_data = 1;
+	}
+
+	error = gfs_inplace_reserve(ip);
+	if (error)
+		goto out_gunlock_q;
+
+	/* Trans may require:
+	   Full extention of the metadata tree, block allocation,
+	   a dinode modification, and a quota change */
+
+	error = gfs_trans_begin(sdp,
+				sdp->sd_max_height + al->al_rgd->rd_ri.ri_length +
+				1 + !!journaled,
+				1);
+	if (error)
+		goto out_ipres;
+
+	if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode)) {
+		if (gfs_is_stuffed(ip)) {
+			error = gfs_unstuff_dinode(ip, gfs_unstuffer_sync, NULL);
+			if (error)
+				goto out_end_trans;
+		}
+
+		h = calc_tree_height(ip, size);
+		if (ip->i_di.di_height < h) {
+			down_write(&ip->i_rw_mutex);
+			error = build_height(ip, h);
+			up_write(&ip->i_rw_mutex);
+			if (error)
+				goto out_end_trans;
+		}
+	}
+
+	ip->i_di.di_size = size;
+	ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		goto out_end_trans;
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+	brelse(dibh);
+
+ out_end_trans:
+	gfs_trans_end(sdp);
+
+ out_ipres:
+	gfs_inplace_release(ip);
+
+ out_gunlock_q:
+	gfs_quota_unlock_m(ip);
+
+ out:
+	gfs_alloc_put(ip);
+
+	return error;
+}
+
+/**
+ * recursive_scan - recursively scan through the end of a file
+ * @ip: the inode
+ * @dibh: the dinode buffer
+ * @mp: the path through the metadata to the point to start
+ * @height: the height the recursion is at
+ * @block: the indirect block to look at
+ * @first: TRUE if this is the first block
+ * @bc: the call to make for each piece of metadata
+ * @data: data opaque to this function to pass to @bc
+ *
+ * When this is first called @height and @block should be zero and
+ * @first should be TRUE.
+ *
+ * Returns: errno
+ */
+
+static int
+recursive_scan(struct gfs_inode *ip, struct buffer_head *dibh,
+	       struct metapath *mp, unsigned int height, uint64_t block,
+	       int first, block_call_t bc, void *data)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *bh = NULL;
+	uint64_t *top, *bottom;
+	uint64_t bn;
+	int error;
+
+	if (!height) {
+		error = gfs_get_inode_buffer(ip, &bh);
+		if (error)
+			goto fail;
+		dibh = bh;
+
+		top = (uint64_t *)(bh->b_data + sizeof(struct gfs_dinode)) +
+			mp->mp_list[0];
+		bottom = (uint64_t *)(bh->b_data + sizeof(struct gfs_dinode)) +
+			sdp->sd_diptrs;
+	} else {
+		error = gfs_get_meta_buffer(ip, height, block, FALSE, &bh);
+		if (error)
+			goto fail;
+
+		top = (uint64_t *)(bh->b_data + sizeof(struct gfs_indirect)) +
+			((first) ? mp->mp_list[height] : 0);
+		bottom = (uint64_t *)(bh->b_data + sizeof(struct gfs_indirect)) +
+			sdp->sd_inptrs;
+	}
+
+	error = bc(ip, dibh, bh, top, bottom, height, data);
+	if (error)
+		goto fail;
+
+	if (height < ip->i_di.di_height - 1)
+		for (; top < bottom; top++, first = FALSE) {
+			if (!*top)
+				continue;
+
+			bn = gfs64_to_cpu(*top);
+
+			error = recursive_scan(ip, dibh, mp,
+					       height + 1, bn, first,
+					       bc, data);
+			if (error)
+				goto fail;
+		}
+
+	brelse(bh);
+
+	return 0;
+
+ fail:
+	if (bh)
+		brelse(bh);
+
+	return error;
+}
+
+/**
+ * do_strip - Look for a layer a particular layer of the file and strip it off
+ * @ip: the inode
+ * @dibh: the dinode buffer
+ * @bh: A buffer of pointers
+ * @top: The first pointer in the buffer
+ * @bottom: One more than the last pointer
+ * @height: the height this buffer is at
+ * @data: a pointer to a struct strip_mine
+ *
+ * Returns: errno
+ */
+
+static int
+do_strip(struct gfs_inode *ip, struct buffer_head *dibh,
+	 struct buffer_head *bh, uint64_t *top, uint64_t *bottom,
+	 unsigned int height, void *data)
+{
+	struct strip_mine *sm = (struct strip_mine *)data;
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_rgrp_list rlist;
+	uint64_t bn, bstart;
+	uint32_t blen;
+	uint64_t *p;
+	unsigned int rg_blocks = 0;
+	int metadata;
+	int x;
+	int error;
+
+	if (!*top)
+		sm->sm_first = FALSE;
+
+	if (height != sm->sm_height)
+		return 0;
+
+	if (sm->sm_first) {
+		top++;
+		sm->sm_first = FALSE;
+	}
+
+	metadata = (height != ip->i_di.di_height - 1) || gfs_is_jdata(ip);
+
+	error = gfs_rindex_hold(sdp, &ip->i_alloc->al_ri_gh);
+	if (error)
+		return error;
+
+	memset(&rlist, 0, sizeof(struct gfs_rgrp_list));
+	bstart = 0;
+	blen = 0;
+
+	for (p = top; p < bottom; p++) {
+		if (!*p)
+			continue;
+
+		bn = gfs64_to_cpu(*p);
+
+		if (bstart + blen == bn)
+			blen++;
+		else {
+			if (bstart)
+				gfs_rlist_add(sdp, &rlist, bstart);
+
+			bstart = bn;
+			blen = 1;
+		}
+	}
+
+	if (bstart)
+		gfs_rlist_add(sdp, &rlist, bstart);
+	else
+		goto out; /* Nothing to do */
+
+	gfs_rlist_alloc(&rlist, LM_ST_EXCLUSIVE, 0);
+
+	for (x = 0; x < rlist.rl_rgrps; x++) {
+		struct gfs_rgrpd *rgd;
+		rgd = gl2rgd(rlist.rl_ghs[x].gh_gl);
+		rg_blocks += rgd->rd_ri.ri_length;
+	}
+
+	error = gfs_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs);
+	if (error)
+		goto out_rlist;
+
+	/* Trans may require:
+	   All the bitmaps that were reserved. 
+	   One block for the dinode.
+	   One block for the indirect block being cleared.
+	   One block for a quota change. */
+
+	error = gfs_trans_begin(sdp, rg_blocks + 2, 1);
+	if (error)
+		goto out_rg_gunlock;
+
+	down_write(&ip->i_rw_mutex);
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+	gfs_trans_add_bh(ip->i_gl, bh);
+
+	bstart = 0;
+	blen = 0;
+
+	for (p = top; p < bottom; p++) {
+		if (!*p)
+			continue;
+
+		bn = gfs64_to_cpu(*p);
+
+		if (bstart + blen == bn)
+			blen++;
+		else {
+			if (bstart) {
+				if (metadata)
+					gfs_metafree(ip, bstart, blen);
+				else
+					gfs_blkfree(ip, bstart, blen);
+			}
+
+			bstart = bn;
+			blen = 1;
+		}
+
+		*p = 0;
+		if (!ip->i_di.di_blocks)
+			gfs_consist_inode(ip);
+		ip->i_di.di_blocks--;
+	}
+	if (bstart) {
+		if (metadata)
+			gfs_metafree(ip, bstart, blen);
+		else
+			gfs_blkfree(ip, bstart, blen);
+	}
+
+	ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
+
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+
+	up_write(&ip->i_rw_mutex);
+
+	gfs_trans_end(sdp);
+
+ out_rg_gunlock:
+	gfs_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs);
+
+ out_rlist:
+	gfs_rlist_free(&rlist);
+
+ out:
+	gfs_glock_dq_uninit(&ip->i_alloc->al_ri_gh);
+
+	return error;
+}
+
+/**
+ * gfs_truncator_default - truncate a partial data block
+ * @ip: the inode
+ * @size: the size the file should be
+ *
+ * Returns: errno
+ */
+
+int
+gfs_truncator_default(struct gfs_inode *ip, uint64_t size)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *bh;
+	uint64_t bn;
+	int not_new = 0;
+	int error;
+
+	error = gfs_block_map(ip, size >> sdp->sd_sb.sb_bsize_shift, &not_new,
+			      &bn, NULL);
+	if (error)
+		return error;
+	if (!bn)
+		return 0;
+
+	error = gfs_get_data_buffer(ip, bn, FALSE, &bh);
+	if (error)
+		return error;
+
+	gfs_buffer_clear_tail(bh, size & (sdp->sd_sb.sb_bsize - 1));
+
+	error = gfs_dwrite(sdp, bh, DIO_DIRTY);
+
+	brelse(bh);
+
+	return error;
+}
+
+/**
+ * truncator_journaled - truncate a partial data block
+ * @ip: the inode
+ * @size: the size the file should be
+ *
+ * Returns: errno
+ */
+
+static int
+truncator_journaled(struct gfs_inode *ip, uint64_t size)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *bh;
+	uint64_t lbn, dbn;
+	uint32_t off;
+	int not_new = 0;
+	int error;
+
+	lbn = size;
+	off = do_div(lbn, sdp->sd_jbsize);
+
+	error = gfs_block_map(ip, lbn, &not_new, &dbn, NULL);
+	if (error)
+		return error;
+	if (!dbn)
+		return 0;
+
+	error = gfs_trans_begin(sdp, 1, 0);
+	if (error)
+		return error;
+
+	error = gfs_get_data_buffer(ip, dbn, FALSE, &bh);
+	if (!error) {
+		gfs_trans_add_bh(ip->i_gl, bh);
+		gfs_buffer_clear_tail(bh,
+				      sizeof(struct gfs_meta_header) +
+				      off);
+		brelse(bh);
+	}
+
+	gfs_trans_end(sdp);
+
+	return error;
+}
+
+/**
+ * gfs_shrink - make a file smaller
+ * @ip: the inode
+ * @size: the size to make the file
+ * @truncator: function to truncate the last partial block
+ *
+ * Called with an exclusive lock on @ip.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_shrink(struct gfs_inode *ip, uint64_t size, gfs_truncator_t truncator)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_holder ri_gh;
+	struct gfs_rgrpd *rgd;
+	struct buffer_head *dibh;
+	uint64_t block;
+	unsigned int height;
+	int journaled = gfs_is_jdata(ip);
+	int error;
+
+	if (!size)
+		block = 0;
+	else if (journaled) {
+		block = size - 1;
+		do_div(block, sdp->sd_jbsize);
+	}
+	else
+		block = (size - 1) >> sdp->sd_sb.sb_bsize_shift;
+
+	/*  Get rid of all the data/metadata blocks  */
+
+	height = ip->i_di.di_height;
+	if (height) {
+		struct metapath *mp = find_metapath(ip, block);
+		gfs_alloc_get(ip);
+
+		error = gfs_quota_hold_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+		if (error) {
+			gfs_alloc_put(ip);
+			kfree(mp);
+			return error;
+		}
+
+		while (height--) {
+			struct strip_mine sm;
+
+			sm.sm_first = (size) ? TRUE : FALSE;
+			sm.sm_height = height;
+
+			error = recursive_scan(ip, NULL, mp, 0, 0, TRUE,
+					       do_strip, &sm);
+			if (error) {
+				gfs_quota_unhold_m(ip);
+				gfs_alloc_put(ip);
+				kfree(mp);
+				return error;
+			}
+		}
+
+		gfs_quota_unhold_m(ip);
+		gfs_alloc_put(ip);
+		kfree(mp);
+	}
+
+	/*  If we truncated in the middle of a block, zero out the leftovers.  */
+
+	if (gfs_is_stuffed(ip)) {
+		/*  Do nothing  */
+	} else if (journaled) {
+		if (do_mod(size, sdp->sd_jbsize)) {
+			error = truncator_journaled(ip, size);
+			if (error)
+				return error;
+		}
+	} else if (size & (uint64_t)(sdp->sd_sb.sb_bsize - 1)) {
+		error = truncator(ip, size);
+		if (error)
+			return error;
+	}
+
+	/*  Set the new size (and possibly the height)  */
+
+	if (!size) {
+		error = gfs_rindex_hold(sdp, &ri_gh);
+		if (error)
+			return error;
+	}
+
+	error = gfs_trans_begin(sdp, 1, 0);
+	if (error)
+		goto out;
+
+	down_write(&ip->i_rw_mutex);
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		goto out_end_trans;
+
+	if (!size) {
+		ip->i_di.di_height = 0;
+
+		rgd = gfs_blk2rgrpd(sdp, ip->i_num.no_addr);
+		if (!rgd) {
+			gfs_consist_inode(ip);
+			error = -EIO;
+			goto out_end_trans;
+		}
+
+		ip->i_di.di_goal_rgrp = rgd->rd_ri.ri_addr;
+		ip->i_di.di_goal_dblk =
+			ip->i_di.di_goal_mblk =
+			ip->i_num.no_addr - rgd->rd_ri.ri_data1;
+	}
+
+	ip->i_di.di_size = size;
+	ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+
+	if (!ip->i_di.di_height &&
+	    size < sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode))
+		gfs_buffer_clear_tail(dibh, sizeof(struct gfs_dinode) + size);
+
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+	brelse(dibh);
+
+ out_end_trans:
+	up_write(&ip->i_rw_mutex);
+
+	gfs_trans_end(sdp);
+
+ out:
+	if (!size)
+		gfs_glock_dq_uninit(&ri_gh);
+
+	return error;
+}
+
+/**
+ * do_same - truncate to same size (update time stamps)
+ * @ip: 
+ *
+ * Returns: errno
+ */
+
+static int
+do_same(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *dibh;
+	int error;
+
+	error = gfs_trans_begin(sdp, 1, 0);
+	if (error)
+		return error;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		goto out;
+
+	ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+
+	brelse(dibh);
+
+ out:
+	gfs_trans_end(sdp);
+
+	return error;
+}
+
+/**
+ * gfs_truncatei - make a file a give size
+ * @ip: the inode
+ * @size: the size to make the file
+ * @truncator: function to truncate the last partial block
+ *
+ * The file size can grow, shrink, or stay the same size.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_truncatei(struct gfs_inode *ip, uint64_t size,
+	      gfs_truncator_t truncator)
+{
+	if (gfs_assert_warn(ip->i_sbd, ip->i_di.di_type == GFS_FILE_REG))
+		return -EINVAL;
+
+	if (size == ip->i_di.di_size)
+		return do_same(ip);
+	else if (size > ip->i_di.di_size)
+		return do_grow(ip, size);
+	else
+		return gfs_shrink(ip, size, truncator);
+}
+
+/**
+ * gfs_write_calc_reserv - calculate the number of blocks needed to write to a file
+ * @ip: the file
+ * @len: the number of bytes to be written to the file
+ * @data_blocks: returns the number of data blocks required
+ * @ind_blocks: returns the number of indirect blocks required
+ *
+ */
+
+void
+gfs_write_calc_reserv(struct gfs_inode *ip, unsigned int len,
+		      unsigned int *data_blocks, unsigned int *ind_blocks)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	unsigned int tmp;
+
+	if (gfs_is_jdata(ip)) {
+		*data_blocks = DIV_RU(len, sdp->sd_jbsize) + 2;
+		*ind_blocks = 3 * (sdp->sd_max_jheight - 1);
+	} else {
+		*data_blocks = (len >> sdp->sd_sb.sb_bsize_shift) + 3;
+		*ind_blocks = 3 * (sdp->sd_max_height - 1);
+	}
+
+	for (tmp = *data_blocks; tmp > sdp->sd_diptrs;) {
+		tmp = DIV_RU(tmp, sdp->sd_inptrs);
+		*ind_blocks += tmp;
+	}
+}
+
+/**
+ * gfs_write_alloc_required - figure out if a write is going to require an allocation
+ * @ip: the file being written to
+ * @offset: the offset to write to
+ * @len: the number of bytes being written
+ * @alloc_required: the int is set to TRUE if an alloc is required, FALSE otherwise
+ *
+ * Returns: errno
+ */
+
+int
+gfs_write_alloc_required(struct gfs_inode *ip,
+			 uint64_t offset, unsigned int len,
+			 int *alloc_required)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	uint64_t lblock, lblock_stop, dblock;
+	uint32_t extlen;
+	int not_new = FALSE;
+	int error = 0;
+
+	*alloc_required = FALSE;
+
+	if (!len)
+		return 0;
+
+	if (gfs_is_stuffed(ip)) {
+		if (offset + len > sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode))
+			*alloc_required = TRUE;
+		return 0;
+	}
+
+	if (gfs_is_jdata(ip)) {
+		unsigned int bsize = sdp->sd_jbsize;
+		lblock = offset;
+		do_div(lblock, bsize);
+		lblock_stop = offset + len + bsize - 1;
+		do_div(lblock_stop, bsize);
+	} else {
+		unsigned int shift = sdp->sd_sb.sb_bsize_shift;
+		lblock = offset >> shift;
+		lblock_stop = (offset + len + sdp->sd_sb.sb_bsize - 1) >> shift;
+	}
+
+	for (; lblock < lblock_stop; lblock += extlen) {
+		error = gfs_block_map(ip, lblock, &not_new, &dblock, &extlen);
+		if (error)
+			return error;
+
+		if (!dblock) {
+			*alloc_required = TRUE;
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * do_gfm - Copy out the dinode/indirect blocks of a file
+ * @ip: the file
+ * @dibh: the dinode buffer
+ * @bh: the indirect buffer we're looking at
+ * @top: the first pointer in the block
+ * @bottom: one more than the last pointer in the block
+ * @height: the height the block is at
+ * @data: a pointer to a struct gfs_user_buffer structure
+ *
+ * If this is a journaled file, copy out the data too.
+ *
+ * Returns: errno
+ */
+
+static int
+do_gfm(struct gfs_inode *ip, struct buffer_head *dibh,
+       struct buffer_head *bh, uint64_t *top, uint64_t *bottom,
+       unsigned int height, void *data)
+{
+	struct gfs_user_buffer *ub = (struct gfs_user_buffer *)data;
+	int error;
+
+	error = gfs_add_bh_to_ub(ub, bh);
+	if (error)
+		return error;
+
+	if (ip->i_di.di_type != GFS_FILE_DIR ||
+	    height + 1 != ip->i_di.di_height)
+		return 0;
+
+	for (; top < bottom; top++)
+		if (*top) {
+			struct buffer_head *data_bh;
+
+			error = gfs_dread(ip->i_gl, gfs64_to_cpu(*top),
+					  DIO_START | DIO_WAIT,
+					  &data_bh);
+			if (error)
+				return error;
+
+			error = gfs_add_bh_to_ub(ub, data_bh);
+
+			brelse(data_bh);
+
+			if (error)
+				return error;
+		}
+
+	return 0;
+}
+
+/**
+ * gfs_get_file_meta - return all the metadata for a file
+ * @ip: the file
+ * @ub: the structure representing the meta
+ *
+ * Returns: errno
+ */
+
+int
+gfs_get_file_meta(struct gfs_inode *ip, struct gfs_user_buffer *ub)
+{
+	int error;
+
+	if (gfs_is_stuffed(ip)) {
+		struct buffer_head *dibh;
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (!error) {
+			error = gfs_add_bh_to_ub(ub, dibh);
+			brelse(dibh);
+		}
+	} else {
+		struct metapath *mp = find_metapath(ip, 0);
+		error = recursive_scan(ip, NULL, mp, 0, 0, TRUE, do_gfm, ub);
+		kfree(mp);
+	}
+
+	return error;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/bmap.h linux-2.6.9.debug/fs/gfs/bmap.h
--- linux-2.6.9.orig/fs/gfs/bmap.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/bmap.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,48 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __BMAP_DOT_H__
+#define __BMAP_DOT_H__
+
+typedef int (*gfs_unstuffer_t) (struct gfs_inode * ip,
+				struct buffer_head * dibh, uint64_t block,
+				void *private);
+
+int gfs_unstuffer_sync(struct gfs_inode *ip, struct buffer_head *dibh,
+		       uint64_t block, void *private);
+int gfs_unstuffer_async(struct gfs_inode *ip, struct buffer_head *dibh,
+			uint64_t block, void *private);
+
+int gfs_unstuff_dinode(struct gfs_inode *ip, gfs_unstuffer_t unstuffer,
+		       void *private);
+
+int gfs_block_map(struct gfs_inode *ip,
+		  uint64_t lblock, int *new,
+		  uint64_t *dblock, uint32_t *extlen);
+
+typedef int (*gfs_truncator_t) (struct gfs_inode * ip, uint64_t size);
+
+int gfs_truncator_default(struct gfs_inode *ip, uint64_t size);
+
+int gfs_shrink(struct gfs_inode *ip, uint64_t size, gfs_truncator_t truncator);
+int gfs_truncatei(struct gfs_inode *ip, uint64_t size,
+		  gfs_truncator_t truncator);
+
+void gfs_write_calc_reserv(struct gfs_inode *ip, unsigned int len,
+			   unsigned int *data_blocks, unsigned int *ind_blocks);
+int gfs_write_alloc_required(struct gfs_inode *ip, uint64_t offset,
+			     unsigned int len, int *alloc_required);
+
+int gfs_get_file_meta(struct gfs_inode *ip, struct gfs_user_buffer *ub);
+
+#endif /* __BMAP_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/daemon.c linux-2.6.9.debug/fs/gfs/daemon.c
--- linux-2.6.9.orig/fs/gfs/daemon.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/daemon.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,273 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "daemon.h"
+#include "glock.h"
+#include "log.h"
+#include "quota.h"
+#include "recovery.h"
+#include "super.h"
+#include "unlinked.h"
+
+/**
+ * gfs_scand - Look for cached glocks and inodes to toss from memory
+ * @sdp: Pointer to GFS superblock
+ *
+ * One of these daemons runs, finding candidates to add to sd_reclaim_list.
+ * See gfs_glockd()
+ */
+
+int
+gfs_scand(void *data)
+{
+	struct gfs_sbd *sdp = (struct gfs_sbd *)data;
+
+	daemonize("gfs_scand");
+	sdp->sd_scand_process = current;
+	set_bit(SDF_SCAND_RUN, &sdp->sd_flags);
+	complete(&sdp->sd_thread_completion);
+
+	for (;;) {
+		gfs_scand_internal(sdp);
+
+		if (!test_bit(SDF_SCAND_RUN, &sdp->sd_flags))
+			break;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(gfs_tune_get(sdp, gt_scand_secs) * HZ);
+	}
+
+	down(&sdp->sd_thread_lock);
+	up(&sdp->sd_thread_lock);
+
+	complete(&sdp->sd_thread_completion);
+
+	return 0;
+}
+
+/**
+ * gfs_glockd - Reclaim unused glock structures
+ * @sdp: Pointer to GFS superblock
+ *
+ * One or more of these daemons run, reclaiming glocks on sd_reclaim_list.
+ * sd_glockd_num says how many daemons are running now.
+ * Number of daemons can be set by user, with num_glockd mount option.
+ * See gfs_scand()
+ */
+
+int
+gfs_glockd(void *data)
+{
+	struct gfs_sbd *sdp = (struct gfs_sbd *)data;
+
+	daemonize("gfs_glockd");
+	set_bit(SDF_GLOCKD_RUN, &sdp->sd_flags);
+	complete(&sdp->sd_thread_completion);
+
+	for (;;) {
+		while (atomic_read(&sdp->sd_reclaim_count))
+			gfs_reclaim_glock(sdp);
+
+		if (!test_bit(SDF_GLOCKD_RUN, &sdp->sd_flags))
+			break;
+
+		{
+			DECLARE_WAITQUEUE(__wait_chan, current);
+			set_current_state(TASK_INTERRUPTIBLE);
+			add_wait_queue(&sdp->sd_reclaim_wchan, &__wait_chan);
+			if (!atomic_read(&sdp->sd_reclaim_count)
+			    && test_bit(SDF_GLOCKD_RUN, &sdp->sd_flags))
+				schedule();
+			remove_wait_queue(&sdp->sd_reclaim_wchan, &__wait_chan);
+			set_current_state(TASK_RUNNING);
+		}
+	}
+
+	complete(&sdp->sd_thread_completion);
+
+	return 0;
+}
+
+/**
+ * gfs_recoverd - Recover dead machine's journals
+ * @sdp: Pointer to GFS superblock
+ *
+ */
+
+int
+gfs_recoverd(void *data)
+{
+	struct gfs_sbd *sdp = (struct gfs_sbd *)data;
+
+	daemonize("gfs_recoverd");
+	sdp->sd_recoverd_process = current;
+	set_bit(SDF_RECOVERD_RUN, &sdp->sd_flags);
+	complete(&sdp->sd_thread_completion);
+
+	for (;;) {
+		gfs_check_journals(sdp);
+
+		if (!test_bit(SDF_RECOVERD_RUN, &sdp->sd_flags))
+			break;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(gfs_tune_get(sdp, gt_recoverd_secs) * HZ);
+	}
+
+	down(&sdp->sd_thread_lock);
+	up(&sdp->sd_thread_lock);
+
+	complete(&sdp->sd_thread_completion);
+
+	return 0;
+}
+
+/**
+ * gfs_logd - Update log tail as Active Items get flushed to in-place blocks
+ * @sdp: Pointer to GFS superblock
+ *
+ * Also, periodically check to make sure that we're using the most recent
+ * journal index.
+ */
+
+int
+gfs_logd(void *data)
+{
+	struct gfs_sbd *sdp = (struct gfs_sbd *)data;
+	struct gfs_holder ji_gh;
+
+	daemonize("gfs_logd");
+	sdp->sd_logd_process = current;
+	set_bit(SDF_LOGD_RUN, &sdp->sd_flags);
+	complete(&sdp->sd_thread_completion);
+
+	for (;;) {
+		/* Advance the log tail */
+		gfs_ail_empty(sdp);
+
+		/* Check for latest journal index */
+		if (time_after_eq(jiffies,
+				  sdp->sd_jindex_refresh_time +
+				  gfs_tune_get(sdp, gt_jindex_refresh_secs) * HZ)) {
+			if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags) &&
+			    !gfs_jindex_hold(sdp, &ji_gh))
+				gfs_glock_dq_uninit(&ji_gh);
+			sdp->sd_jindex_refresh_time = jiffies;
+		}
+
+		if (!test_bit(SDF_LOGD_RUN, &sdp->sd_flags))
+			break;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(gfs_tune_get(sdp, gt_logd_secs) * HZ);
+	}
+
+	down(&sdp->sd_thread_lock);
+	up(&sdp->sd_thread_lock);
+
+	complete(&sdp->sd_thread_completion);
+
+	return 0;
+}
+
+/**
+ * gfs_quotad - Write cached quota changes into the quota file
+ * @sdp: Pointer to GFS superblock
+ *
+ */
+
+int
+gfs_quotad(void *data)
+{
+	struct gfs_sbd *sdp = (struct gfs_sbd *)data;
+	int error;
+
+	daemonize("gfs_quotad");
+	sdp->sd_quotad_process = current;
+	set_bit(SDF_QUOTAD_RUN, &sdp->sd_flags);
+	complete(&sdp->sd_thread_completion);
+
+	for (;;) {
+		/* Update quota file */
+		if (time_after_eq(jiffies,
+				  sdp->sd_quota_sync_time +
+				  gfs_tune_get(sdp, gt_quota_quantum) * HZ)) {
+			error = gfs_quota_sync(sdp);
+			if (error &&
+			    error != -EROFS &&
+			    !test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
+				printk("GFS: fsid=%s: quotad: error = %d\n",
+				       sdp->sd_fsname, error);
+			sdp->sd_quota_sync_time = jiffies;
+		}
+
+		/* Clean up */
+		gfs_quota_scan(sdp);
+
+		if (!test_bit(SDF_QUOTAD_RUN, &sdp->sd_flags))
+			break;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(gfs_tune_get(sdp, gt_quotad_secs) * HZ);
+	}
+
+	down(&sdp->sd_thread_lock);
+	up(&sdp->sd_thread_lock);
+
+	complete(&sdp->sd_thread_completion);
+
+	return 0;
+}
+
+/**
+ * gfs_inoded - Deallocate unlinked inodes
+ * @sdp: Pointer to GFS superblock
+ *
+ */
+
+int
+gfs_inoded(void *data)
+{
+	struct gfs_sbd *sdp = (struct gfs_sbd *)data;
+
+	daemonize("gfs_inoded");
+	sdp->sd_inoded_process = current;
+	set_bit(SDF_INODED_RUN, &sdp->sd_flags);
+	complete(&sdp->sd_thread_completion);
+
+	for (;;) {
+		gfs_unlinked_dealloc(sdp);
+
+		if (!test_bit(SDF_INODED_RUN, &sdp->sd_flags))
+			break;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(gfs_tune_get(sdp, gt_inoded_secs) * HZ);
+	}
+
+	down(&sdp->sd_thread_lock);
+	up(&sdp->sd_thread_lock);
+
+	complete(&sdp->sd_thread_completion);
+
+	return 0;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/daemon.h linux-2.6.9.debug/fs/gfs/daemon.h
--- linux-2.6.9.orig/fs/gfs/daemon.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/daemon.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,24 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __DAEMON_DOT_H__
+#define __DAEMON_DOT_H__
+
+int gfs_scand(void *data);
+int gfs_glockd(void *data);
+int gfs_recoverd(void *data);
+int gfs_logd(void *data);
+int gfs_quotad(void *data);
+int gfs_inoded(void *data);
+
+#endif /* __DAEMON_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/diaper.c linux-2.6.9.debug/fs/gfs/diaper.c
--- linux-2.6.9.orig/fs/gfs/diaper.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/diaper.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,630 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/idr.h>
+#include <linux/mempool.h>
+
+#include "gfs.h"
+#include "diaper.h"
+#include "ops_fstype.h"
+
+struct diaper_holder {
+	struct list_head dh_list;
+	unsigned int dh_count;
+
+	struct gendisk *dh_gendisk;
+
+	struct block_device *dh_real;
+	struct block_device *dh_diaper;
+
+	struct gfs_sbd *dh_sbd;
+	mempool_t *dh_mempool;
+	struct super_block *dh_dummy_sb;
+};
+
+struct bio_wrapper {
+	struct bio *bw_orig;
+	struct diaper_holder *bw_dh;
+};
+
+static int diaper_major = 0;
+static LIST_HEAD(diaper_list);
+static spinlock_t diaper_lock;
+static DEFINE_IDR(diaper_idr);
+kmem_cache_t *diaper_slab;
+
+/**
+ * diaper_open -
+ * @inode:
+ * @file:
+ *
+ * Don't allow these devices to be opened from userspace
+ * or from other kernel routines.  They should only be opened
+ * from this file.
+ *
+ * Returns: -ENOSYS
+ */
+
+static int
+diaper_open(struct inode *inode, struct file *file)
+{
+	return -ENOSYS;
+}
+
+static struct block_device_operations diaper_fops = {
+	.owner = THIS_MODULE,
+	.open = diaper_open,
+};
+
+/**
+ * diaper_end_io - Called at the end of a block I/O
+ * @bio:
+ * @bytes_done:
+ * @error:
+ *
+ * Returns: an integer thats usually discarded
+ */
+
+static int
+diaper_end_io(struct bio *bio, unsigned int bytes_done, int error)
+{
+	struct bio_wrapper *bw = (struct bio_wrapper *)bio->bi_private;
+	struct diaper_holder *dh = bw->bw_dh;
+	struct gfs_sbd *sdp = dh->dh_sbd;
+
+	bio_endio(bw->bw_orig, bytes_done, error);
+	if (bio->bi_size)
+		return 1;
+
+	atomic_dec(&sdp->sd_bio_outstanding);
+	bio_put(bio);
+	mempool_free(bw, dh->dh_mempool);
+
+	return 0;
+}
+
+/**
+ * diaper_make_request -
+ * @q:
+ * @bio:
+ *
+ * Returns: 0
+ */
+
+static int
+diaper_make_request(request_queue_t *q, struct bio *bio)
+{
+	struct diaper_holder *dh = (struct diaper_holder *)q->queuedata;
+	struct gfs_sbd *sdp = dh->dh_sbd;
+	struct bio_wrapper *bw;
+	struct bio *bi;
+
+	atomic_inc(&sdp->sd_bio_outstanding);
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) {
+		atomic_dec(&sdp->sd_bio_outstanding);
+		bio_endio(bio, bio->bi_size, 0);
+		return 0;
+	}
+	if (bio_rw(bio) == WRITE)
+		atomic_add(bio->bi_size >> 9, &sdp->sd_bio_writes);
+	else
+		atomic_add(bio->bi_size >> 9, &sdp->sd_bio_reads);
+
+	bw = mempool_alloc(dh->dh_mempool, GFP_NOIO);
+	bw->bw_orig = bio;
+	bw->bw_dh = dh;
+
+	bi = bio_clone(bio, GFP_NOIO);
+	bi->bi_bdev = dh->dh_real;
+	bi->bi_end_io = diaper_end_io;
+	bi->bi_private = bw;
+
+	generic_make_request(bi);
+
+	return 0;
+}
+
+/**
+ * minor_get -
+ *
+ * Returns: a unused minor number
+ */
+
+static int
+minor_get(void)
+{
+	int minor;
+	int error;
+      
+	for (;;) {
+		if (!idr_pre_get(&diaper_idr, GFP_KERNEL))
+			return -ENOMEM;
+      
+		spin_lock(&diaper_lock);
+		error = idr_get_new(&diaper_idr, NULL, &minor);
+		spin_unlock(&diaper_lock);
+
+		if (!error)
+			break;
+		if (error != -EAGAIN)
+			return error;
+	}
+
+	return minor;
+}
+
+/**
+ * minor_put - Free a used minor number
+ * @minor:
+ *
+ */
+
+static void
+minor_put(int minor)
+{
+	spin_lock(&diaper_lock);
+	idr_remove(&diaper_idr, minor);
+	spin_unlock(&diaper_lock);
+}
+
+/**
+ * gfs_dummy_write_super_lockfs - pass a freeze from the real device to the diaper
+ * @sb: the real device's dummy sb
+ *
+ */
+
+static void
+gfs_dummy_write_super_lockfs(struct super_block *sb)
+{
+	struct diaper_holder *dh = (struct diaper_holder *)sb->s_fs_info;
+	freeze_bdev(dh->dh_diaper);
+}
+
+/**
+ * gfs_dummy_unlockfs - pass a thaw from the real device to the diaper
+ * @sb: the real device's dummy sb
+ *
+ */
+
+static void
+gfs_dummy_unlockfs(struct super_block *sb)
+{
+	struct diaper_holder *dh = (struct diaper_holder *)sb->s_fs_info;
+	thaw_bdev(dh->dh_diaper, dh->dh_sbd->sd_vfs);
+}
+
+struct super_operations gfs_dummy_sops = {
+	.write_super_lockfs = gfs_dummy_write_super_lockfs,
+	.unlockfs = gfs_dummy_unlockfs,
+};
+
+/**
+ * gfs_dummy_sb - create a dummy superblock for the real device
+ * @dh:
+ *
+ * Returns: errno
+ */
+
+static int
+get_dummy_sb(struct diaper_holder *dh)
+{
+	struct block_device *real = dh->dh_real;
+	struct super_block *sb;
+	struct inode *inode;
+	int error;
+
+	down(&real->bd_mount_sem);
+	sb = sget(&gfs_fs_type, gfs_test_bdev_super, gfs_set_bdev_super, real);
+	up(&real->bd_mount_sem);
+	if (IS_ERR(sb))
+		return PTR_ERR(sb);
+
+	error = -ENOMEM;
+	inode = new_inode(sb);
+	if (!inode)
+		goto fail;
+
+	make_bad_inode(inode);
+
+	sb->s_root = d_alloc_root(inode);
+	if (!sb->s_root)
+		goto fail_iput;
+
+	sb->s_op = &gfs_dummy_sops;
+	sb->s_fs_info = dh;
+
+	up_write(&sb->s_umount);
+	module_put(gfs_fs_type.owner);
+
+	dh->dh_dummy_sb = sb;
+
+	return 0;
+
+ fail_iput:
+	iput(inode);
+
+ fail:
+	up_write(&sb->s_umount);
+	deactivate_super(sb);
+	return error;
+}
+
+static int
+diaper_congested(void *congested_data, int bdi_bits)
+{
+	struct diaper_holder *dh = (struct diaper_holder *)congested_data;
+	request_queue_t *q = bdev_get_queue(dh->dh_real);
+	return bdi_congested(&q->backing_dev_info, bdi_bits);
+}
+
+static void
+diaper_unplug(request_queue_t *q)
+{
+	struct diaper_holder *dh = (struct diaper_holder *)q->queuedata;
+	request_queue_t *rq = bdev_get_queue(dh->dh_real);
+
+	if (rq->unplug_fn)
+		rq->unplug_fn(rq);
+}
+
+static int
+diaper_flush(request_queue_t *q, struct gendisk *disk,
+	     sector_t *error_sector)
+{
+	struct diaper_holder *dh = (struct diaper_holder *)q->queuedata;
+	request_queue_t *rq = bdev_get_queue(dh->dh_real);
+	int error = -EOPNOTSUPP;
+
+	if (rq->issue_flush_fn)
+		error = rq->issue_flush_fn(rq, dh->dh_real->bd_disk, NULL);
+
+	return error;
+}
+
+/**
+ * diaper_get - Do the work of creating a diaper device
+ * @real:
+ * @flags:
+ *
+ * Returns: the diaper device or ERR_PTR()
+ */
+
+static struct diaper_holder *
+diaper_get(struct block_device *real, int flags)
+{
+	struct diaper_holder *dh;
+	struct gendisk *gd;
+	struct block_device *diaper;
+	unsigned int minor;
+	int error = -ENOMEM;
+
+	minor = minor_get();
+	if (minor < 0)
+		return ERR_PTR(error);	
+
+	dh = kmalloc(sizeof(struct diaper_holder), GFP_KERNEL);
+	if (!dh)
+		goto fail;
+	memset(dh, 0, sizeof(struct diaper_holder));
+
+	gd = alloc_disk(1);
+	if (!gd)
+		goto fail_kfree;
+
+	gd->queue = blk_alloc_queue(GFP_KERNEL);
+	if (!gd->queue)
+		goto fail_gd;
+
+	gd->queue->queuedata = dh;
+	gd->queue->backing_dev_info.congested_fn = diaper_congested;
+	gd->queue->backing_dev_info.congested_data = dh;
+	gd->queue->unplug_fn = diaper_unplug;
+	gd->queue->issue_flush_fn = diaper_flush;
+	blk_queue_make_request(gd->queue, diaper_make_request);
+	blk_queue_stack_limits(gd->queue, bdev_get_queue(real));
+	if (bdev_get_queue(real)->merge_bvec_fn &&
+	    gd->queue->max_sectors > (PAGE_SIZE >> 9))
+		blk_queue_max_sectors(gd->queue, PAGE_SIZE >> 9);
+	blk_queue_hardsect_size(gd->queue, bdev_hardsect_size(real));
+
+	gd->major = diaper_major;
+	gd->first_minor = minor;
+	{
+		char buf[BDEVNAME_SIZE];
+		bdevname(real, buf);
+		snprintf(gd->disk_name, sizeof(gd->disk_name), "diapered_%s", buf);
+	}
+	gd->fops = &diaper_fops;
+	gd->private_data = dh;
+	gd->capacity--;
+
+	add_disk(gd);
+
+	diaper = bdget_disk(gd, 0);
+	if (!diaper)
+		goto fail_remove;
+
+	down(&diaper->bd_sem);
+	if (!diaper->bd_openers) {
+		diaper->bd_disk = gd;
+		diaper->bd_contains = diaper;
+		bd_set_size(diaper, 0x7FFFFFFFFFFFFFFFULL);
+		diaper->bd_inode->i_data.backing_dev_info = &gd->queue->backing_dev_info;
+	} else
+		printk("GFS: diaper: reopening\n");
+	diaper->bd_openers++;
+	up(&diaper->bd_sem);
+
+	dh->dh_mempool = mempool_create(512,
+					mempool_alloc_slab, mempool_free_slab,
+					diaper_slab);
+	if (!dh->dh_mempool)
+		goto fail_bdput;
+
+	dh->dh_count = 1;
+	dh->dh_gendisk = gd;
+	dh->dh_real = real;
+	dh->dh_diaper = diaper;
+
+	error = get_dummy_sb(dh);
+	if (error)
+		goto fail_mempool;
+
+	return dh;
+
+ fail_mempool:
+	mempool_destroy(dh->dh_mempool);
+
+ fail_bdput:
+	down(&diaper->bd_sem);
+	if (!--diaper->bd_openers) {
+		invalidate_bdev(diaper, 1);
+		diaper->bd_contains = NULL;
+		diaper->bd_disk = NULL;
+	} else
+		printk("GFS: diaper: not closed\n");
+	up(&diaper->bd_sem);
+	bdput(diaper);	
+
+ fail_remove:
+	del_gendisk(gd);
+	blk_put_queue(gd->queue);
+
+ fail_gd:
+	put_disk(gd);
+
+ fail_kfree:
+	kfree(dh);
+
+ fail:
+	minor_put(minor);
+	return ERR_PTR(error);
+}
+
+/**
+ * diaper_put - Do the work of destroying a diaper device
+ * @dh:
+ *
+ */
+
+static void
+diaper_put(struct diaper_holder *dh)
+{
+	struct block_device *diaper = dh->dh_diaper;
+	struct gendisk *gd = dh->dh_gendisk;
+	int minor = dh->dh_gendisk->first_minor;
+
+	generic_shutdown_super(dh->dh_dummy_sb);
+
+	mempool_destroy(dh->dh_mempool);
+
+	down(&diaper->bd_sem);
+	if (!--diaper->bd_openers) {
+		invalidate_bdev(diaper, 1);
+		diaper->bd_contains = NULL;
+		diaper->bd_disk = NULL;
+	} else
+		printk("GFS: diaper: not closed\n");
+	up(&diaper->bd_sem);
+
+	bdput(diaper);
+	del_gendisk(gd);
+	blk_put_queue(gd->queue);
+	put_disk(gd);
+	kfree(dh);
+	minor_put(minor);
+}
+
+/**
+ * gfs_diaper_get - Get a hold of an existing diaper or create a new one
+ * @real:
+ * @flags:
+ *
+ * Returns: the diaper device or ERR_PTR()
+ */
+
+struct block_device *
+gfs_diaper_get(struct block_device *real, int flags)
+{
+	struct list_head *tmp;
+	struct diaper_holder *dh, *dh_new = NULL;
+
+	for (;;) {
+		spin_lock(&diaper_lock);
+		for (tmp = diaper_list.next;
+		     tmp != &diaper_list;
+		     tmp = tmp->next) {
+			dh = list_entry(tmp, struct diaper_holder, dh_list);
+			if (dh->dh_real == real) {
+				dh->dh_count++;
+				break;
+			}
+		}
+		if (tmp == &diaper_list)
+			dh = NULL;
+		if (!dh && dh_new) {
+			dh = dh_new;
+			list_add(&dh->dh_list, &diaper_list);
+			dh_new = NULL;
+		}
+		spin_unlock(&diaper_lock);
+
+		if (dh) {
+			if (dh_new)
+				diaper_put(dh_new);
+			return dh->dh_diaper;
+		}
+
+		dh_new = diaper_get(real, flags);
+		if (IS_ERR(dh_new))
+			return (struct block_device *)dh_new;
+	}
+}
+
+/**
+ * gfs_diaper_put - Drop a reference on a diaper
+ * @diaper:
+ *
+ */
+
+void
+gfs_diaper_put(struct block_device *diaper)
+{
+	struct list_head *tmp;
+	struct diaper_holder *dh;
+
+	spin_lock(&diaper_lock);
+	for (tmp = diaper_list.next;
+	     tmp != &diaper_list;
+	     tmp = tmp->next) {
+		dh = list_entry(tmp, struct diaper_holder, dh_list);
+		if (dh->dh_diaper == diaper) {
+			if (!--dh->dh_count) {
+				list_del(&dh->dh_list);
+				spin_unlock(&diaper_lock);
+				diaper_put(dh);
+			} else
+				spin_unlock(&diaper_lock);
+			return;
+		}
+	}
+	spin_unlock(&diaper_lock);
+
+	printk("GFS: diaper: unknown undiaper\n");
+}
+
+/**
+ * gfs_diaper_register_sbd -
+ * @diaper:
+ * @sdp:
+ *
+ */
+
+void
+gfs_diaper_register_sbd(struct block_device *diaper, struct gfs_sbd *sdp)
+{
+	struct list_head *tmp;
+	struct diaper_holder *dh;
+
+	spin_lock(&diaper_lock);
+	for (tmp = diaper_list.next;
+	     tmp != &diaper_list;
+	     tmp = tmp->next) {
+		dh = list_entry(tmp, struct diaper_holder, dh_list);
+		if (dh->dh_diaper == diaper) {
+			dh->dh_sbd = sdp;
+			spin_unlock(&diaper_lock);
+			return;
+		}
+	}
+	spin_unlock(&diaper_lock);
+
+	printk("GFS: diaper: unknown register\n");
+}
+
+/**
+ * gfs_diaper_2real -
+ * @diaper:
+ *
+ * Returns: the real device cooresponding to the diaper
+ */
+
+struct block_device *
+gfs_diaper_2real(struct block_device *diaper)
+{
+        struct list_head *tmp;
+        struct diaper_holder *dh;
+
+        spin_lock(&diaper_lock);
+        for (tmp = diaper_list.next;
+             tmp != &diaper_list;
+             tmp = tmp->next) {
+                dh = list_entry(tmp, struct diaper_holder, dh_list);
+                if (dh->dh_diaper == diaper) {
+                        spin_unlock(&diaper_lock);
+			return dh->dh_real;
+                }
+        }
+        spin_unlock(&diaper_lock);
+
+        printk("GFS: diaper: unknown 2real\n");
+	return NULL;
+}
+
+/**
+ * gfs_diaper_init -
+ *
+ * Returns: errno
+ */
+
+int
+gfs_diaper_init(void)
+{
+	spin_lock_init(&diaper_lock);
+
+	diaper_slab = kmem_cache_create("gfs_bio_wrapper", sizeof(struct bio_wrapper),
+					0, 0,
+					NULL, NULL);	
+	if (!diaper_slab)
+		return -ENOMEM;
+
+	diaper_major = register_blkdev(0, "gfs_diaper");
+	if (diaper_major < 0) {
+ 		kmem_cache_destroy(diaper_slab);
+		return diaper_major;
+	}
+	
+	return 0;
+}
+
+/**
+ * gfs_diaper_uninit -
+ *
+ */
+
+void
+gfs_diaper_uninit(void)
+{
+	kmem_cache_destroy(diaper_slab);
+	unregister_blkdev(diaper_major, "gfs_diaper");
+}
+
diff -pruN linux-2.6.9.orig/fs/gfs/diaper.h linux-2.6.9.debug/fs/gfs/diaper.h
--- linux-2.6.9.orig/fs/gfs/diaper.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/diaper.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,26 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __DIAPER_DOT_H__
+#define __DIAPER_DOT_H__
+
+struct block_device *gfs_diaper_get(struct block_device *real, int flags);
+void gfs_diaper_put(struct block_device *diaper);
+
+void gfs_diaper_register_sbd(struct block_device *diaper, struct gfs_sbd *sdp);
+struct block_device *gfs_diaper_2real(struct block_device *diaper);
+
+int gfs_diaper_init(void);
+void gfs_diaper_uninit(void);
+
+#endif /* __DIAPER_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/dio.c linux-2.6.9.debug/fs/gfs/dio.c
--- linux-2.6.9.orig/fs/gfs/dio.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/dio.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1353 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/writeback.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "log.h"
+#include "lops.h"
+#include "rgrp.h"
+#include "trans.h"
+
+#define buffer_busy(bh) ((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock)))
+
+/**
+ * aspace_get_block - 
+ * @inode:
+ * @lblock:
+ * @bh_result:
+ * @create:
+ *
+ * Returns: errno
+ */
+
+static int
+aspace_get_block(struct inode *inode, sector_t lblock,
+		 struct buffer_head *bh_result, int create)
+{
+	gfs_assert_warn(vfs2sdp(inode->i_sb), FALSE);
+	return -ENOSYS;
+}
+
+/**
+ * gfs_aspace_writepage - write an aspace page
+ * @page: the page
+ * @wbc:
+ *
+ * Returns: errno
+ */
+
+static int 
+gfs_aspace_writepage(struct page *page, struct writeback_control *wbc)
+{
+	return block_write_full_page(page, aspace_get_block, wbc);
+}
+
+/**
+ * stuck_releasepage - We're stuck in gfs_releasepage().  Print stuff out.
+ * @bh: the buffer we're stuck on
+ *
+ */
+
+static void
+stuck_releasepage(struct buffer_head *bh)
+{
+	struct gfs_sbd *sdp = vfs2sdp(bh->b_page->mapping->host->i_sb);
+	struct gfs_bufdata *bd = bh2bd(bh);
+
+	printk("GFS: fsid=%s: stuck in gfs_releasepage()...\n", sdp->sd_fsname);
+	printk("GFS: fsid=%s: blkno = %"PRIu64", bh->b_count = %d\n",
+	       sdp->sd_fsname,
+	       (uint64_t)bh->b_blocknr,
+	       atomic_read(&bh->b_count));
+	printk("GFS: fsid=%s: bh2bd(bh) = %s\n",
+	       sdp->sd_fsname,
+	       (bd) ? "!NULL" : "NULL");
+
+	if (bd) {
+		struct gfs_glock *gl = bd->bd_gl;
+
+		printk("GFS: fsid=%s: gl = (%u, %"PRIu64")\n",
+		       sdp->sd_fsname,
+		       gl->gl_name.ln_type,
+		       gl->gl_name.ln_number);
+
+		printk("GFS: fsid=%s: bd_new_le.le_trans = %s\n",
+		       sdp->sd_fsname,
+		       (bd->bd_new_le.le_trans) ? "!NULL" : "NULL");
+		printk("GFS: fsid=%s: bd_incore_le.le_trans = %s\n",
+		       sdp->sd_fsname,
+		       (bd->bd_incore_le.le_trans) ? "!NULL" : "NULL");
+		printk("GFS: fsid=%s: bd_frozen = %s\n",
+		       sdp->sd_fsname,
+		       (bd->bd_frozen) ? "!NULL" : "NULL");
+		printk("GFS: fsid=%s: bd_pinned = %u\n",
+		       sdp->sd_fsname, bd->bd_pinned);
+		printk("GFS: fsid=%s: bd_ail_tr_list = %s\n",
+		       sdp->sd_fsname,
+		       (list_empty(&bd->bd_ail_tr_list)) ? "Empty" : "!Empty");
+
+		if (gl->gl_ops == &gfs_inode_glops) {
+			struct gfs_inode *ip = gl2ip(gl);
+
+			if (ip) {
+				unsigned int x;
+
+				printk("GFS: fsid=%s: ip = %"PRIu64"/%"PRIu64"\n",
+				       sdp->sd_fsname,
+				       ip->i_num.no_formal_ino,
+				       ip->i_num.no_addr);
+				printk("GFS: fsid=%s: ip->i_count = %d, ip->i_vnode = %s\n",
+				     sdp->sd_fsname,
+				     atomic_read(&ip->i_count),
+				     (ip->i_vnode) ? "!NULL" : "NULL");
+				for (x = 0; x < GFS_MAX_META_HEIGHT; x++)
+					printk("GFS: fsid=%s: ip->i_cache[%u] = %s\n",
+					       sdp->sd_fsname, x,
+					       (ip->i_cache[x]) ? "!NULL" : "NULL");
+			}
+		}
+	}
+}
+
+/**
+ * gfs_aspace_releasepage - free the metadata associated with a page 
+ * @page: the page that's being released
+ * @gfp_mask: passed from Linux VFS, ignored by us
+ *
+ * Call try_to_free_buffers() if the buffers in this page can be
+ * released.
+ *
+ * Returns: 0
+ */
+
+static int
+gfs_aspace_releasepage(struct page *page, int gfp_mask)
+{
+	struct inode *aspace = page->mapping->host;
+	struct gfs_sbd *sdp = vfs2sdp(aspace->i_sb);
+	struct buffer_head *bh, *head;
+	struct gfs_bufdata *bd;
+	unsigned long t;
+
+	if (!page_has_buffers(page))
+		goto out;
+
+	head = bh = page_buffers(page);
+	do {
+		t = jiffies;
+
+		while (atomic_read(&bh->b_count)) {
+			if (atomic_read(&aspace->i_writecount)) {
+				if (time_after_eq(jiffies,
+						  t +
+						  gfs_tune_get(sdp, gt_stall_secs) * HZ)) {
+					stuck_releasepage(bh);
+					t = jiffies;
+				}
+
+				yield();
+				continue;
+			}
+
+			return 0;
+		}
+
+		bd = bh2bd(bh);
+		if (bd) {
+			gfs_assert_warn(sdp, bd->bd_bh == bh);
+			gfs_assert_warn(sdp, !bd->bd_new_le.le_trans);
+		        gfs_assert_warn(sdp, !bd->bd_incore_le.le_trans);
+			gfs_assert_warn(sdp, !bd->bd_frozen);
+			gfs_assert_warn(sdp, !bd->bd_pinned);
+			gfs_assert_warn(sdp, list_empty(&bd->bd_ail_tr_list));
+			kmem_cache_free(gfs_bufdata_cachep, bd);
+			atomic_dec(&sdp->sd_bufdata_count);
+			bh2bd(bh) = NULL;
+		}
+
+		bh = bh->b_this_page;
+	}
+	while (bh != head);
+
+ out:
+	return try_to_free_buffers(page);
+}
+
+static struct address_space_operations aspace_aops = {
+	.writepage = gfs_aspace_writepage,
+	.releasepage = gfs_aspace_releasepage,
+};
+
+/**
+ * gfs_aspace_get - Create and initialize a struct inode structure
+ * @sdp: the filesystem the aspace is in
+ *
+ * Right now a struct inode is just a struct inode.  Maybe Linux
+ * will supply a more lightweight address space construct (that works)
+ * in the future.
+ *
+ * Make sure pages/buffers in this aspace aren't in high memory.
+ *
+ * Returns: the aspace
+ */
+
+struct inode *
+gfs_aspace_get(struct gfs_sbd *sdp)
+{
+	struct inode *aspace;
+
+	aspace = new_inode(sdp->sd_vfs);
+	if (aspace) {
+		mapping_set_gfp_mask(aspace->i_mapping, GFP_KERNEL);
+		aspace->i_mapping->a_ops = &aspace_aops;
+		aspace->i_size = ~0ULL;
+		vn2ip(aspace) = NULL;
+		insert_inode_hash(aspace);
+	}
+
+	return aspace;
+}
+
+/**
+ * gfs_aspace_put - get rid of an aspace
+ * @aspace:
+ *
+ */
+
+void
+gfs_aspace_put(struct inode *aspace)
+{
+	remove_inode_hash(aspace);
+	iput(aspace);
+}
+
+/**
+ * gfs_ail_start_trans - Start I/O on a part of the AIL
+ * @sdp: the filesystem
+ * @tr: the part of the AIL
+ *
+ */
+
+void
+gfs_ail_start_trans(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct list_head *head, *tmp, *prev;
+	struct gfs_bufdata *bd;
+	struct buffer_head *bh;
+	int retry;
+
+	do {
+		retry = FALSE;
+
+		spin_lock(&sdp->sd_ail_lock);
+
+		for (head = &tr->tr_ail_bufs, tmp = head->prev, prev = tmp->prev;
+		     tmp != head;
+		     tmp = prev, prev = tmp->prev) {
+			bd = list_entry(tmp, struct gfs_bufdata, bd_ail_tr_list);
+			bh = bd->bd_bh;
+
+			if (gfs_trylock_buffer(bh))
+				continue;
+
+			if (bd->bd_pinned) {
+				gfs_unlock_buffer(bh);
+				continue;
+			}
+
+			if (!buffer_busy(bh)) {
+				if (!buffer_uptodate(bh))
+					gfs_io_error_bh(sdp, bh);
+
+				list_del_init(&bd->bd_ail_tr_list);
+				list_del(&bd->bd_ail_gl_list);
+
+				gfs_unlock_buffer(bh);
+				brelse(bh);
+				continue;
+			}
+
+			if (buffer_dirty(bh)) {
+				list_move(&bd->bd_ail_tr_list, head);
+
+				spin_unlock(&sdp->sd_ail_lock);
+				wait_on_buffer(bh);
+				ll_rw_block(WRITE, 1, &bh);
+				spin_lock(&sdp->sd_ail_lock);
+
+				gfs_unlock_buffer(bh);
+				retry = TRUE;
+				break;
+			}
+
+			gfs_unlock_buffer(bh);
+		}
+
+		spin_unlock(&sdp->sd_ail_lock);
+	} while (retry);
+}
+
+/**
+ * gfs_ail_empty_trans - Check whether or not a trans in the AIL has been synced
+ * @sdp: the filesystem
+ * @tr: the transaction
+ *
+ */
+
+int
+gfs_ail_empty_trans(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct list_head *head, *tmp, *prev;
+	struct gfs_bufdata *bd;
+	struct buffer_head *bh;
+	int ret;
+
+	spin_lock(&sdp->sd_ail_lock);
+
+	for (head = &tr->tr_ail_bufs, tmp = head->prev, prev = tmp->prev;
+	     tmp != head;
+	     tmp = prev, prev = tmp->prev) {
+		bd = list_entry(tmp, struct gfs_bufdata, bd_ail_tr_list);
+		bh = bd->bd_bh;
+
+		if (gfs_trylock_buffer(bh))
+			continue;
+
+		if (bd->bd_pinned || buffer_busy(bh)) {
+			gfs_unlock_buffer(bh);
+			continue;
+		}
+
+		if (!buffer_uptodate(bh))
+			gfs_io_error_bh(sdp, bh);
+
+		list_del_init(&bd->bd_ail_tr_list);
+		list_del(&bd->bd_ail_gl_list);
+
+		gfs_unlock_buffer(bh);
+		brelse(bh);
+	}
+
+	ret = list_empty(head);
+
+	spin_unlock(&sdp->sd_ail_lock);
+
+	return ret;
+}
+
+/**
+ * ail_empty_gl - remove all buffers for a given lock from the AIL
+ * @gl: the glock
+ *
+ * None of the buffers should be dirty, locked, or pinned.
+ */
+
+static void
+ail_empty_gl(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_bufdata *bd;
+	struct buffer_head *bh;
+
+	spin_lock(&sdp->sd_ail_lock);
+
+	while (!list_empty(&gl->gl_ail_bufs)) {
+		bd = list_entry(gl->gl_ail_bufs.next,
+				struct gfs_bufdata, bd_ail_gl_list);
+		bh = bd->bd_bh;
+
+		gfs_assert_withdraw(sdp, !bd->bd_pinned && !buffer_busy(bh));
+		if (!buffer_uptodate(bh))
+			gfs_io_error_bh(sdp, bh);
+
+		list_del_init(&bd->bd_ail_tr_list);
+		list_del(&bd->bd_ail_gl_list);
+
+		brelse(bh);
+	}
+
+	spin_unlock(&sdp->sd_ail_lock);
+}
+
+/**
+ * gfs_inval_buf - Invalidate all buffers associated with a glock
+ * @gl: the glock
+ *
+ */
+
+void
+gfs_inval_buf(struct gfs_glock *gl)
+{
+	struct inode *aspace = gl->gl_aspace;
+	struct address_space *mapping = gl->gl_aspace->i_mapping;
+
+	ail_empty_gl(gl);
+
+	atomic_inc(&aspace->i_writecount);
+	truncate_inode_pages(mapping, 0);
+	atomic_dec(&aspace->i_writecount);
+
+	gfs_assert_withdraw(gl->gl_sbd, !mapping->nrpages);
+}
+
+/**
+ * gfs_sync_buf - Sync all buffers associated with a glock
+ * @gl: The glock
+ * @flags: DIO_START | DIO_WAIT | DIO_CHECK
+ *
+ */
+
+void
+gfs_sync_buf(struct gfs_glock *gl, int flags)
+{
+	struct address_space *mapping = gl->gl_aspace->i_mapping;
+	int error = 0;
+
+	if (flags & DIO_START)
+		error = filemap_fdatawrite(mapping);
+	if (!error && (flags & DIO_WAIT))
+		error = filemap_fdatawait(mapping);
+	if (!error && (flags & (DIO_INVISIBLE | DIO_CHECK)) == DIO_CHECK)
+		ail_empty_gl(gl);
+
+	if (error)
+		gfs_io_error(gl->gl_sbd);
+}
+
+/**
+ * getbuf - Get a buffer with a given address space
+ * @sdp: the filesystem
+ * @aspace: the address space
+ * @blkno: the block number (filesystem scope)
+ * @create: TRUE if the buffer should be created
+ *
+ * Returns: the buffer
+ */
+
+static struct buffer_head *
+getbuf(struct gfs_sbd *sdp, struct inode *aspace, uint64_t blkno, int create)
+{
+	struct page *page;
+	struct buffer_head *bh;
+	unsigned int shift;
+	unsigned long index;
+	unsigned int bufnum;
+
+	shift = PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift;
+	index = blkno >> shift;             /* convert block to page */
+	bufnum = blkno - (index << shift);  /* block buf index within page */
+
+	if (create) {
+		RETRY_MALLOC(page = grab_cache_page(aspace->i_mapping, index), page);
+	} else {
+		page = find_lock_page(aspace->i_mapping, index);
+		if (!page)
+			return NULL;
+	}
+
+	if (!page_has_buffers(page))
+		create_empty_buffers(page, sdp->sd_sb.sb_bsize, 0);
+
+	/* Locate header for our buffer within our page */
+	for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
+		/* Do nothing */;
+	get_bh(bh);
+
+	if (!buffer_mapped(bh))
+		map_bh(bh, sdp->sd_vfs, blkno);
+	else if (gfs_assert_warn(sdp, bh->b_bdev == sdp->sd_vfs->s_bdev &&
+				 bh->b_blocknr == blkno))
+		map_bh(bh, sdp->sd_vfs, blkno);
+
+	unlock_page(page);
+	page_cache_release(page);
+
+	return bh;
+}
+
+/**
+ * gfs_dgetblk - Get a block
+ * @gl: The glock associated with this block
+ * @blkno: The block number
+ *
+ * Returns: The buffer
+ */
+
+struct buffer_head *
+gfs_dgetblk(struct gfs_glock *gl, uint64_t blkno)
+{
+	return getbuf(gl->gl_sbd, gl->gl_aspace, blkno, CREATE);
+}
+
+/**
+ * gfs_dread - Read a block from disk
+ * @gl: The glock covering the block
+ * @blkno: The block number
+ * @flags: flags to gfs_dreread()
+ * @bhp: the place where the buffer is returned (NULL on failure)
+ *
+ * Returns: errno
+ */
+
+int
+gfs_dread(struct gfs_glock *gl, uint64_t blkno,
+	  int flags, struct buffer_head **bhp)
+{
+	int error;
+
+	*bhp = gfs_dgetblk(gl, blkno);
+	error = gfs_dreread(gl->gl_sbd, *bhp, flags);
+	if (error)
+		brelse(*bhp);
+
+	return error;
+}
+
+/**
+ * gfs_prep_new_buffer - Mark a new buffer we just gfs_dgetblk()ed uptodate
+ * @bh: the buffer
+ *
+ */
+
+void
+gfs_prep_new_buffer(struct buffer_head *bh)
+{
+	wait_on_buffer(bh);
+	clear_buffer_dirty(bh);
+	set_buffer_uptodate(bh);
+}
+
+/**
+ * gfs_dreread - Reread a block from disk
+ * @sdp: the filesystem
+ * @bh: The block to read
+ * @flags: Flags that control the read
+ *
+ * Returns: errno
+ */
+
+int
+gfs_dreread(struct gfs_sbd *sdp, struct buffer_head *bh, int flags)
+{
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		return -EIO;
+
+	/* Fill in meta-header if we have a cached copy, else read from disk */
+	if (flags & DIO_NEW) {
+		if (gfs_mhc_fish(sdp, bh))
+			return 0;
+		clear_buffer_uptodate(bh);
+	}
+
+	if (flags & DIO_FORCE)
+		clear_buffer_uptodate(bh);
+
+	if ((flags & DIO_START) && !buffer_uptodate(bh))
+		ll_rw_block(READ, 1, &bh);
+
+	if (flags & DIO_WAIT) {
+		wait_on_buffer(bh);
+
+		if (!buffer_uptodate(bh)) {
+			gfs_io_error_bh(sdp, bh);
+			return -EIO;
+		}
+		if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+			return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_dwrite - Write a buffer to disk (and/or wait for write to complete)
+ * @sdp: the filesystem
+ * @bh: The buffer to write
+ * @flags:  DIO_XXX The type of write/wait operation to do
+ *
+ * Returns: errno
+ */
+
+int
+gfs_dwrite(struct gfs_sbd *sdp, struct buffer_head *bh, int flags)
+{
+	if (gfs_assert_warn(sdp, !test_bit(SDF_ROFS, &sdp->sd_flags)))
+		return -EIO;
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		return -EIO;
+
+	if (flags & DIO_CLEAN) {
+		lock_buffer(bh);
+		clear_buffer_dirty(bh);
+		unlock_buffer(bh);
+	}
+
+	if (flags & DIO_DIRTY) {
+		if (gfs_assert_warn(sdp, buffer_uptodate(bh)))
+			return -EIO;
+		mark_buffer_dirty(bh);
+	}
+
+	if ((flags & DIO_START) && buffer_dirty(bh)) {
+		wait_on_buffer(bh);
+		ll_rw_block(WRITE, 1, &bh);
+	}
+
+	if (flags & DIO_WAIT) {
+		wait_on_buffer(bh);
+
+		if (!buffer_uptodate(bh) || buffer_dirty(bh)) {
+			gfs_io_error_bh(sdp, bh);
+			return -EIO;
+		}
+		if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+			return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_attach_bufdata - attach a struct gfs_bufdata structure to a buffer
+ * @bh: The buffer to be attached to
+ * @gl: the glock the buffer belongs to
+ *
+ */
+
+void
+gfs_attach_bufdata(struct buffer_head *bh, struct gfs_glock *gl)
+{
+	struct gfs_bufdata *bd;
+
+	lock_page(bh->b_page);
+
+	/* If there's one attached already, we're done */
+	if (bh2bd(bh)) {
+		unlock_page(bh->b_page);
+		return;
+	}
+
+	RETRY_MALLOC(bd = kmem_cache_alloc(gfs_bufdata_cachep, GFP_KERNEL), bd);
+	atomic_inc(&gl->gl_sbd->sd_bufdata_count);
+
+	memset(bd, 0, sizeof(struct gfs_bufdata));
+
+	bd->bd_bh = bh;
+	bd->bd_gl = gl;
+
+	INIT_LE(&bd->bd_new_le, &gfs_buf_lops);
+	INIT_LE(&bd->bd_incore_le, &gfs_buf_lops);
+
+	init_MUTEX(&bd->bd_lock);
+
+	INIT_LIST_HEAD(&bd->bd_ail_tr_list);
+
+	bh2bd(bh) = bd;
+
+	unlock_page(bh->b_page);
+}
+
+/**
+ * gfs_is_pinned - Figure out if a buffer is pinned or not
+ * @sdp: the filesystem the buffer belongs to
+ * @bh: The buffer to be pinned
+ *
+ * Returns: TRUE if the buffer is pinned, FALSE otherwise
+ */
+
+int
+gfs_is_pinned(struct gfs_sbd *sdp, struct buffer_head *bh)
+{
+	struct gfs_bufdata *bd = bh2bd(bh);
+	int ret = FALSE;
+
+	if (bd) {
+		gfs_lock_buffer(bh);
+		if (bd->bd_pinned)
+			ret = TRUE;
+		gfs_unlock_buffer(bh);
+	}
+
+	return ret;
+}
+
+/**
+ * gfs_dpin - Pin a metadata buffer in memory
+ * @sdp: the filesystem the buffer belongs to
+ * @bh: The buffer to be pinned
+ *
+ * "Pinning" means keeping buffer from being written to its in-place location.
+ * A buffer should be pinned from the time it is added to a new transaction,
+ *   until after it has been written to the log.
+ * If an earlier change to this buffer is still pinned, waiting to be written
+ *   to on-disk log, we need to keep a "frozen" copy of the old data while this
+ *   transaction is modifying the real data.  We keep the frozen copy until
+ *   this transaction's incore_commit(), i.e. until the transaction has
+ *   finished modifying the real data, at which point we can use the real
+ *   buffer for logging, even if the frozen copy didn't get written to the log.
+ *
+ */
+
+void
+gfs_dpin(struct gfs_sbd *sdp, struct buffer_head *bh)
+{
+	struct gfs_bufdata *bd = bh2bd(bh);
+	char *data;
+
+	gfs_assert_withdraw(sdp, !test_bit(SDF_ROFS, &sdp->sd_flags));
+
+	gfs_lock_buffer(bh);
+
+	gfs_assert_warn(sdp, !bd->bd_frozen);
+
+	if (!bd->bd_pinned++) {
+		wait_on_buffer(bh);
+
+		/* If this buffer is in the AIL and it has already been written
+		   to in-place disk block, remove it from the AIL. */
+
+		spin_lock(&sdp->sd_ail_lock);
+		if (!list_empty(&bd->bd_ail_tr_list) && !buffer_busy(bh)) {
+			list_del_init(&bd->bd_ail_tr_list);
+			list_del(&bd->bd_ail_gl_list);
+			brelse(bh);
+		}
+		spin_unlock(&sdp->sd_ail_lock);
+
+		clear_buffer_dirty(bh);
+		wait_on_buffer(bh);
+
+		if (!buffer_uptodate(bh))
+			gfs_io_error_bh(sdp, bh);
+	} else {
+		gfs_unlock_buffer(bh);
+
+		gfs_assert_withdraw(sdp, buffer_uptodate(bh));
+
+		data = gmalloc(sdp->sd_sb.sb_bsize);
+
+		gfs_lock_buffer(bh);
+
+		/* Create frozen copy, if needed. */
+		if (bd->bd_pinned > 1) {
+			memcpy(data, bh->b_data, sdp->sd_sb.sb_bsize);
+			bd->bd_frozen = data;
+		} else
+			kfree(data);
+	}
+
+	gfs_unlock_buffer(bh);
+
+	get_bh(bh);
+}
+
+/**
+ * gfs_dunpin - Unpin a buffer
+ * @sdp: the filesystem the buffer belongs to
+ * @bh: The buffer to unpin
+ * @tr: The transaction in the AIL that contains this buffer
+ *      If NULL, don't attach buffer to any AIL list
+ *      (i.e. when dropping a pin reference when merging a new transaction
+ *       with an already existing incore transaction)
+ *
+ * Called for (meta) buffers, after they've been logged to on-disk journal.
+ * Make a (meta) buffer writeable to in-place location on-disk, if recursive
+ *   pin count is 1 (i.e. no other, later transaction is modifying this buffer).
+ * Add buffer to AIL lists of 1) the latest transaction that's modified and
+ *   logged (on-disk) the buffer, and of 2) the glock that protects the buffer.
+ * A single buffer might have been modified by more than one transaction
+ *   since the buffer's previous write to disk (in-place location).  We keep
+ *   the buffer on only one transaction's AIL list, i.e. that of the latest
+ *   transaction that's completed logging this buffer (no need to write it to
+ *   in-place block multiple times for multiple transactions, only once with
+ *   the most up-to-date data).
+ * A single buffer will be protected by one and only one glock.  If buffer is 
+ *   already on a (previous) transaction's AIL, we know that we're already
+ *   on buffer's glock's AIL.
+ * 
+ */
+
+void
+gfs_dunpin(struct gfs_sbd *sdp, struct buffer_head *bh, struct gfs_trans *tr)
+{
+	struct gfs_bufdata *bd = bh2bd(bh);
+
+	gfs_assert_withdraw(sdp, buffer_uptodate(bh));
+
+	gfs_lock_buffer(bh);
+
+	if (gfs_assert_warn(sdp, bd->bd_pinned)) {
+		gfs_unlock_buffer(bh);
+		return;
+	}
+
+	/* No other (later) transaction is modifying buffer; ready to write */
+	if (bd->bd_pinned == 1)
+		mark_buffer_dirty(bh);
+
+	bd->bd_pinned--;
+
+	gfs_unlock_buffer(bh);
+
+	if (tr) {
+		spin_lock(&sdp->sd_ail_lock);
+
+		if (list_empty(&bd->bd_ail_tr_list)) {
+			/* Buffer not attached to any earlier transaction.  Add
+			   it to glock's AIL, and this trans' AIL (below). */
+			list_add(&bd->bd_ail_gl_list, &bd->bd_gl->gl_ail_bufs);
+		} else {
+			/* Was part of earlier transaction.
+			   Move from that trans' AIL to this newer one's AIL.
+			   Buf is already on glock's AIL. */
+			list_del_init(&bd->bd_ail_tr_list);
+			brelse(bh);
+		}
+		list_add(&bd->bd_ail_tr_list, &tr->tr_ail_bufs);
+
+		spin_unlock(&sdp->sd_ail_lock);
+	} else
+		brelse(bh);
+}
+
+/**
+ * logbh_end_io - Called by OS at the end of a logbh ("fake" bh) write to log
+ * @bh: the buffer
+ * @uptodate: whether or not the write succeeded
+ *
+ */
+
+static void
+logbh_end_io(struct buffer_head *bh, int uptodate)
+{
+	if (uptodate)
+		set_buffer_uptodate(bh);
+	else
+		clear_buffer_uptodate(bh);
+	unlock_buffer(bh);
+}
+
+/**
+ * gfs_logbh_init - Initialize a fake buffer head
+ * @sdp: the filesystem
+ * @bh: the buffer to initialize
+ * @blkno: the block address of the buffer
+ * @data: the data to be written
+ *
+ */
+
+void
+gfs_logbh_init(struct gfs_sbd *sdp, struct buffer_head *bh,
+	       uint64_t blkno, char *data)
+{
+	memset(bh, 0, sizeof(struct buffer_head));
+	bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate) | (1 << BH_Lock);
+	atomic_set(&bh->b_count, 1);
+	set_bh_page(bh, virt_to_page(data), ((unsigned long)data) & (PAGE_SIZE - 1));
+	bh->b_blocknr = blkno;
+	bh->b_size = sdp->sd_sb.sb_bsize;
+	bh->b_bdev = sdp->sd_vfs->s_bdev;
+	init_buffer(bh, logbh_end_io, NULL);
+	INIT_LIST_HEAD(&bh->b_assoc_buffers);
+}
+
+/**
+ * gfs_logbh_uninit - Clean up a fake buffer head
+ * @sdp: the filesystem
+ * @bh: the buffer to clean
+ *
+ */
+
+void
+gfs_logbh_uninit(struct gfs_sbd *sdp, struct buffer_head *bh)
+{
+	gfs_assert_warn(sdp, test_bit(SDF_SHUTDOWN, &sdp->sd_flags) ||
+			!buffer_busy(bh));
+	gfs_assert_warn(sdp, atomic_read(&bh->b_count) == 1);
+}
+
+/**
+ * gfs_logbh_start - Start writing a fake buffer head
+ * @sdp: the filesystem
+ * @bh: the buffer to write
+ *
+ * This starts a block write to our journal.
+ */
+
+void
+gfs_logbh_start(struct gfs_sbd *sdp, struct buffer_head *bh)
+{
+	submit_bh(WRITE, bh);
+}
+
+/**
+ * gfs_logbh_wait - Wait for the write of a fake buffer head to complete
+ * @sdp: the filesystem
+ * @bh: the buffer to write
+ *
+ * This waits for a block write to our journal to complete.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_logbh_wait(struct gfs_sbd *sdp, struct buffer_head *bh)
+{
+	wait_on_buffer(bh);
+
+	if (!buffer_uptodate(bh) || buffer_dirty(bh)) {
+		gfs_io_error_bh(sdp, bh);
+		return -EIO;
+	}
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * gfs_replay_buf - write a log buffer to its inplace location
+ * @gl: the journal's glock 
+ * @bh: the buffer
+ *
+ * Returns: errno
+ */
+
+int
+gfs_replay_buf(struct gfs_glock *gl, struct buffer_head *bh)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_bufdata *bd;
+
+	bd = bh2bd(bh);
+	if (!bd) {
+		gfs_attach_bufdata(bh, gl);
+		bd = bh2bd(bh);
+	}
+
+	mark_buffer_dirty(bh);
+
+	if (list_empty(&bd->bd_ail_tr_list)) {
+		get_bh(bh);
+		list_add(&bd->bd_ail_tr_list, &sdp->sd_recovery_bufs);
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_replay_check - Check up on journal replay
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_replay_check(struct gfs_sbd *sdp)
+{
+	struct buffer_head *bh;
+	struct gfs_bufdata *bd;
+
+	while (!list_empty(&sdp->sd_recovery_bufs)) {
+		bd = list_entry(sdp->sd_recovery_bufs.prev,
+				struct gfs_bufdata, bd_ail_tr_list);
+		bh = bd->bd_bh;
+
+		if (buffer_busy(bh)) {
+			list_move(&bd->bd_ail_tr_list,
+				  &sdp->sd_recovery_bufs);
+			break;
+		} else {
+			list_del_init(&bd->bd_ail_tr_list);
+			if (!buffer_uptodate(bh))
+				gfs_io_error_bh(sdp, bh);
+			brelse(bh);
+		}
+	}
+}
+
+/**
+ * gfs_replay_wait - Wait for all replayed buffers to hit the disk
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_replay_wait(struct gfs_sbd *sdp)
+{
+	struct list_head *head, *tmp, *prev;
+	struct buffer_head *bh;
+	struct gfs_bufdata *bd;
+
+	for (head = &sdp->sd_recovery_bufs, tmp = head->prev, prev = tmp->prev;
+	     tmp != head;
+	     tmp = prev, prev = tmp->prev) {
+		bd = list_entry(tmp, struct gfs_bufdata, bd_ail_tr_list);
+		bh = bd->bd_bh;
+
+		if (!buffer_busy(bh)) {
+			list_del_init(&bd->bd_ail_tr_list);
+			if (!buffer_uptodate(bh))
+				gfs_io_error_bh(sdp, bh);
+			brelse(bh);
+			continue;
+		}
+
+		if (buffer_dirty(bh)) {
+			wait_on_buffer(bh);
+			ll_rw_block(WRITE, 1, &bh);
+		}
+	}
+
+	while (!list_empty(head)) {
+		bd = list_entry(head->prev, struct gfs_bufdata, bd_ail_tr_list);
+		bh = bd->bd_bh;
+
+		wait_on_buffer(bh);
+
+		gfs_assert_withdraw(sdp, !buffer_busy(bh));
+
+		list_del_init(&bd->bd_ail_tr_list);
+		if (!buffer_uptodate(bh))
+			gfs_io_error_bh(sdp, bh);
+		brelse(bh);
+	}
+}
+
+/**
+ * gfs_wipe_buffers - make inode's buffers so they aren't dirty/AILed anymore
+ * @ip: the inode who owns the buffers
+ * @rgd: the resource group
+ * @bstart: the first buffer in the run
+ * @blen: the number of buffers in the run
+ *
+ * Called when de-allocating a contiguous run of meta blocks within an rgrp.
+ * Make sure all buffers for de-alloc'd blocks are removed from the AIL, if
+ * they can be.  Dirty or pinned blocks are left alone.  Add relevant
+ * meta-headers to meta-header cache, so we don't need to read disk
+ * if we re-allocate blocks.
+ */
+
+void
+gfs_wipe_buffers(struct gfs_inode *ip, struct gfs_rgrpd *rgd,
+		 uint64_t bstart, uint32_t blen)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct inode *aspace = ip->i_gl->gl_aspace;
+	struct buffer_head *bh;
+	struct gfs_bufdata *bd;
+	int busy;
+	int add = FALSE;
+
+	while (blen) {
+		bh = getbuf(sdp, aspace, bstart, NO_CREATE);
+		if (bh) {
+
+			bd = bh2bd(bh);
+
+			if (buffer_uptodate(bh)) {
+				if (bd) {
+					gfs_lock_buffer(bh);
+					gfs_mhc_add(rgd, &bh, 1);
+					busy = bd->bd_pinned || buffer_busy(bh);
+					gfs_unlock_buffer(bh);
+
+					if (busy)
+						add = TRUE;
+					else {
+						spin_lock(&sdp->sd_ail_lock);
+						if (!list_empty(&bd->bd_ail_tr_list)) {
+							list_del_init(&bd->bd_ail_tr_list);
+							list_del(&bd->bd_ail_gl_list);
+							brelse(bh);
+						}
+						spin_unlock(&sdp->sd_ail_lock);
+					}
+				} else {
+					gfs_assert_withdraw(sdp, !buffer_dirty(bh));
+					wait_on_buffer(bh);
+					gfs_assert_withdraw(sdp, !buffer_busy(bh));
+					gfs_mhc_add(rgd, &bh, 1);
+				}
+			} else {
+				gfs_assert_withdraw(sdp, !bd || !bd->bd_pinned);
+				gfs_assert_withdraw(sdp, !buffer_dirty(bh));
+				wait_on_buffer(bh);
+				gfs_assert_withdraw(sdp, !buffer_busy(bh));
+			}
+
+			brelse(bh);
+		}
+
+		bstart++;
+		blen--;
+	}
+
+	if (add)
+		gfs_depend_add(rgd, ip->i_num.no_formal_ino);
+}
+
+/**
+ * gfs_sync_meta - sync all the buffers in a filesystem
+ * @sdp: the filesystem
+ *
+ * Flush metadata blocks to on-disk journal, then
+ * Flush metadata blocks (now in AIL) to on-disk in-place locations
+ * Periodically keep checking until done (AIL empty)
+ */
+
+void
+gfs_sync_meta(struct gfs_sbd *sdp)
+{
+	gfs_log_flush(sdp);
+	for (;;) {
+		gfs_ail_start(sdp, DIO_ALL);
+		if (gfs_ail_empty(sdp))
+			break;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ / 10);
+	}
+}
+
+/**
+ * gfs_flush_meta_cache - get rid of any references on buffers for this inode
+ * @ip: The GFS inode
+ *
+ * This releases buffers that are in the most-recently-used array of
+ *   blocks used for indirect block addressing for this inode.
+ * Don't confuse this with the meta-HEADER cache (mhc)!
+ */
+
+void
+gfs_flush_meta_cache(struct gfs_inode *ip)
+{
+	struct buffer_head **bh_slot;
+	unsigned int x;
+
+	spin_lock(&ip->i_spin);
+
+	for (x = 0; x < GFS_MAX_META_HEIGHT; x++) {
+		bh_slot = &ip->i_cache[x];
+		if (*bh_slot) {
+			brelse(*bh_slot);
+			*bh_slot = NULL;
+		}
+	}
+
+	spin_unlock(&ip->i_spin);
+}
+
+/**
+ * gfs_get_meta_buffer - Get a metadata buffer
+ * @ip: The GFS inode
+ * @height: The level of this buf in the metadata (indir addr) tree (if any)
+ * @num: The block number (device relative) of the buffer
+ * @new: Non-zero if we may create a new buffer
+ * @bhp: the buffer is returned here
+ *
+ * Returns: errno
+ */
+
+int
+gfs_get_meta_buffer(struct gfs_inode *ip, int height, uint64_t num, int new,
+		    struct buffer_head **bhp)
+{
+	struct buffer_head *bh, **bh_slot = &ip->i_cache[height];
+	int flags = ((new) ? DIO_NEW : 0) | DIO_START | DIO_WAIT;
+	int error;
+
+	/* Try to use the gfs_inode's MRU metadata tree cache */
+	spin_lock(&ip->i_spin);
+	bh = *bh_slot;
+	if (bh) {
+		if (bh->b_blocknr == num)
+			get_bh(bh);
+		else
+			bh = NULL;
+	}
+	spin_unlock(&ip->i_spin);
+
+	if (bh) {
+		error = gfs_dreread(ip->i_sbd, bh, flags);
+		if (error) {
+			brelse(bh);
+			return error;
+		}
+	} else {
+		error = gfs_dread(ip->i_gl, num, flags, &bh);
+		if (error)
+			return error;
+
+		spin_lock(&ip->i_spin);
+		if (*bh_slot != bh) {
+			if (*bh_slot)
+				brelse(*bh_slot);
+			*bh_slot = bh;
+			get_bh(bh);
+		}
+		spin_unlock(&ip->i_spin);
+	}
+
+	if (new) {
+		if (gfs_assert_warn(ip->i_sbd, height)) {
+			brelse(bh);
+			return -EIO;
+		}
+		gfs_trans_add_bh(ip->i_gl, bh);
+		gfs_metatype_set(bh, GFS_METATYPE_IN, GFS_FORMAT_IN);
+		gfs_buffer_clear_tail(bh, sizeof(struct gfs_meta_header));
+	} else if (gfs_metatype_check(ip->i_sbd, bh,
+				      (height) ? GFS_METATYPE_IN : GFS_METATYPE_DI)) {
+		brelse(bh);
+		return -EIO;
+	}
+
+	*bhp = bh;
+
+	return 0;
+}
+
+/**
+ * gfs_get_data_buffer - Get a data buffer
+ * @ip: The GFS inode
+ * @num: The block number (device relative) of the data block
+ * @new: Non-zero if this is a new allocation
+ * @bhp: the buffer is returned here
+ *
+ * Returns: errno
+ */
+
+int
+gfs_get_data_buffer(struct gfs_inode *ip, uint64_t block, int new,
+		    struct buffer_head **bhp)
+{
+	struct buffer_head *bh;
+	int error = 0;
+
+	if (block == ip->i_num.no_addr) {
+		if (gfs_assert_warn(ip->i_sbd, !new))
+			return -EIO;
+		error = gfs_dread(ip->i_gl, block, DIO_START | DIO_WAIT, &bh);
+		if (error)
+			return error;
+		if (gfs_metatype_check(ip->i_sbd, bh, GFS_METATYPE_DI)) {
+			brelse(bh);
+			return -EIO;
+		}
+	} else if (gfs_is_jdata(ip)) {
+		if (new) {
+			error = gfs_dread(ip->i_gl, block,
+					  DIO_NEW | DIO_START | DIO_WAIT, &bh);
+			if (error)
+				return error;
+			gfs_trans_add_bh(ip->i_gl, bh);
+			gfs_metatype_set(bh, GFS_METATYPE_JD, GFS_FORMAT_JD);
+			gfs_buffer_clear_tail(bh, sizeof(struct gfs_meta_header));
+		} else {
+			error = gfs_dread(ip->i_gl, block,
+					  DIO_START | DIO_WAIT, &bh);
+			if (error)
+				return error;
+			if (gfs_metatype_check(ip->i_sbd, bh, GFS_METATYPE_JD)) {
+				brelse(bh);
+				return -EIO;
+			}
+		}
+	} else {
+		if (new) {
+			bh = gfs_dgetblk(ip->i_gl, block);
+			gfs_prep_new_buffer(bh);
+		} else {
+			error = gfs_dread(ip->i_gl, block,
+					  DIO_START | DIO_WAIT, &bh);
+			if (error)
+				return error;
+		}
+	}
+
+	*bhp = bh;
+
+	return 0;
+}
+
+/**
+ * gfs_start_ra - start readahead on an extent of a file
+ * @gl: the glock the blocks belong to
+ * @dblock: the starting disk block
+ * @extlen: the number of blocks in the extent
+ *
+ */
+
+void
+gfs_start_ra(struct gfs_glock *gl, uint64_t dblock, uint32_t extlen)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct inode *aspace = gl->gl_aspace;
+	struct buffer_head *first_bh, *bh;
+	uint32_t max_ra = gfs_tune_get(sdp, gt_max_readahead) >> sdp->sd_sb.sb_bsize_shift;
+	int error;
+
+	if (!extlen)
+		return;
+	if (!max_ra)
+		return;
+	if (extlen > max_ra)
+		extlen = max_ra;
+
+	first_bh = getbuf(sdp, aspace, dblock, CREATE);
+
+	if (buffer_uptodate(first_bh))
+		goto out;
+	if (!buffer_locked(first_bh)) {
+		error = gfs_dreread(sdp, first_bh, DIO_START);
+		if (error)
+			goto out;
+	}
+
+	dblock++;
+	extlen--;
+
+	while (extlen) {
+		bh = getbuf(sdp, aspace, dblock, CREATE);
+
+		if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
+			error = gfs_dreread(sdp, bh, DIO_START);
+			brelse(bh);
+			if (error)
+				goto out;
+		} else
+			brelse(bh);
+
+		dblock++;
+		extlen--;
+
+		if (buffer_uptodate(first_bh))
+			break;
+	}
+
+ out:
+	brelse(first_bh);
+}
diff -pruN linux-2.6.9.orig/fs/gfs/dio.h linux-2.6.9.debug/fs/gfs/dio.h
--- linux-2.6.9.orig/fs/gfs/dio.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/dio.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,170 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __DIO_DOT_H__
+#define __DIO_DOT_H__
+
+void gfs_ail_start_trans(struct gfs_sbd *sdp, struct gfs_trans *tr);
+int gfs_ail_empty_trans(struct gfs_sbd *sdp, struct gfs_trans *tr);
+
+/*  Asynchronous I/O Routines  */
+
+struct buffer_head *gfs_dgetblk(struct gfs_glock *gl, uint64_t blkno);
+int gfs_dread(struct gfs_glock *gl, uint64_t blkno,
+	      int flags, struct buffer_head **bhp);
+
+void gfs_prep_new_buffer(struct buffer_head *bh);
+int gfs_dreread(struct gfs_sbd *sdp, struct buffer_head *bh, int flags);
+int gfs_dwrite(struct gfs_sbd *sdp, struct buffer_head *bh, int flags);
+
+void gfs_attach_bufdata(struct buffer_head *bh, struct gfs_glock *gl);
+int gfs_is_pinned(struct gfs_sbd *sdp, struct buffer_head *bh);
+void gfs_dpin(struct gfs_sbd *sdp, struct buffer_head *bh);
+void gfs_dunpin(struct gfs_sbd *sdp, struct buffer_head *bh,
+		struct gfs_trans *tr);
+
+static __inline__
+void gfs_lock_buffer(struct buffer_head *bh)
+{
+	struct gfs_bufdata *bd = bh2bd(bh);
+	down(&bd->bd_lock);
+}
+static __inline__
+int gfs_trylock_buffer(struct buffer_head *bh)
+{
+	struct gfs_bufdata *bd = bh2bd(bh);
+	return down_trylock(&bd->bd_lock);
+}
+static __inline__
+void gfs_unlock_buffer(struct buffer_head *bh)
+{
+	struct gfs_bufdata *bd = bh2bd(bh);
+	up(&bd->bd_lock);
+}
+
+void gfs_logbh_init(struct gfs_sbd *sdp, struct buffer_head *bh, uint64_t blkno,
+		    char *data);
+void gfs_logbh_uninit(struct gfs_sbd *sdp, struct buffer_head *bh);
+void gfs_logbh_start(struct gfs_sbd *sdp, struct buffer_head *bh);
+int gfs_logbh_wait(struct gfs_sbd *sdp, struct buffer_head *bh);
+
+int gfs_replay_buf(struct gfs_glock *gl, struct buffer_head *bh);
+void gfs_replay_check(struct gfs_sbd *sdp);
+void gfs_replay_wait(struct gfs_sbd *sdp);
+
+void gfs_wipe_buffers(struct gfs_inode *ip, struct gfs_rgrpd *rgd,
+		      uint64_t bstart, uint32_t blen);
+
+void gfs_sync_meta(struct gfs_sbd *sdp);
+
+/*  Buffer Caching routines  */
+
+int gfs_get_meta_buffer(struct gfs_inode *ip, int height, uint64_t num, int new,
+			struct buffer_head **bhp);
+int gfs_get_data_buffer(struct gfs_inode *ip, uint64_t block, int new,
+			struct buffer_head **bhp);
+void gfs_start_ra(struct gfs_glock *gl, uint64_t dblock, uint32_t extlen);
+
+static __inline__ int
+gfs_get_inode_buffer(struct gfs_inode *ip, struct buffer_head **bhp)
+{
+	return gfs_get_meta_buffer(ip, 0, ip->i_num.no_addr, FALSE, bhp);
+}
+
+struct inode *gfs_aspace_get(struct gfs_sbd *sdp);
+void gfs_aspace_put(struct inode *aspace);
+
+void gfs_inval_buf(struct gfs_glock *gl);
+void gfs_sync_buf(struct gfs_glock *gl, int flags);
+
+void gfs_flush_meta_cache(struct gfs_inode *ip);
+
+/*  Buffer Content Functions  */
+
+/**
+ * gfs_buffer_clear - Zeros out a buffer
+ * @ip: The GFS inode
+ * @bh: The buffer to zero
+ *
+ */
+
+static __inline__ void
+gfs_buffer_clear(struct buffer_head *bh)
+{
+	memset(bh->b_data, 0, bh->b_size);
+}
+
+/**
+ * gfs_buffer_clear_tail - Clear buffer beyond the dinode
+ * @bh: The buffer containing the on-disk inode
+ * @head: the size of the head of the buffer
+ *
+ * Clears the remaining part of an on-disk inode that is not a dinode.
+ * i.e. The data part of a stuffed inode, or the top level of metadata
+ * of a non-stuffed inode.
+ */
+
+static __inline__ void
+gfs_buffer_clear_tail(struct buffer_head *bh, int head)
+{
+	memset(bh->b_data + head, 0, bh->b_size - head);
+}
+
+/**
+ * gfs_buffer_clear_ends - Zero out any bits of a buffer which are not being written
+ * @bh: The buffer
+ * @offset: Offset in buffer where write starts
+ * @amount: Amount of data being written
+ * @journaled: TRUE if this is a journaled buffer
+ *
+ */
+
+static __inline__ void
+gfs_buffer_clear_ends(struct buffer_head *bh, int offset, int amount,
+		      int journaled)
+{
+	int z_off1 = (journaled) ? sizeof(struct gfs_meta_header) : 0;
+	int z_len1 = offset - z_off1;
+	int z_off2 = offset + amount;
+	int z_len2 = (bh)->b_size - z_off2;
+
+	if (z_len1)
+		memset(bh->b_data + z_off1, 0, z_len1);
+
+	if (z_len2)
+		memset(bh->b_data + z_off2, 0, z_len2);
+}
+
+/**
+ * gfs_buffer_copy_tail - copies the tail of one buffer to another
+ * @to_bh: the buffer to copy to
+ * @to_head: the size of the head of to_bh
+ * @from_bh: the buffer to copy from
+ * @from_head: the size of the head of from_bh
+ *
+ * from_head is guaranteed to bigger than to_head 
+ */
+
+static __inline__ void
+gfs_buffer_copy_tail(struct buffer_head *to_bh, int to_head,
+		     struct buffer_head *from_bh, int from_head)
+{
+	memcpy(to_bh->b_data + to_head,
+	       from_bh->b_data + from_head,
+	       from_bh->b_size - from_head);
+	memset(to_bh->b_data + to_bh->b_size + to_head - from_head,
+	       0,
+	       from_head - to_head);
+}
+
+#endif /* __DIO_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/dir.c linux-2.6.9.debug/fs/gfs/dir.c
--- linux-2.6.9.orig/fs/gfs/dir.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/dir.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,2411 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+* Implements Extendible Hashing as described in:
+*   "Extendible Hashing" by Fagin, et al in
+*     __ACM Trans. on Database Systems__, Sept 1979.
+*
+*
+* Here's the layout of dirents which is essentially the same as that of ext2 
+* within a single block. The field de_name_len is the number of bytes
+* actually required for the name (no null terminator). The field de_rec_len
+* is the number of bytes allocated to the dirent. The offset of the next
+* dirent in the block is (dirent + dirent->de_rec_len). When a dirent is
+* deleted, the preceding dirent inherits its allocated space, ie
+* prev->de_rec_len += deleted->de_rec_len. Since the next dirent is obtained
+* by adding de_rec_len to the current dirent, this essentially causes the
+* deleted dirent to get jumped over when iterating through all the dirents.
+*
+* When deleting the first dirent in a block, there is no previous dirent so
+* the field de_ino is set to zero to designate it as deleted. When allocating
+* a dirent, gfs_dirent_alloc iterates through the dirents in a block. If the
+* first dirent has (de_ino == 0) and de_rec_len is large enough, this first
+* dirent is allocated. Otherwise it must go through all the 'used' dirents
+* searching for one in which the amount of total space minus the amount of
+* used space will provide enough space for the new dirent.
+*
+* There are two types of blocks in which dirents reside. In a stuffed dinode,
+* the dirents begin at offset sizeof(struct gfs_dinode) from the beginning of
+* the block.  In leaves, they begin at offset sizeof (struct gfs_leaf) from the
+* beginning of the leaf block. The dirents reside in leaves when 
+* 
+* dip->i_di.di_flags & GFS_DIF_EXHASH is true
+* 
+* Otherwise, the dirents are "linear", within a single stuffed dinode block.
+*
+* When the dirents are in leaves, the actual contents of the directory file are
+* used as an array of 64-bit block pointers pointing to the leaf blocks. The
+* dirents are NOT in the directory file itself. There can be more than one block
+* pointer in the array that points to the same leaf. In fact, when a directory
+* is first converted from linear to exhash, all of the pointers point to the
+* same leaf. 
+*
+* When a leaf is completely full, the size of the hash table can be
+* doubled unless it is already at the maximum size which is hard coded into 
+* GFS_DIR_MAX_DEPTH. After that, leaves are chained together in a linked list,
+* but never before the maximum hash table size has been reached.
+*/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/vmalloc.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "dir.h"
+#include "file.h"
+#include "glock.h"
+#include "inode.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "trans.h"
+
+#define IS_LEAF     (1) /* Hashed (leaf) directory */
+#define IS_DINODE   (2) /* Linear (stuffed dinode block) directory */
+
+#if 1
+#define gfs_dir_hash2offset(h) (((uint64_t)(h)) >> 1)
+#define gfs_dir_offset2hash(p) ((uint32_t)(((uint64_t)(p)) << 1))
+#else
+#define gfs_dir_hash2offset(h) (((uint64_t)(h)))
+#define gfs_dir_offset2hash(p) ((uint32_t)(((uint64_t)(p))))
+#endif
+
+typedef int (*leaf_call_t) (struct gfs_inode *dip,
+			    uint32_t index, uint32_t len, uint64_t leaf_no,
+			    void *data);
+
+/**
+ * int gfs_filecmp - Compare two filenames
+ * @file1: The first filename
+ * @file2: The second filename
+ * @len_of_file2: The length of the second file
+ *
+ * This routine compares two filenames and returns TRUE if they are equal.
+ *
+ * Returns: TRUE (!=0) if the files are the same, otherwise FALSE (0).
+ */
+
+int
+gfs_filecmp(struct qstr *file1, char *file2, int len_of_file2)
+{
+	if (file1->len != len_of_file2)
+		return FALSE;
+	if (memcmp(file1->name, file2, file1->len))
+		return FALSE;
+	return TRUE;
+}
+
+/**
+ * dirent_first - Return the first dirent
+ * @dip: the directory
+ * @bh: The buffer
+ * @dent: Pointer to list of dirents
+ *
+ * return first dirent whether bh points to leaf or stuffed dinode
+ *
+ * Returns: IS_LEAF, IS_DINODE, or -errno
+ */
+
+static int
+dirent_first(struct gfs_inode *dip, struct buffer_head *bh,
+	     struct gfs_dirent **dent)
+{
+	struct gfs_meta_header *h = (struct gfs_meta_header *)bh->b_data;
+
+	if (gfs32_to_cpu(h->mh_type) == GFS_METATYPE_LF) {
+		if (gfs_meta_check(dip->i_sbd, bh))
+			return -EIO;
+		*dent = (struct gfs_dirent *)(bh->b_data + sizeof(struct gfs_leaf));
+		return IS_LEAF;
+	} else {
+		if (gfs_metatype_check(dip->i_sbd, bh, GFS_METATYPE_DI))
+			return -EIO;
+		*dent = (struct gfs_dirent *)(bh->b_data + sizeof(struct gfs_dinode));
+		return IS_DINODE;
+	}
+}
+
+/**
+ * dirent_next - Next dirent
+ * @dip: the directory
+ * @bh: The buffer
+ * @dent: Pointer to list of dirents
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+static int
+dirent_next(struct gfs_inode *dip, struct buffer_head *bh,
+	    struct gfs_dirent **dent)
+{
+	struct gfs_dirent *tmp, *cur;
+	char *bh_end;
+	uint32_t cur_rec_len;
+
+	cur = *dent;
+	bh_end = bh->b_data + bh->b_size;
+	cur_rec_len = gfs16_to_cpu(cur->de_rec_len);
+
+	if ((char *)cur + cur_rec_len >= bh_end) {
+		if ((char *)cur + cur_rec_len > bh_end) {
+			gfs_consist_inode(dip);
+			return -EIO;
+		}
+		return -ENOENT;
+	}
+
+	tmp = (struct gfs_dirent *)((char *)cur + cur_rec_len);
+
+	if ((char *)tmp + gfs16_to_cpu(tmp->de_rec_len) > bh_end) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+        /* Only the first dent could ever have de_ino == 0 */
+	if (!tmp->de_inum.no_formal_ino) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	*dent = tmp;
+
+	return 0;
+}
+
+/**
+ * dirent_del - Delete a dirent
+ * @dip: The GFS inode
+ * @bh: The buffer
+ * @prev: The previous dirent
+ * @cur: The current dirent
+ *
+ */
+
+static void
+dirent_del(struct gfs_inode *dip, struct buffer_head *bh,
+	   struct gfs_dirent *prev, struct gfs_dirent *cur)
+{
+	uint32_t cur_rec_len, prev_rec_len;
+
+	if (!cur->de_inum.no_formal_ino) {
+		gfs_consist_inode(dip);
+		return;
+	}
+
+	gfs_trans_add_bh(dip->i_gl, bh);
+
+	/* If there is no prev entry, this is the first entry in the block.
+	   The de_rec_len is already as big as it needs to be.  Just zero
+	   out the inode number and return.  */
+
+	if (!prev) {
+		cur->de_inum.no_formal_ino = 0;	/* No endianess worries */
+		return;
+	}
+
+	/*  Combine this dentry with the previous one.  */
+
+	prev_rec_len = gfs16_to_cpu(prev->de_rec_len);
+	cur_rec_len = gfs16_to_cpu(cur->de_rec_len);
+
+	if ((char *)prev + prev_rec_len != (char *)cur)
+		gfs_consist_inode(dip);
+	if ((char *)cur + cur_rec_len > bh->b_data + bh->b_size)
+		gfs_consist_inode(dip);
+
+	prev_rec_len += cur_rec_len;
+	prev->de_rec_len = cpu_to_gfs16(prev_rec_len);
+}
+
+/**
+ * gfs_dirent_alloc - Allocate a directory entry
+ * @dip: The GFS inode
+ * @bh: The buffer
+ * @name_len: The length of the name
+ * @dent_out: Pointer to list of dirents
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+int
+gfs_dirent_alloc(struct gfs_inode *dip, struct buffer_head *bh, int name_len,
+		 struct gfs_dirent **dent_out)
+{
+	struct gfs_dirent *dent, *new;
+	unsigned int rec_len = GFS_DIRENT_SIZE(name_len);
+	unsigned int entries = 0, offset = 0;
+	int type;
+
+	type = dirent_first(dip, bh, &dent);
+	if (type < 0)
+		return type;
+
+	if (type == IS_LEAF) {
+		struct gfs_leaf *leaf = (struct gfs_leaf *)bh->b_data;
+		entries = gfs16_to_cpu(leaf->lf_entries);
+		offset = sizeof(struct gfs_leaf);
+	} else {
+		struct gfs_dinode *dinode = (struct gfs_dinode *)bh->b_data;
+		entries = gfs32_to_cpu(dinode->di_entries);
+		offset = sizeof(struct gfs_dinode);
+	}
+
+	if (!entries) {
+		if (dent->de_inum.no_formal_ino) {
+			gfs_consist_inode(dip);
+			return -EIO;
+		}
+
+		gfs_trans_add_bh(dip->i_gl, bh);
+
+		dent->de_rec_len = bh->b_size - offset;
+		dent->de_rec_len = cpu_to_gfs16(dent->de_rec_len);
+		dent->de_name_len = cpu_to_gfs16(name_len);
+
+		*dent_out = dent;
+		return 0;
+	}
+
+	do {
+		uint32_t cur_rec_len, cur_name_len;
+
+		cur_rec_len = gfs16_to_cpu(dent->de_rec_len);
+		cur_name_len = gfs16_to_cpu(dent->de_name_len);
+
+		if ((!dent->de_inum.no_formal_ino && cur_rec_len >= rec_len) ||
+		    (cur_rec_len >= GFS_DIRENT_SIZE(cur_name_len) + rec_len)) {
+			gfs_trans_add_bh(dip->i_gl, bh);
+
+			if (dent->de_inum.no_formal_ino) {
+				new = (struct gfs_dirent *)((char *)dent +
+							    GFS_DIRENT_SIZE(cur_name_len));
+				memset(new, 0, sizeof(struct gfs_dirent));
+
+				new->de_rec_len = cpu_to_gfs16(cur_rec_len -
+							       GFS_DIRENT_SIZE(cur_name_len));
+				new->de_name_len = cpu_to_gfs16(name_len);
+
+				dent->de_rec_len = cur_rec_len - gfs16_to_cpu(new->de_rec_len);
+				dent->de_rec_len = cpu_to_gfs16(dent->de_rec_len);
+
+				*dent_out = new;
+				return 0;
+			}
+
+			dent->de_name_len = cpu_to_gfs16(name_len);
+
+			*dent_out = dent;
+			return 0;
+		}
+	} while (dirent_next(dip, bh, &dent) == 0);
+
+	return -ENOSPC;
+}
+
+/**
+ * dirent_fits - See if we can fit a entry in this buffer
+ * @dip: The GFS inode
+ * @bh: The buffer
+ * @name_len: The length of the name
+ *
+ * Returns: TRUE if it can fit, FALSE otherwise
+ */
+
+static int
+dirent_fits(struct gfs_inode *dip, struct buffer_head *bh, int name_len)
+{
+	struct gfs_dirent *dent;
+	unsigned int rec_len = GFS_DIRENT_SIZE(name_len);
+	unsigned int entries = 0;
+	int type;
+
+	type = dirent_first(dip, bh, &dent);
+	if (type < 0)
+		return type;
+
+	if (type == IS_LEAF) {
+		struct gfs_leaf *leaf = (struct gfs_leaf *)bh->b_data;
+		entries = gfs16_to_cpu(leaf->lf_entries);
+	} else {
+		struct gfs_dinode *dinode = (struct gfs_dinode *)bh->b_data;
+		entries = gfs32_to_cpu(dinode->di_entries);
+	}
+
+	if (!entries)
+		return TRUE;
+
+	do {
+		uint32_t cur_rec_len, cur_name_len;
+
+		cur_rec_len = gfs16_to_cpu(dent->de_rec_len);
+		cur_name_len = gfs16_to_cpu(dent->de_name_len);
+
+		if ((!dent->de_inum.no_formal_ino && cur_rec_len >= rec_len) ||
+		    (cur_rec_len >= GFS_DIRENT_SIZE(cur_name_len) + rec_len))
+			return TRUE;
+	} while (dirent_next(dip, bh, &dent) == 0);
+
+	return FALSE;
+}
+
+/**
+ * leaf_search
+ * @bh:
+ * @filename:
+ * @dent_out:
+ * @dent_prev:
+ *
+ * Returns:
+ */
+
+static int
+leaf_search(struct gfs_inode *dip,
+	    struct buffer_head *bh, struct qstr *filename,
+	    struct gfs_dirent **dent_out, struct gfs_dirent **dent_prev)
+{
+	uint32_t hash;
+	struct gfs_dirent *dent, *prev = NULL;
+	unsigned int entries = 0;
+	int type;
+
+	type = dirent_first(dip, bh, &dent);
+	if (type < 0)
+		return type;
+
+	if (type == IS_LEAF) {
+		struct gfs_leaf *leaf = (struct gfs_leaf *)bh->b_data;
+		entries = gfs16_to_cpu(leaf->lf_entries);
+	} else if (type == IS_DINODE) {
+		struct gfs_dinode *dinode = (struct gfs_dinode *)bh->b_data;
+		entries = gfs32_to_cpu(dinode->di_entries);
+	}
+
+	hash = gfs_dir_hash(filename->name, filename->len);
+
+	do {
+		if (!dent->de_inum.no_formal_ino) {
+			prev = dent;
+			continue;
+		}
+
+		if (gfs32_to_cpu(dent->de_hash) == hash &&
+		    gfs_filecmp(filename, (char *)(dent + 1),
+				gfs16_to_cpu(dent->de_name_len))) {
+			*dent_out = dent;
+			if (dent_prev)
+				*dent_prev = prev;
+
+			return 0;
+		}
+
+		prev = dent;
+	} while (dirent_next(dip, bh, &dent) == 0);
+
+	return -ENOENT;
+}
+
+/**
+ * get_leaf - Get leaf
+ * @dip:
+ * @leaf_no:
+ * @bh_out:
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+static int
+get_leaf(struct gfs_inode *dip, uint64_t leaf_no, struct buffer_head **bhp)
+{
+	int error;
+
+	error = gfs_dread(dip->i_gl, leaf_no, DIO_START | DIO_WAIT, bhp);
+	if (!error && gfs_metatype_check(dip->i_sbd, *bhp, GFS_METATYPE_LF))
+		error = -EIO;
+
+	return error;
+}
+
+/**
+ * get_leaf_nr - Get a leaf number associated with the index
+ * @dip: The GFS inode
+ * @index:
+ * @leaf_out:
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+static int
+get_leaf_nr(struct gfs_inode *dip, uint32_t index, uint64_t *leaf_out)
+{
+	uint64_t leaf_no;
+	int error;
+
+	error = gfs_internal_read(dip, (char *)&leaf_no,
+				  index * sizeof(uint64_t),
+				  sizeof(uint64_t));
+	if (error != sizeof(uint64_t))
+		return (error < 0) ? error : -EIO;
+
+	*leaf_out = gfs64_to_cpu(leaf_no);
+
+	return 0;
+}
+
+/**
+ * get_first_leaf - Get first leaf
+ * @dip: The GFS inode
+ * @index:
+ * @bh_out:
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+static int
+get_first_leaf(struct gfs_inode *dip, uint32_t index,
+	       struct buffer_head **bh_out)
+{
+	uint64_t leaf_no;
+	int error;
+
+	error = get_leaf_nr(dip, index, &leaf_no);
+	if (!error)
+		error = get_leaf(dip, leaf_no, bh_out);
+
+	return error;
+}
+
+/**
+ * get_next_leaf - Get next leaf
+ * @dip: The GFS inode
+ * @bh_in: The buffer
+ * @bh_out:
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+static int
+get_next_leaf(struct gfs_inode *dip, struct buffer_head *bh_in,
+	      struct buffer_head **bh_out)
+{
+	struct gfs_leaf *leaf;
+	int error;
+
+	leaf = (struct gfs_leaf *)bh_in->b_data;
+
+	if (!leaf->lf_next)
+		error = -ENOENT;
+	else
+		error = get_leaf(dip, gfs64_to_cpu(leaf->lf_next), bh_out);
+
+	return error;
+}
+
+/**
+ * linked_leaf_search - Linked leaf search
+ * @dip: The GFS inode
+ * @filename: The filename to search for
+ * @dent_out:
+ * @dent_prev:
+ * @bh_out:
+ *
+ * Returns: 0 on sucess, error code otherwise
+ */
+
+static int
+linked_leaf_search(struct gfs_inode *dip, struct qstr *filename,
+		   struct gfs_dirent **dent_out, struct gfs_dirent **dent_prev,
+		   struct buffer_head **bh_out)
+{
+	struct buffer_head *bh = NULL, *bh_next;
+	uint32_t hsize, index;
+	uint32_t hash;
+	int error;
+
+	hsize = 1 << dip->i_di.di_depth;
+	if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	/*  Figure out the address of the leaf node.  */
+
+	hash = gfs_dir_hash(filename->name, filename->len);
+	index = hash >> (32 - dip->i_di.di_depth);
+
+	error = get_first_leaf(dip, index, &bh_next);
+	if (error)
+		return error;
+
+	/*  Find the entry  */
+
+	do {
+		if (bh)
+			brelse(bh);
+
+		bh = bh_next;
+
+		error = leaf_search(dip, bh, filename, dent_out, dent_prev);
+		switch (error) {
+		case 0:
+			*bh_out = bh;
+			return 0;
+
+		case -ENOENT:
+			break;
+
+		default:
+			brelse(bh);
+			return error;
+		}
+
+		error = get_next_leaf(dip, bh, &bh_next);
+	}
+	while (!error);
+
+	brelse(bh);
+
+	return error;
+}
+
+/**
+ * dir_make_exhash - Convert a stuffed directory into an ExHash directory
+ * @dip: The GFS inode
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+static int
+dir_make_exhash(struct gfs_inode *dip)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_dirent *dent;
+	struct buffer_head *bh, *dibh;
+	struct gfs_leaf *leaf;
+	int y;
+	uint32_t x;
+	uint64_t *lp, bn;
+	int error;
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		return error;
+
+	/*  Allocate a new block for the first leaf node  */
+
+	error = gfs_metaalloc(dip, &bn);
+	if (error)
+		goto fail;
+
+	/*  Turn over a new leaf  */
+
+	error = gfs_dread(dip->i_gl, bn, DIO_NEW | DIO_START | DIO_WAIT, &bh);
+	if (error)
+		goto fail;
+
+	gfs_trans_add_bh(dip->i_gl, bh);
+	gfs_metatype_set(bh, GFS_METATYPE_LF, GFS_FORMAT_LF);
+	gfs_buffer_clear_tail(bh, sizeof(struct gfs_meta_header));
+
+	/*  Fill in the leaf structure  */
+
+	leaf = (struct gfs_leaf *)bh->b_data;
+
+	gfs_assert(sdp, dip->i_di.di_entries < (1 << 16),);
+
+	leaf->lf_dirent_format = cpu_to_gfs32(GFS_FORMAT_DE);
+	leaf->lf_entries = cpu_to_gfs16(dip->i_di.di_entries);
+
+	/*  Copy dirents  */
+
+	gfs_buffer_copy_tail(bh, sizeof(struct gfs_leaf), dibh,
+			     sizeof(struct gfs_dinode));
+
+	/*  Find last entry  */
+
+	x = 0;
+	dirent_first(dip, bh, &dent);
+
+	do {
+		if (!dent->de_inum.no_formal_ino)
+			continue;
+		if (++x == dip->i_di.di_entries)
+			break;
+	}
+	while (dirent_next(dip, bh, &dent) == 0);
+
+	/*  Adjust the last dirent's record length
+	   (Remember that dent still points to the last entry.)  */
+
+	dent->de_rec_len = gfs16_to_cpu(dent->de_rec_len) +
+		sizeof(struct gfs_dinode) -
+		sizeof(struct gfs_leaf);
+	dent->de_rec_len = cpu_to_gfs16(dent->de_rec_len);
+
+	brelse(bh);
+
+	/*  We're done with the new leaf block, now setup the new
+	    hash table.  */
+
+	gfs_trans_add_bh(dip->i_gl, dibh);
+	gfs_buffer_clear_tail(dibh, sizeof (struct gfs_dinode));
+
+	lp = (uint64_t *)(dibh->b_data + sizeof(struct gfs_dinode));
+
+	for (x = sdp->sd_hash_ptrs; x--; lp++)
+		*lp = cpu_to_gfs64(bn);
+
+	dip->i_di.di_size = sdp->sd_sb.sb_bsize / 2;
+	dip->i_di.di_blocks++;
+	dip->i_di.di_flags |= GFS_DIF_EXHASH;
+	dip->i_di.di_payload_format = 0;
+
+	for (x = sdp->sd_hash_ptrs, y = -1; x; x >>= 1, y++) ;
+	dip->i_di.di_depth = y;
+
+	gfs_dinode_out(&dip->i_di, dibh->b_data);
+
+	brelse(dibh);
+
+	return 0;
+
+ fail:
+	brelse(dibh);
+	return error;
+}
+
+/**
+ * dir_split_leaf - Split a leaf block into two
+ * @dip: The GFS inode
+ * @index:
+ * @leaf_no:
+ *
+ * Returns: 0 on success, error code on failure
+ */
+
+static int
+dir_split_leaf(struct gfs_inode *dip, uint32_t index, uint64_t leaf_no)
+{
+	struct buffer_head *nbh, *obh, *dibh;
+	struct gfs_leaf *nleaf, *oleaf;
+	struct gfs_dirent *dent, *prev = NULL, *next = NULL, *new;
+	uint32_t start, len, half_len, divider;
+	uint64_t bn, *lp;
+	uint32_t name_len;
+	int x, moved = FALSE;
+	int error, lp_vfree=0;
+
+	/*  Allocate the new leaf block  */
+
+	error = gfs_metaalloc(dip, &bn);
+	if (error)
+		return error;
+
+	/*  Get the new leaf block  */
+
+	error = gfs_dread(dip->i_gl, bn,
+			  DIO_NEW | DIO_START | DIO_WAIT, &nbh);
+	if (error)
+		return error;
+
+	gfs_trans_add_bh(dip->i_gl, nbh);
+	gfs_metatype_set(nbh, GFS_METATYPE_LF, GFS_FORMAT_LF);
+	gfs_buffer_clear_tail(nbh, sizeof (struct gfs_meta_header));
+
+	nleaf = (struct gfs_leaf *)nbh->b_data;
+
+	nleaf->lf_dirent_format = cpu_to_gfs32(GFS_FORMAT_DE);
+
+	/*  Get the old leaf block  */
+
+	error = get_leaf(dip, leaf_no, &obh);
+	if (error)
+		goto fail;
+
+	gfs_trans_add_bh(dip->i_gl, obh);
+
+	oleaf = (struct gfs_leaf *)obh->b_data;
+
+	/*  Compute the start and len of leaf pointers in the hash table.  */
+
+	len = 1 << (dip->i_di.di_depth - gfs16_to_cpu(oleaf->lf_depth));
+	half_len = len >> 1;
+	if (!half_len) {
+		gfs_consist_inode(dip);
+		error = -EIO;
+		goto fail_brelse;
+	}
+
+	start = (index & ~(len - 1));
+
+	/* Change the pointers.
+	   Don't bother distinguishing stuffed from non-stuffed.
+	   This code is complicated enough already. */
+
+	lp = kmalloc(half_len * sizeof (uint64_t), GFP_KERNEL);
+	if (unlikely(!lp)) {
+		lp = vmalloc(half_len * sizeof (uint64_t));
+		if (!lp) {
+			printk("GFS: dir_split_leaf vmalloc fail - half_len=%d\n", half_len);
+			error = -ENOMEM;
+			goto fail_brelse;
+		} else
+			lp_vfree = 1;
+	}
+
+	error = gfs_internal_read(dip, (char *)lp, start * sizeof(uint64_t),
+				  half_len * sizeof(uint64_t));
+	if (error != half_len * sizeof(uint64_t)) {
+		if (error >= 0)
+			error = -EIO;
+		goto fail_lpfree;
+	}
+
+	/*  Change the pointers  */
+
+	for (x = 0; x < half_len; x++)
+		lp[x] = cpu_to_gfs64(bn);
+
+	error = gfs_internal_write(dip, (char *)lp, start * sizeof(uint64_t),
+				   half_len * sizeof(uint64_t));
+	if (error != half_len * sizeof(uint64_t)) {
+		if (error >= 0)
+			error = -EIO;
+		goto fail_lpfree;
+	}
+
+	if (unlikely(lp_vfree))
+		vfree(lp);
+	else
+		kfree(lp);
+
+	/*  Compute the divider  */
+
+	divider = (start + half_len) << (32 - dip->i_di.di_depth);
+
+	/*  Copy the entries  */
+
+	dirent_first(dip, obh, &dent);
+
+	do {
+		next = dent;
+		if (dirent_next(dip, obh, &next))
+			next = NULL;
+
+		if (dent->de_inum.no_formal_ino &&
+		    gfs32_to_cpu(dent->de_hash) < divider) {
+			name_len = gfs16_to_cpu(dent->de_name_len);
+
+			gfs_dirent_alloc(dip, nbh, name_len, &new);
+
+			new->de_inum = dent->de_inum; /* No endianness worries */
+			new->de_hash = dent->de_hash; /* No endianness worries */
+			new->de_type = dent->de_type; /* No endianness worries */
+			memcpy((char *)(new + 1), (char *)(dent + 1),
+			       name_len);
+
+			nleaf->lf_entries = gfs16_to_cpu(nleaf->lf_entries) + 1;
+			nleaf->lf_entries = cpu_to_gfs16(nleaf->lf_entries);
+
+			dirent_del(dip, obh, prev, dent);
+
+			if (!oleaf->lf_entries)
+				gfs_consist_inode(dip);
+			oleaf->lf_entries = gfs16_to_cpu(oleaf->lf_entries) - 1;
+			oleaf->lf_entries = cpu_to_gfs16(oleaf->lf_entries);
+
+			if (!prev)
+				prev = dent;
+
+			moved = TRUE;
+		} else
+			prev = dent;
+
+		dent = next;
+	}
+	while (dent);
+
+	/* If none of the entries got moved into the new leaf,
+	   artificially fill in the first entry. */
+
+	if (!moved) {
+		gfs_dirent_alloc(dip, nbh, 0, &new);
+		new->de_inum.no_formal_ino = 0;
+	}
+
+	oleaf->lf_depth = gfs16_to_cpu(oleaf->lf_depth) + 1;
+	oleaf->lf_depth = cpu_to_gfs16(oleaf->lf_depth);
+	nleaf->lf_depth = oleaf->lf_depth;
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (!gfs_assert_withdraw(dip->i_sbd, !error)) {
+		dip->i_di.di_blocks++;
+		gfs_dinode_out(&dip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	brelse(obh);
+	brelse(nbh);
+
+	return error;
+
+ fail_lpfree:
+	if (unlikely(lp_vfree))
+		vfree(lp);
+	else
+		kfree(lp);
+
+ fail_brelse:
+	brelse(obh);
+
+ fail:
+	brelse(nbh);
+	return error;
+}
+
+/**
+ * dir_double_exhash - Double size of ExHash table
+ * @dip: The GFS dinode
+ *
+ * Returns: 0 on success, error code on failure
+ */
+
+static int
+dir_double_exhash(struct gfs_inode *dip)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct buffer_head *dibh;
+	uint32_t hsize;
+	uint64_t *buf;
+	uint64_t *from, *to;
+	uint64_t block;
+	int x;
+	int error = 0;
+
+	hsize = 1 << dip->i_di.di_depth;
+	if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	/*  Allocate both the "from" and "to" buffers in one big chunk  */
+
+	buf = gmalloc(3 * sdp->sd_hash_bsize);
+
+	for (block = dip->i_di.di_size >> sdp->sd_hash_bsize_shift; block--;) {
+		error = gfs_internal_read(dip, (char *)buf,
+					  block * sdp->sd_hash_bsize,
+					  sdp->sd_hash_bsize);
+		if (error != sdp->sd_hash_bsize) {
+			if (error >= 0)
+				error = -EIO;
+			goto fail;
+		}
+
+		from = buf;
+		to = (uint64_t *)((char *)buf + sdp->sd_hash_bsize);
+
+		for (x = sdp->sd_hash_ptrs; x--; from++) {
+			*to++ = *from;	/*  No endianess worries  */
+			*to++ = *from;
+		}
+
+		error = gfs_internal_write(dip, (char *)buf + sdp->sd_hash_bsize,
+					   block * sdp->sd_sb.sb_bsize,
+					   sdp->sd_sb.sb_bsize);
+		if (error != sdp->sd_sb.sb_bsize) {
+			if (error >= 0)
+				error = -EIO;
+			goto fail;
+		}
+	}
+
+	kfree(buf);
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (!gfs_assert_withdraw(sdp, !error)) {
+		dip->i_di.di_depth++;
+		gfs_dinode_out(&dip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	return error;
+
+ fail:
+	kfree(buf);
+
+	return error;
+}
+
+/**
+ * compare_dents - compare directory entries by hash value
+ * @a: first dent
+ * @b: second dent
+ *
+ * When comparing the hash entries of @a to @b:
+ *   gt: returns 1
+ *   lt: returns -1
+ *   eq: returns 0
+ */
+
+static int
+compare_dents(const void *a, const void *b)
+{
+	struct gfs_dirent *dent_a, *dent_b;
+	uint32_t hash_a, hash_b;
+	int ret = 0;
+
+	dent_a = *(struct gfs_dirent **)a;
+	hash_a = dent_a->de_hash;
+	hash_a = gfs32_to_cpu(hash_a);
+
+	dent_b = *(struct gfs_dirent **)b;
+	hash_b = dent_b->de_hash;
+	hash_b = gfs32_to_cpu(hash_b);
+
+	if (hash_a > hash_b)
+		ret = 1;
+	else if (hash_a < hash_b)
+		ret = -1;
+	else {
+		unsigned int len_a = gfs16_to_cpu(dent_a->de_name_len);
+		unsigned int len_b = gfs16_to_cpu(dent_b->de_name_len);
+
+		if (len_a > len_b)
+			ret = 1;
+		else if (len_a < len_b)
+			ret = -1;
+		else
+			ret = memcmp((char *)(dent_a + 1),
+				     (char *)(dent_b + 1),
+				     len_a);
+	}
+
+	return ret;
+}
+
+/**
+ * do_filldir_main - read out directory entries
+ * @dip: The GFS inode
+ * @offset: The offset in the file to read from
+ * @opaque: opaque data to pass to filldir
+ * @filldir: The function to pass entries to 
+ * @darr: an array of struct gfs_dirent pointers to read
+ * @entries: the number of entries in darr
+ * @copied: pointer to int that's non-zero if a entry has been copied out
+ *
+ * Jump through some hoops to make sure that if there are hash collsions,
+ * they are read out at the beginning of a buffer.  We want to minimize
+ * the possibility that they will fall into different readdir buffers or
+ * that someone will want to seek to that location.
+ *
+ * Returns: errno, >0 on exception from filldir
+ */
+
+static int
+do_filldir_main(struct gfs_inode *dip, uint64_t *offset,
+		void *opaque, gfs_filldir_t filldir,
+		struct gfs_dirent **darr, uint32_t entries, int *copied)
+{
+	struct gfs_dirent *dent, *dent_next;
+	struct gfs_inum inum;
+	uint64_t off, off_next;
+	unsigned int x, y;
+	int run = FALSE;
+	int error = 0;
+
+	gfs_sort(darr, entries, sizeof(struct gfs_dirent *), compare_dents);
+
+	dent_next = darr[0];
+	off_next = gfs32_to_cpu(dent_next->de_hash);
+	off_next = gfs_dir_hash2offset(off_next);
+
+	for (x = 0, y = 1; x < entries; x++, y++) {
+		dent = dent_next;
+		off = off_next;
+
+		if (y < entries) {
+			dent_next = darr[y];
+			off_next = gfs32_to_cpu(dent_next->de_hash);
+			off_next = gfs_dir_hash2offset(off_next);
+
+			if (off < *offset)
+				continue;
+			*offset = off;
+
+			if (off_next == off) {
+				if (*copied && !run)
+					return 1;
+				run = TRUE;
+			} else
+				run = FALSE;
+		} else {
+			if (off < *offset)
+				continue;
+			*offset = off;
+		}
+
+		gfs_inum_in(&inum, (char *)&dent->de_inum);
+
+		error = filldir(opaque, (char *)(dent + 1),
+				gfs16_to_cpu(dent->de_name_len),
+				off, &inum,
+				gfs16_to_cpu(dent->de_type));
+		if (error)
+			return 1;
+
+		*copied = TRUE;
+	}
+
+	/* Increment the *offset by one, so the next time we come into the do_filldir fxn, 
+	   we get the next entry instead of the last one in the current leaf */
+
+	(*offset)++;
+
+	return 0;
+}
+
+/**
+ * do_filldir_single - Read directory entries out of a single block
+ * @dip: The GFS inode
+ * @offset: The offset in the file to read from
+ * @opaque: opaque data to pass to filldir
+ * @filldir: The function to pass entries to 
+ * @bh: the block
+ * @entries: the number of entries in the block
+ * @copied: pointer to int that's non-zero if a entry has been copied out
+ *
+ * Returns: errno, >0 on exception from filldir
+ */
+
+static int
+do_filldir_single(struct gfs_inode *dip, uint64_t *offset,
+		  void *opaque, gfs_filldir_t filldir,
+		  struct buffer_head *bh, uint32_t entries, int *copied)
+{
+	struct gfs_dirent **darr;
+	struct gfs_dirent *de;
+	unsigned int e = 0;
+	int error, do_vfree=0;
+
+	if (!entries)
+		return 0;
+
+	darr = kmalloc(entries * sizeof(struct gfs_dirent *), GFP_KERNEL);
+	if (unlikely(!darr)) {
+		darr = vmalloc(entries * sizeof (struct gfs_dirent *));
+		if (!darr) {
+			printk("GFS: do_filldir_single vmalloc fails, entries=%d\n", entries);
+			return -ENOMEM;
+		}
+	else
+		do_vfree = 1;
+	}
+
+	dirent_first(dip, bh, &de);
+	do {
+		if (!de->de_inum.no_formal_ino)
+			continue;
+		if (e >= entries) {
+			gfs_consist_inode(dip);
+			error = -EIO;
+			goto out;
+		}
+		darr[e++] = de;
+	}
+	while (dirent_next(dip, bh, &de) == 0);
+
+	if (e != entries) {
+		gfs_consist_inode(dip);
+		error = -EIO;
+		goto out;
+	}
+
+	error = do_filldir_main(dip, offset, opaque, filldir, darr,
+				entries, copied);
+
+ out:
+	if (unlikely(do_vfree))
+		vfree(darr);
+	else
+		kfree(darr);
+
+	return error;
+}
+
+/**
+ * do_filldir_multi - Read directory entries out of a linked leaf list
+ * @dip: The GFS inode
+ * @offset: The offset in the file to read from
+ * @opaque: opaque data to pass to filldir
+ * @filldir: The function to pass entries to 
+ * @bh: the first leaf in the list
+ * @copied: pointer to int that's non-zero if a entry has been copied out
+ *
+ * Returns: errno, >0 on exception from filldir
+ */
+
+static int
+do_filldir_multi(struct gfs_inode *dip, uint64_t *offset,
+		 void *opaque, gfs_filldir_t filldir,
+		 struct buffer_head *bh, int *copied)
+{
+	struct buffer_head **larr = NULL;
+	struct gfs_dirent **darr;
+	struct gfs_leaf *leaf;
+	struct buffer_head *tmp_bh;
+	struct gfs_dirent *de;
+	unsigned int entries, e = 0;
+	unsigned int leaves = 0, l = 0;
+	unsigned int x;
+	uint64_t ln;
+	int error = 0, leaves_vfree=0, entries_vfree=0;
+
+	/*  Count leaves and entries  */
+
+	leaf = (struct gfs_leaf *)bh->b_data;
+	entries = gfs16_to_cpu(leaf->lf_entries);
+	ln = leaf->lf_next;
+
+	while (ln) {
+		ln = gfs64_to_cpu(ln);
+
+		error = get_leaf(dip, ln, &tmp_bh);
+		if (error)
+			return error;
+
+		leaf = (struct gfs_leaf *)tmp_bh->b_data;
+		if (leaf->lf_entries) {
+			entries += gfs16_to_cpu(leaf->lf_entries);
+			leaves++;
+		}
+		ln = leaf->lf_next;
+
+		brelse(tmp_bh);
+	}
+
+	/*  Bail out if there's nothing to do  */
+
+	if (!entries)
+		return 0;
+
+	/*  Alloc arrays  */
+
+	if (leaves) {
+		larr = kmalloc(leaves * sizeof(struct buffer_head *), GFP_KERNEL);
+		if (unlikely(!larr)) {
+			larr = vmalloc(leaves * sizeof (struct buffer_head *));
+			if (!larr) {
+				printk("GFS: do_filldir_multi vmalloc fails leaves=%d\n", leaves);
+				return -ENOMEM;
+			} else
+				leaves_vfree = 1;
+		}
+	}
+
+	darr = kmalloc(entries * sizeof(struct gfs_dirent *), GFP_KERNEL);
+	if (unlikely(!darr)) {
+		darr = vmalloc(entries * sizeof (struct gfs_dirent *));
+		if (!darr) {
+			printk("GFS: do_filldir_multi vmalloc fails entries=%d\n", entries);
+			if (larr) {
+				if (leaves_vfree) 
+					vfree(larr);
+				else 
+					kfree(larr);
+			}
+			return -ENOMEM;
+		} else
+			entries_vfree = 1;
+	} 
+	if (!darr) {
+		if (larr)
+			kfree(larr);
+		return -ENOMEM;
+	}
+
+	/*  Fill in arrays  */
+
+	leaf = (struct gfs_leaf *)bh->b_data;
+	if (leaf->lf_entries) {
+		dirent_first(dip, bh, &de);
+		do {
+			if (!de->de_inum.no_formal_ino)
+				continue;
+			if (e >= entries) {
+				gfs_consist_inode(dip);
+				error = -EIO;
+				goto out;
+			}
+			darr[e++] = de;
+		}
+		while (dirent_next(dip, bh, &de) == 0);
+	}
+	ln = leaf->lf_next;
+
+	while (ln) {
+		ln = gfs64_to_cpu(ln);
+
+		error = get_leaf(dip, ln, &tmp_bh);
+		if (error)
+			goto out;
+
+		leaf = (struct gfs_leaf *)tmp_bh->b_data;
+		if (leaf->lf_entries) {
+			dirent_first(dip, tmp_bh, &de);
+			do {
+				if (!de->de_inum.no_formal_ino)
+					continue;
+				if (e >= entries) {
+					gfs_consist_inode(dip);
+					error = -EIO;
+					goto out;
+				}
+				darr[e++] = de;
+			}
+			while (dirent_next(dip, tmp_bh, &de) == 0);
+
+			larr[l++] = tmp_bh;
+
+			ln = leaf->lf_next;
+		} else {
+			ln = leaf->lf_next;
+			brelse(tmp_bh);
+		}
+	}
+
+	if (gfs_assert_withdraw(dip->i_sbd, l == leaves)) {
+		error = -EIO;
+		goto out;
+	}
+	if (e != entries) {
+		gfs_consist_inode(dip);
+		error = -EIO;
+		goto out;
+	}
+
+	/*  Do work  */
+
+	error = do_filldir_main(dip, offset, opaque, filldir, darr,
+				entries, copied);
+
+	/*  Clean up  */
+
+ out:
+	if (unlikely(entries_vfree))
+		vfree(darr);
+	else
+		kfree(darr);
+
+	for (x = 0; x < l; x++)
+		brelse(larr[x]);
+
+	if (leaves) {
+		if (unlikely(leaves_vfree))
+			vfree(larr);
+		else
+			kfree(larr);
+	}
+
+	return error;
+}
+
+/**
+ * dir_e_search - Search exhash (leaf) dir for inode matching name
+ * @dip: The GFS inode
+ * @filename: Filename string
+ * @inode: If non-NULL, function fills with formal inode # and block address
+ * @type: If non-NULL, function fills with GFS_FILE_... dinode type
+ *
+ * Returns:
+ */
+
+static int
+dir_e_search(struct gfs_inode *dip, struct qstr *filename,
+	     struct gfs_inum *inum, unsigned int *type)
+{
+	struct buffer_head *bh;
+	struct gfs_dirent *dent;
+	int error;
+
+	error = linked_leaf_search(dip, filename, &dent, NULL, &bh);
+	if (error)
+		return error;
+
+	if (inum)
+		gfs_inum_in(inum, (char *)&dent->de_inum);
+	if (type)
+		*type = gfs16_to_cpu(dent->de_type);
+
+	brelse(bh);
+
+	return 0;
+}
+
+/**
+ * dir_e_add -
+ * @dip: The GFS inode
+ * @filename:
+ * @inode:
+ * @type:
+ *
+ */
+
+static int
+dir_e_add(struct gfs_inode *dip, struct qstr *filename,
+	  struct gfs_inum *inum, unsigned int type)
+{
+	struct buffer_head *bh, *nbh, *dibh;
+	struct gfs_leaf *leaf, *nleaf;
+	struct gfs_dirent *dent;
+	uint32_t hsize, index;
+	uint32_t hash;
+	uint64_t leaf_no, bn;
+	int error;
+
+ restart:
+	hsize = 1 << dip->i_di.di_depth;
+	if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	/*  Figure out the address of the leaf node.  */
+
+	hash = gfs_dir_hash(filename->name, filename->len);
+	index = hash >> (32 - dip->i_di.di_depth);
+
+	error = get_leaf_nr(dip, index, &leaf_no);
+	if (error)
+		return error;
+
+	/*  Add entry to the leaf  */
+
+	for (;;) {
+		error = get_leaf(dip, leaf_no, &bh);
+		if (error)
+			return error;
+
+		leaf = (struct gfs_leaf *)bh->b_data;
+
+		if (gfs_dirent_alloc(dip, bh, filename->len, &dent)) {
+
+			if (gfs16_to_cpu(leaf->lf_depth) < dip->i_di.di_depth) {
+				/* Can we split the leaf? */
+
+				brelse(bh);
+
+				error = dir_split_leaf(dip, index, leaf_no);
+				if (error)
+					return error;
+
+				goto restart;
+
+			} else if (dip->i_di.di_depth < GFS_DIR_MAX_DEPTH) {
+				/* Can we double the hash table? */
+
+				brelse(bh);
+
+				error = dir_double_exhash(dip);
+				if (error)
+					return error;
+
+				goto restart;
+
+			} else if (leaf->lf_next) {
+				/* Can we try the next leaf in the list? */
+				leaf_no = gfs64_to_cpu(leaf->lf_next);
+				brelse(bh);
+				continue;
+
+			} else {
+				/* Create a new leaf and add it to the list. */
+
+				error = gfs_metaalloc(dip, &bn);
+				if (error) {
+					brelse(bh);
+					return error;
+				}
+
+				error = gfs_dread(dip->i_gl, bn,
+						  DIO_NEW | DIO_START | DIO_WAIT,
+						  &nbh);
+				if (error) {
+					brelse(bh);
+					return error;
+				}
+
+				gfs_trans_add_bh(dip->i_gl, nbh);
+				gfs_metatype_set(nbh,
+						 GFS_METATYPE_LF,
+						 GFS_FORMAT_LF);
+				gfs_buffer_clear_tail(nbh,
+						      sizeof(struct gfs_meta_header));
+
+				gfs_trans_add_bh(dip->i_gl, bh);
+				leaf->lf_next = cpu_to_gfs64(bn);
+
+				nleaf = (struct gfs_leaf *)nbh->b_data;
+				nleaf->lf_depth = leaf->lf_depth;
+				nleaf->lf_dirent_format = cpu_to_gfs32(GFS_FORMAT_DE);
+
+				gfs_dirent_alloc(dip, nbh, filename->len, &dent);
+
+				dip->i_di.di_blocks++;
+
+				brelse(bh);
+
+				bh = nbh;
+				leaf = nleaf;
+			}
+		}
+
+		/*  If the gfs_dirent_alloc() succeeded, it pinned the "bh".  */
+
+		gfs_inum_out(inum, (char *)&dent->de_inum);
+		dent->de_hash = cpu_to_gfs32(hash);
+		dent->de_type = cpu_to_gfs16(type);
+		memcpy((char *)(dent + 1), filename->name, filename->len);
+
+		leaf->lf_entries = gfs16_to_cpu(leaf->lf_entries) + 1;
+		leaf->lf_entries = cpu_to_gfs16(leaf->lf_entries);
+
+		brelse(bh);
+
+		error = gfs_get_inode_buffer(dip, &dibh);
+		if (error)
+			return error;
+
+		dip->i_di.di_entries++;
+		dip->i_di.di_mtime = dip->i_di.di_ctime = get_seconds();
+
+		gfs_trans_add_bh(dip->i_gl, dibh);
+		gfs_dinode_out(&dip->i_di, dibh->b_data);
+		brelse(dibh);
+
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * dir_e_del - 
+ * @dip: The GFS inode
+ * @filename:
+ *
+ * Returns:
+ */
+
+static int
+dir_e_del(struct gfs_inode *dip, struct qstr *filename)
+{
+	struct buffer_head *bh, *dibh;
+	struct gfs_dirent *dent, *prev;
+	struct gfs_leaf *leaf;
+	unsigned int entries;
+	int error;
+
+	error = linked_leaf_search(dip, filename, &dent, &prev, &bh);
+	if (error == -ENOENT) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+	if (error)
+		return error;
+
+	dirent_del(dip, bh, prev, dent); /* Pins bh */
+
+	leaf = (struct gfs_leaf *)bh->b_data;
+	entries = gfs16_to_cpu(leaf->lf_entries);
+	if (!entries)
+		gfs_consist_inode(dip);
+	entries--;
+	leaf->lf_entries = cpu_to_gfs16(entries);
+
+	brelse(bh);
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		return error;
+
+	if (!dip->i_di.di_entries)
+		gfs_consist_inode(dip);
+	dip->i_di.di_entries--;
+	dip->i_di.di_mtime = dip->i_di.di_ctime = get_seconds();
+
+	gfs_trans_add_bh(dip->i_gl, dibh);
+	gfs_dinode_out(&dip->i_di, dibh->b_data);
+	brelse(dibh);
+
+	return 0;
+}
+
+/**
+ * dir_e_read - Reads the entries from a directory into a filldir buffer 
+ * @dip: dinode pointer
+ * @offset: the hash of the last entry read shifted to the right once
+ * @opaque: buffer for the filldir function to fill 
+ * @filldir: points to the filldir function to use
+ *
+ * Returns: errno
+ */
+
+static int
+dir_e_read(struct gfs_inode *dip, uint64_t *offset, void *opaque,
+	   gfs_filldir_t filldir)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct buffer_head *bh;
+	struct gfs_leaf leaf;
+	uint32_t hsize, len;
+	uint32_t ht_offset, lp_offset, ht_offset_cur = -1;
+	uint32_t hash, index;
+	uint64_t *lp;
+	int copied = FALSE;
+	int error = 0;
+
+	hsize = 1 << dip->i_di.di_depth;
+	if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	hash = gfs_dir_offset2hash(*offset);
+	index = hash >> (32 - dip->i_di.di_depth);
+
+	lp = kmalloc(sdp->sd_hash_bsize, GFP_KERNEL);
+	if (!lp)
+		return -ENOMEM;
+
+	while (index < hsize) {
+		lp_offset = index & (sdp->sd_hash_ptrs - 1);
+		ht_offset = index - lp_offset;
+
+		if (ht_offset_cur != ht_offset) {
+			error = gfs_internal_read(dip, (char *)lp,
+						  ht_offset * sizeof(uint64_t),
+						  sdp->sd_hash_bsize);
+			if (error != sdp->sd_hash_bsize) {
+				if (error >= 0)
+					error = -EIO;
+				goto out;
+			}
+			ht_offset_cur = ht_offset;
+		}
+
+		error = get_leaf(dip, gfs64_to_cpu(lp[lp_offset]), &bh);
+		if (error)
+			goto out;
+
+		gfs_leaf_in(&leaf, bh->b_data);
+
+		if (leaf.lf_next)
+			error = do_filldir_multi(dip, offset,
+						 opaque, filldir,
+						 bh, &copied);
+		else
+			error = do_filldir_single(dip, offset,
+						  opaque, filldir,
+						  bh, leaf.lf_entries,
+						  &copied);
+
+		brelse(bh);
+
+		if (error) {
+			if (error > 0)
+				error = 0;
+			goto out;
+		}
+
+		len = 1 << (dip->i_di.di_depth - leaf.lf_depth);
+		index = (index & ~(len - 1)) + len;
+	}
+
+ out:
+	kfree(lp);
+
+	return error;
+}
+
+/**
+ * dir_e_mvino -
+ * @dip: The GFS inode
+ * @filename:
+ * @new_inode:
+ *
+ * Returns:
+ */
+
+static int
+dir_e_mvino(struct gfs_inode *dip, struct qstr *filename,
+	    struct gfs_inum *inum, unsigned int new_type)
+{
+	struct buffer_head *bh, *dibh;
+	struct gfs_dirent *dent;
+	int error;
+
+	error = linked_leaf_search(dip, filename, &dent, NULL, &bh);
+	if (error == -ENOENT) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+	if (error)
+		return error;
+
+	gfs_trans_add_bh(dip->i_gl, bh);
+
+	gfs_inum_out(inum, (char *)&dent->de_inum);
+	dent->de_type = cpu_to_gfs16(new_type);
+
+	brelse(bh);
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		return error;
+
+	dip->i_di.di_mtime = dip->i_di.di_ctime = get_seconds();
+
+	gfs_trans_add_bh(dip->i_gl, dibh);
+	gfs_dinode_out(&dip->i_di, dibh->b_data);
+	brelse(dibh);
+
+	return 0;
+}
+
+/**
+ * dir_l_search - Search linear (stuffed dinode) dir for inode matching name
+ * @dip: The GFS inode
+ * @filename: Filename string
+ * @inode: If non-NULL, function fills with formal inode # and block address
+ * @type: If non-NULL, function fills with GFS_FILE_... dinode type
+ *
+ * Returns:
+ */
+
+static int
+dir_l_search(struct gfs_inode *dip, struct qstr *filename,
+	     struct gfs_inum *inum, unsigned int *type)
+{
+	struct buffer_head *dibh;
+	struct gfs_dirent *dent;
+	int error;
+
+	if (!gfs_is_stuffed(dip)) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		return error;
+
+	error = leaf_search(dip, dibh, filename, &dent, NULL);
+	if (!error) {
+		if (inum)
+			gfs_inum_in(inum, (char *)&dent->de_inum);
+		if (type)
+			*type = gfs16_to_cpu(dent->de_type);
+	}
+
+	brelse(dibh);
+
+	return error;
+}
+
+/**
+ * dir_l_add -
+ * @dip: The GFS inode
+ * @filename:
+ * @inode:
+ * @type:
+ *
+ * Returns:
+ */
+
+static int
+dir_l_add(struct gfs_inode *dip, struct qstr *filename,
+	  struct gfs_inum *inum, unsigned int type)
+{
+	struct buffer_head *dibh;
+	struct gfs_dirent *dent;
+	int error;
+
+	if (!gfs_is_stuffed(dip)) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		return error;
+
+	if (gfs_dirent_alloc(dip, dibh, filename->len, &dent)) {
+		brelse(dibh);
+
+		error = dir_make_exhash(dip);
+		if (!error)
+			error = dir_e_add(dip, filename, inum, type);
+
+		return error;
+	}
+
+	/*  gfs_dirent_alloc() pins  */
+
+	gfs_inum_out(inum, (char *)&dent->de_inum);
+	dent->de_hash = gfs_dir_hash(filename->name, filename->len);
+	dent->de_hash = cpu_to_gfs32(dent->de_hash);
+	dent->de_type = cpu_to_gfs16(type);
+	memcpy((char *)(dent + 1), filename->name, filename->len);
+
+	dip->i_di.di_entries++;
+	dip->i_di.di_mtime = dip->i_di.di_ctime = get_seconds();
+
+	gfs_dinode_out(&dip->i_di, dibh->b_data);
+	brelse(dibh);
+
+	return 0;
+}
+
+/**
+ * dir_l_del - 
+ * @dip: The GFS inode
+ * @filename:
+ *
+ * Returns:
+ */
+
+static int
+dir_l_del(struct gfs_inode *dip, struct qstr *filename)
+{
+	struct buffer_head *dibh;
+	struct gfs_dirent *dent, *prev;
+	int error;
+
+	if (!gfs_is_stuffed(dip)) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		return error;
+
+	error = leaf_search(dip, dibh, filename, &dent, &prev);
+	if (error == -ENOENT) {
+		gfs_consist_inode(dip);
+		error = -EIO;
+		goto out;
+	}
+	if (error)
+		goto out;
+
+	dirent_del(dip, dibh, prev, dent);
+
+	/*  dirent_del() pins  */
+
+	if (!dip->i_di.di_entries)
+		gfs_consist_inode(dip);
+	dip->i_di.di_entries--;
+
+	dip->i_di.di_mtime = dip->i_di.di_ctime = get_seconds();
+
+	gfs_dinode_out(&dip->i_di, dibh->b_data);
+
+ out:
+	brelse(dibh);
+
+	return error;
+}
+
+/**
+ * dir_l_read -
+ * @dip:
+ * @offset:
+ * @opaque:
+ * @filldir:
+ *
+ * Returns:
+ */
+
+static int
+dir_l_read(struct gfs_inode *dip, uint64_t *offset, void *opaque,
+	   gfs_filldir_t filldir)
+{
+	struct buffer_head *dibh;
+	int copied = FALSE;
+	int error;
+
+	if (!gfs_is_stuffed(dip)) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	if (!dip->i_di.di_entries)
+		return 0;
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		return error;
+
+	error = do_filldir_single(dip, offset,
+				  opaque, filldir,
+				  dibh, dip->i_di.di_entries,
+				  &copied);
+	if (error > 0)
+		error = 0;
+
+	brelse(dibh);
+
+	return error;
+}
+
+/**
+ * dir_l_mvino -
+ * @dip:
+ * @filename:
+ * @new_inode:
+ *
+ * Returns:
+ */
+
+static int
+dir_l_mvino(struct gfs_inode *dip, struct qstr *filename,
+	    struct gfs_inum *inum, unsigned int new_type)
+{
+	struct buffer_head *dibh;
+	struct gfs_dirent *dent;
+	int error;
+
+	if (!gfs_is_stuffed(dip)) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		return error;
+
+	error = leaf_search(dip, dibh, filename, &dent, NULL);
+	if (error == -ENOENT) {
+		gfs_consist_inode(dip);
+		error = -EIO;
+		goto out;
+	}
+	if (error)
+		goto out;
+
+	gfs_trans_add_bh(dip->i_gl, dibh);
+
+	gfs_inum_out(inum, (char *)&dent->de_inum);
+	dent->de_type = cpu_to_gfs16(new_type);
+
+	dip->i_di.di_mtime = dip->i_di.di_ctime = get_seconds();
+
+	gfs_dinode_out(&dip->i_di, dibh->b_data);
+
+ out:
+	brelse(dibh);
+
+	return error;
+}
+
+/**
+ * gfs_dir_search - Search a directory
+ * @dip: The GFS inode
+ * @filename:
+ * @inode:
+ *
+ * This routine searches a directory for a file or another directory.
+ * Assumes a glock is held on dip.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_dir_search(struct gfs_inode *dip, struct qstr *filename,
+	       struct gfs_inum *inum, unsigned int *type)
+{
+	int error;
+
+	if (dip->i_di.di_flags & GFS_DIF_EXHASH)
+		error = dir_e_search(dip, filename, inum, type);
+	else
+		error = dir_l_search(dip, filename, inum, type);
+
+	return error;
+}
+
+/**
+ * gfs_dir_add - Add new filename into directory
+ * @dip: The GFS inode
+ * @filename: The new name
+ * @inode: The inode number of the entry
+ * @type: The type of the entry
+ *
+ * Returns: 0 on success, error code on failure
+ */
+
+int
+gfs_dir_add(struct gfs_inode *dip, struct qstr *filename,
+	    struct gfs_inum *inum, unsigned int type)
+{
+	int error;
+
+	if (dip->i_di.di_flags & GFS_DIF_EXHASH)
+		error = dir_e_add(dip, filename, inum, type);
+	else
+		error = dir_l_add(dip, filename, inum, type);
+
+	return error;
+}
+
+/**
+ * gfs_dir_del - Delete a directory entry
+ * @dip: The GFS inode
+ * @filename: The filename
+ *
+ * Returns: 0 on success, error code on failure
+ */
+
+int
+gfs_dir_del(struct gfs_inode *dip, struct qstr *filename)
+{
+	int error;
+
+	if (dip->i_di.di_flags & GFS_DIF_EXHASH)
+		error = dir_e_del(dip, filename);
+	else
+		error = dir_l_del(dip, filename);
+
+	return error;
+}
+
+/**
+ * gfs_dir_read - Translate a GFS filename
+ * @dip: The GFS inode
+ * @offset:
+ * @opaque:
+ * @filldir:
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+int
+gfs_dir_read(struct gfs_inode *dip, uint64_t * offset, void *opaque,
+	     gfs_filldir_t filldir)
+{
+	int error;
+
+	if (dip->i_di.di_flags & GFS_DIF_EXHASH)
+		error = dir_e_read(dip, offset, opaque, filldir);
+	else
+		error = dir_l_read(dip, offset, opaque, filldir);
+
+	return error;
+}
+
+/**
+ * gfs_dir_mvino - Change inode number of directory entry
+ * @dip: The GFS inode
+ * @filename:
+ * @new_inode:
+ *
+ * This routine changes the inode number of a directory entry.  It's used
+ * by rename to change ".." when a directory is moved.
+ * Assumes a glock is held on dvp.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_dir_mvino(struct gfs_inode *dip, struct qstr *filename,
+	      struct gfs_inum *inum, unsigned int new_type)
+{
+	int error;
+
+	if (dip->i_di.di_flags & GFS_DIF_EXHASH)
+		error = dir_e_mvino(dip, filename, inum, new_type);
+	else
+		error = dir_l_mvino(dip, filename, inum, new_type);
+
+	return error;
+}
+
+/**
+ * foreach_leaf - call a function for each leaf in a directory
+ * @dip: the directory
+ * @lc: the function to call for each each
+ * @data: private data to pass to it
+ *
+ * Returns: errno
+ */
+
+static int
+foreach_leaf(struct gfs_inode *dip, leaf_call_t lc, void *data)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct buffer_head *bh;
+	struct gfs_leaf leaf;
+	uint32_t hsize, len;
+	uint32_t ht_offset, lp_offset, ht_offset_cur = -1;
+	uint32_t index = 0;
+	uint64_t *lp;
+	uint64_t leaf_no;
+	int error = 0;
+
+	hsize = 1 << dip->i_di.di_depth;
+	if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	lp = kmalloc(sdp->sd_hash_bsize, GFP_KERNEL);
+	if (!lp)
+		return -ENOMEM;
+
+	while (index < hsize) {
+		lp_offset = index & (sdp->sd_hash_ptrs - 1);
+		ht_offset = index - lp_offset;
+
+		if (ht_offset_cur != ht_offset) {
+			error = gfs_internal_read(dip, (char *)lp,
+						  ht_offset * sizeof(uint64_t),
+						  sdp->sd_hash_bsize);
+			if (error != sdp->sd_hash_bsize) {
+				if (error >= 0)
+					error = -EIO;
+				goto out;
+			}
+			ht_offset_cur = ht_offset;
+		}
+
+		leaf_no = gfs64_to_cpu(lp[lp_offset]);
+		if (leaf_no) {
+			error = get_leaf(dip, leaf_no, &bh);
+			if (error)
+				goto out;
+			gfs_leaf_in(&leaf, bh->b_data);
+			brelse(bh);
+
+			len = 1 << (dip->i_di.di_depth - leaf.lf_depth);
+
+			error = lc(dip, index, len, leaf_no, data);
+			if (error)
+				goto out;
+
+			index = (index & ~(len - 1)) + len;
+		} else
+			index++;
+	}
+
+	if (index != hsize) {
+		gfs_consist_inode(dip);
+		error = -EIO;
+	}
+
+ out:
+	kfree(lp);
+
+	return error;
+}
+
+/**
+ * leaf_free - Deallocate a directory leaf
+ * @dip: the directory
+ * @index: the hash table offset in the directory
+ * @len: the number of pointers to this leaf
+ * @leaf_no: the leaf number
+ * @data: not used
+ *
+ * Returns: errno
+ */
+
+static int
+leaf_free(struct gfs_inode *dip,
+	  uint32_t index, uint32_t len,
+	  uint64_t leaf_no, void *data)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_leaf tmp_leaf;
+	struct gfs_rgrp_list rlist;
+	struct buffer_head *bh, *dibh;
+	uint64_t blk;
+	unsigned int rg_blocks = 0;
+	char *ht=0;
+	unsigned int x, size = len * sizeof(uint64_t);
+	int error;
+
+	memset(&rlist, 0, sizeof(struct gfs_rgrp_list));
+
+	gfs_alloc_get(dip);
+
+	error = gfs_quota_hold_m(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto out;
+
+	error = gfs_rindex_hold(sdp, &dip->i_alloc->al_ri_gh);
+	if (error)
+		goto out_qs;
+
+	/*  Count the number of leaves  */
+
+	for (blk = leaf_no; blk; blk = tmp_leaf.lf_next) {
+		error = get_leaf(dip, blk, &bh);
+		if (error)
+			goto out_rlist;
+		gfs_leaf_in(&tmp_leaf, (bh)->b_data);
+		brelse(bh);
+
+		gfs_rlist_add(sdp, &rlist, blk);
+	}
+
+	gfs_rlist_alloc(&rlist, LM_ST_EXCLUSIVE, 0);
+
+	for (x = 0; x < rlist.rl_rgrps; x++) {
+		struct gfs_rgrpd *rgd;
+		rgd = gl2rgd(rlist.rl_ghs[x].gh_gl);
+		rg_blocks += rgd->rd_ri.ri_length;
+	}
+
+	error = gfs_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs);
+	if (error)
+		goto out_rlist;
+
+	/* Trans may require:
+	   All the bitmaps that were reserved.
+	   One block for the dinode.
+	   All the hash blocks that will be changed.
+	   One block for a quota change. */
+
+	error = gfs_trans_begin(sdp,
+				rg_blocks + 1 + (DIV_RU(size, sdp->sd_jbsize) + 1),
+				1);
+	if (error)
+		goto out_rg_gunlock;
+
+	for (blk = leaf_no; blk; blk = tmp_leaf.lf_next) {
+		error = get_leaf(dip, blk, &bh);
+		if (error)
+			goto out_end_trans;
+		gfs_leaf_in(&tmp_leaf, bh->b_data);
+		brelse(bh);
+
+		gfs_metafree(dip, blk, 1);
+
+		if (!dip->i_di.di_blocks)
+			gfs_consist_inode(dip);
+		dip->i_di.di_blocks--;
+	}
+
+	error = gfs_writei(dip, ht, index * sizeof (uint64_t), size, 
+                           gfs_zero_blocks, NULL);
+
+	if (error != size) {
+		if (error >= 0)
+			error = -EIO;
+		goto out_end_trans;
+	}
+
+	error = gfs_get_inode_buffer(dip, &dibh);
+	if (error)
+		goto out_end_trans;
+
+	gfs_trans_add_bh(dip->i_gl, dibh);
+	gfs_dinode_out(&dip->i_di, dibh->b_data);
+	brelse(dibh);
+
+ out_end_trans:
+	gfs_trans_end(sdp);
+
+ out_rg_gunlock:
+	gfs_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs);
+
+ out_rlist:
+	gfs_rlist_free(&rlist);
+	gfs_glock_dq_uninit(&dip->i_alloc->al_ri_gh);
+
+ out_qs:
+	gfs_quota_unhold_m(dip);
+
+ out:
+	gfs_alloc_put(dip);
+
+	return error;
+}
+
+/**
+ * gfs_dir_exhash_free - free all the leaf blocks in a directory
+ * @dip: the directory
+ *
+ * Dealloc all on-disk directory leaves to FREEMETA state
+ * Change on-disk inode type to "regular file"
+ *
+ * Returns: errno
+ */
+
+int
+gfs_dir_exhash_free(struct gfs_inode *dip)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct buffer_head *bh;
+	int error;
+
+	/* Dealloc on-disk leaves to FREEMETA state */
+	error = foreach_leaf(dip, leaf_free, NULL);
+	if (error)
+		return error;
+
+	/*  Make this a regular file in case we crash.
+	   (We don't want to free these blocks a second time.)  */
+
+	error = gfs_trans_begin(sdp, 1, 0);
+	if (error)
+		return error;
+
+	error = gfs_get_inode_buffer(dip, &bh);
+	if (!error) {
+		gfs_trans_add_bh(dip->i_gl, bh);
+		((struct gfs_dinode *)bh->b_data)->di_type = cpu_to_gfs16(GFS_FILE_REG);
+		brelse(bh);
+	}
+
+	gfs_trans_end(sdp);
+
+	return error;
+}
+
+/**
+ * gfs_diradd_alloc_required - figure out if an entry addition is going to require an allocation
+ * @ip: the file being written to
+ * @filname: the filename that's going to be added
+ * @alloc_required: the int is set to TRUE if an alloc is required, FALSE otherwise
+ *
+ * Returns: errno
+ */
+
+int
+gfs_diradd_alloc_required(struct gfs_inode *dip, struct qstr *filename,
+			  int *alloc_required)
+{
+	struct buffer_head *bh = NULL, *bh_next;
+	uint32_t hsize, hash, index;
+	int error = 0;
+
+	*alloc_required = FALSE;
+
+	if (dip->i_di.di_flags & GFS_DIF_EXHASH) {
+		hsize = 1 << dip->i_di.di_depth;
+		if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
+			gfs_consist_inode(dip);
+			return -EIO;
+		}
+
+		hash = gfs_dir_hash(filename->name, filename->len);
+		index = hash >> (32 - dip->i_di.di_depth);
+
+		error = get_first_leaf(dip, index, &bh_next);
+		if (error)
+			return error;
+
+		do {
+			if (bh)
+				brelse(bh);
+
+			bh = bh_next;
+
+			if (dirent_fits(dip, bh, filename->len))
+				break;
+
+			error = get_next_leaf(dip, bh, &bh_next);
+			if (error == -ENOENT) {
+				*alloc_required = TRUE;
+				error = 0;
+				break;
+			}
+		}
+		while (!error);
+
+		brelse(bh);
+	} else {
+		error = gfs_get_inode_buffer(dip, &bh);
+		if (error)
+			return error;
+
+		if (!dirent_fits(dip, bh, filename->len))
+			*alloc_required = TRUE;
+
+		brelse(bh);
+	}
+
+	return error;
+}
+
+/**
+ * do_gdm - copy out one leaf (or list of leaves)
+ * @dip: the directory
+ * @index: the hash table offset in the directory
+ * @len: the number of pointers to this leaf
+ * @leaf_no: the leaf number
+ * @data: a pointer to a struct gfs_user_buffer structure
+ *
+ * Returns: errno
+ */
+
+static int
+do_gdm(struct gfs_inode *dip,
+       uint32_t index, uint32_t len, uint64_t leaf_no,
+       void *data)
+{
+	struct gfs_user_buffer *ub = (struct gfs_user_buffer *)data;
+	struct gfs_leaf leaf;
+	struct buffer_head *bh;
+	uint64_t blk;
+	int error = 0;
+
+	for (blk = leaf_no; blk; blk = leaf.lf_next) {
+		error = get_leaf(dip, blk, &bh);
+		if (error)
+			break;
+
+		gfs_leaf_in(&leaf, bh->b_data);
+
+		error = gfs_add_bh_to_ub(ub, bh);
+
+		brelse(bh);
+
+		if (error)
+			break;
+	}
+
+	return error;
+}
+
+/**
+ * gfs_get_dir_meta - return all the leaf blocks of a directory
+ * @dip: the directory
+ * @ub: the structure representing the meta
+ *
+ * Returns: errno
+ */
+
+int
+gfs_get_dir_meta(struct gfs_inode *dip, struct gfs_user_buffer *ub)
+{
+	return foreach_leaf(dip, do_gdm, ub);
+}
diff -pruN linux-2.6.9.orig/fs/gfs/dir.h linux-2.6.9.debug/fs/gfs/dir.h
--- linux-2.6.9.orig/fs/gfs/dir.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/dir.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,55 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __DIR_DOT_H__
+#define __DIR_DOT_H__
+
+/**
+ * gfs_filldir_t - Report a directory entry to the caller of gfs_dir_read()
+ * @opaque: opaque data used by the function
+ * @name: the name of the directory entry
+ * @length: the length of the name
+ * @offset: the entry's offset in the directory
+ * @inum: the inode number the entry points to
+ * @type: the type of inode the entry points to
+ *
+ * Returns: 0 on success, 1 if buffer full
+ */
+
+typedef int (*gfs_filldir_t) (void *opaque,
+			      const char *name, unsigned int length,
+			      uint64_t offset,
+			      struct gfs_inum *inum, unsigned int type);
+
+int gfs_filecmp(struct qstr *file1, char *file2, int len_of_file2);
+int gfs_dirent_alloc(struct gfs_inode *dip, struct buffer_head *bh,
+		     int name_len, struct gfs_dirent **dent_out);
+
+int gfs_dir_search(struct gfs_inode *dip, struct qstr *filename,
+		   struct gfs_inum *inum, unsigned int *type);
+int gfs_dir_add(struct gfs_inode *dip, struct qstr *filename,
+		struct gfs_inum *inum, unsigned int type);
+int gfs_dir_del(struct gfs_inode *dip, struct qstr *filename);
+int gfs_dir_read(struct gfs_inode *dip, uint64_t * offset, void *opaque,
+		 gfs_filldir_t filldir);
+int gfs_dir_mvino(struct gfs_inode *dip, struct qstr *filename,
+		  struct gfs_inum *new_inum, unsigned int new_type);
+
+int gfs_dir_exhash_free(struct gfs_inode *dip);
+
+int gfs_diradd_alloc_required(struct gfs_inode *dip, struct qstr *filename,
+			      int *alloc_required);
+
+int gfs_get_dir_meta(struct gfs_inode *ip, struct gfs_user_buffer *ub);
+
+#endif /* __DIR_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/eaops.c linux-2.6.9.debug/fs/gfs/eaops.c
--- linux-2.6.9.orig/fs/gfs/eaops.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/eaops.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,246 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/uaccess.h>
+#include <linux/xattr.h>
+#include <linux/xattr_acl.h>
+
+#include "gfs.h"
+#include "acl.h"
+#include "eaops.h"
+#include "eattr.h"
+
+/**
+ * gfs_ea_name2type - get the type of the ea, and trucate the type from the name
+ * @namep: ea name, possibly with type appended
+ *
+ * Returns: GFS_EATYPE_XXX
+ */
+
+unsigned int
+gfs_ea_name2type(const char *name, char **truncated_name)
+{
+	unsigned int type;
+
+	if (strncmp(name, "system.", 7) == 0) {
+		type = GFS_EATYPE_SYS;
+		if (truncated_name)
+			*truncated_name = strchr(name, '.') + 1;
+	} else if (strncmp(name, "user.", 5) == 0) {
+		type = GFS_EATYPE_USR;
+		if (truncated_name)
+			*truncated_name = strchr(name, '.') + 1;
+	} else {
+		type = GFS_EATYPE_UNUSED;
+		if (truncated_name)
+			*truncated_name = NULL;
+	}
+
+	return type;
+}
+
+/**
+ * user_eo_get -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+static int
+user_eo_get(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	{
+		struct inode *inode = ip->i_vnode;
+		int error = permission(inode, MAY_READ, NULL);
+		if (error)
+			return error;
+	}
+
+	return gfs_ea_get_i(ip, er);
+}
+
+/**
+ * user_eo_set -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+static int
+user_eo_set(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	{
+		struct inode *inode = ip->i_vnode;
+		if (S_ISREG(inode->i_mode) ||
+		    (S_ISDIR(inode->i_mode) && !(inode->i_mode & S_ISVTX))) {
+			int error = permission(inode, MAY_WRITE, NULL);
+			if (error)
+				return error;
+		} else
+			return -EPERM;
+	}
+
+	return gfs_ea_set_i(ip, er);
+}
+
+/**
+ * user_eo_remove -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+static int
+user_eo_remove(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	{
+		struct inode *inode = ip->i_vnode;
+		if (S_ISREG(inode->i_mode) ||
+		    (S_ISDIR(inode->i_mode) && !(inode->i_mode & S_ISVTX))) {
+			int error = permission(inode, MAY_WRITE, NULL);
+			if (error)
+				return error;
+		} else
+			return -EPERM;
+	}
+
+	return gfs_ea_remove_i(ip, er);
+}
+
+/**
+ * system_eo_get -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+static int
+system_eo_get(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	if (!GFS_ACL_IS_ACCESS(er->er_name, er->er_name_len) &&
+	    !GFS_ACL_IS_DEFAULT(er->er_name, er->er_name_len) &&
+	    !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (ip->i_sbd->sd_args.ar_posix_acls == FALSE &&
+	    (GFS_ACL_IS_ACCESS(er->er_name, er->er_name_len) ||
+	     GFS_ACL_IS_DEFAULT(er->er_name, er->er_name_len)))
+		return -EOPNOTSUPP;
+
+	return gfs_ea_get_i(ip, er);	
+}
+
+/**
+ * system_eo_set -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+static int
+system_eo_set(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	int remove = FALSE;
+	int error;
+
+	if (GFS_ACL_IS_ACCESS(er->er_name, er->er_name_len)) {
+		er->er_mode = ip->i_vnode->i_mode;
+		error = gfs_acl_validate_set(ip, TRUE, er,
+					     &remove, &er->er_mode);
+		if (error)
+			return error;
+		error = gfs_ea_set_i(ip, er);
+		if (error)
+			return error;
+		if (remove)
+			gfs_ea_remove_i(ip, er);
+		return 0;
+
+	} else if (GFS_ACL_IS_DEFAULT(er->er_name, er->er_name_len)) {
+		int error = gfs_acl_validate_set(ip, FALSE, er,
+						 &remove, NULL);
+		if (error)
+			return error;
+		if (!remove)
+			error = gfs_ea_set_i(ip, er);
+		else {
+			error = gfs_ea_remove_i(ip, er);
+			if (error == -ENODATA)
+				error = 0;
+		}
+		return error;
+	}
+
+	return -EPERM;
+}
+
+/**
+ * system_eo_remove -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+static int
+system_eo_remove(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	if (GFS_ACL_IS_ACCESS(er->er_name, er->er_name_len)) {
+		int error = gfs_acl_validate_remove(ip, TRUE);
+		if (error)
+			return error;
+
+	} else if (GFS_ACL_IS_DEFAULT(er->er_name, er->er_name_len)) {
+		int error = gfs_acl_validate_remove(ip, FALSE);
+		if (error)
+			return error;
+
+	} else
+	        return -EPERM;
+
+	return gfs_ea_remove_i(ip, er);	
+}
+
+struct gfs_eattr_operations gfs_user_eaops = {
+	.eo_get = user_eo_get,
+	.eo_set = user_eo_set,
+	.eo_remove = user_eo_remove,
+	.eo_name = "user",
+};
+
+struct gfs_eattr_operations gfs_system_eaops = {
+	.eo_get = system_eo_get,
+	.eo_set = system_eo_set,
+	.eo_remove = system_eo_remove,
+	.eo_name = "system",
+};
+
+struct gfs_eattr_operations *gfs_ea_ops[] = {
+	NULL,
+	&gfs_user_eaops,
+	&gfs_system_eaops,
+};
+
+
diff -pruN linux-2.6.9.orig/fs/gfs/eaops.h linux-2.6.9.debug/fs/gfs/eaops.h
--- linux-2.6.9.orig/fs/gfs/eaops.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/eaops.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,34 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __EAOPS_DOT_H__
+#define __EAOPS_DOT_H__
+
+struct gfs_ea_request;
+
+struct gfs_eattr_operations {
+	int (*eo_get) (struct gfs_inode *ip, struct gfs_ea_request *er);
+	int (*eo_set) (struct gfs_inode *ip, struct gfs_ea_request *er);
+	int (*eo_remove) (struct gfs_inode *ip, struct gfs_ea_request *er);
+	char *eo_name;
+};
+
+unsigned int gfs_ea_name2type(const char *name, char **truncated_name);
+
+extern struct gfs_eattr_operations gfs_user_eaops;
+extern struct gfs_eattr_operations gfs_system_eaops;
+
+extern struct gfs_eattr_operations *gfs_ea_ops[];
+
+#endif /* __EAOPS_DOT_H__ */
+
diff -pruN linux-2.6.9.orig/fs/gfs/eattr.c linux-2.6.9.debug/fs/gfs/eattr.c
--- linux-2.6.9.orig/fs/gfs/eattr.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/eattr.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,2007 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/uaccess.h>
+#include <linux/xattr.h>
+#include <linux/xattr_acl.h>
+
+#include "gfs.h"
+#include "acl.h"
+#include "dio.h"
+#include "eaops.h"
+#include "eattr.h"
+#include "glock.h"
+#include "inode.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "trans.h"
+
+/**
+ * ea_calc_size - returns the acutal number of bytes the request will take up
+ *                (not counting any unstuffed data blocks)
+ * @sdp:
+ * @er:
+ * @size:
+ *
+ * Returns: TRUE if the EA should be stuffed
+ */
+
+static int
+ea_calc_size(struct gfs_sbd *sdp,
+	    struct gfs_ea_request *er,
+	    unsigned int *size)
+{
+	*size = GFS_EAREQ_SIZE_STUFFED(er);
+	if (*size <= sdp->sd_jbsize)
+		return TRUE;
+
+	*size = GFS_EAREQ_SIZE_UNSTUFFED(sdp, er);
+	return FALSE;
+}
+
+/**
+ * gfs_ea_check_size - 
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_check_size(struct gfs_sbd *sdp, struct gfs_ea_request *er)
+{
+	unsigned int size;
+
+	if (er->er_data_len > GFS_EA_MAX_DATA_LEN)
+		return -ERANGE;
+
+	ea_calc_size(sdp, er, &size);
+	if (size > sdp->sd_jbsize)
+		return -ERANGE; /* This can only happen with 512 byte blocks */
+
+	return 0;
+}
+
+typedef int (*ea_call_t) (struct gfs_inode *ip,
+			  struct buffer_head *bh,
+			  struct gfs_ea_header *ea,
+			  struct gfs_ea_header *prev,
+			  void *private);
+
+/**
+ * ea_foreach_i -
+ * @ip:
+ * @bh:
+ * @eabc:
+ * @data:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_foreach_i(struct gfs_inode *ip,
+	     struct buffer_head *bh,
+	     ea_call_t ea_call, void *data)
+{
+	struct gfs_ea_header *ea, *prev = NULL;
+	int error = 0;
+
+	if (gfs_metatype_check(ip->i_sbd, bh, GFS_METATYPE_EA))
+		return -EIO;
+
+	for (ea = GFS_EA_BH2FIRST(bh);; prev = ea, ea = GFS_EA2NEXT(ea)) {
+		if (!GFS_EA_REC_LEN(ea))
+			goto fail;
+		if (!(bh->b_data <= (char *)ea &&
+		      (char *)GFS_EA2NEXT(ea) <=
+		      bh->b_data + bh->b_size))
+			goto fail;
+		if (!GFS_EATYPE_VALID(ea->ea_type))
+			goto fail;
+
+		error = ea_call(ip, bh, ea, prev, data);
+		if (error)
+			return error;
+
+		if (GFS_EA_IS_LAST(ea)) {
+			if ((char *)GFS_EA2NEXT(ea) !=
+			    bh->b_data + bh->b_size)
+				goto fail;
+			break;
+		}
+	}
+
+	return error;
+
+ fail:
+	gfs_consist_inode(ip);
+	return -EIO;
+}
+
+/**
+ * ea_foreach -
+ * @ip:
+ * @ea_call:
+ * @data:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_foreach(struct gfs_inode *ip,
+	   ea_call_t ea_call,
+	   void *data)
+{
+	struct buffer_head *bh;
+	int error;
+
+	error = gfs_dread(ip->i_gl, ip->i_di.di_eattr,
+			  DIO_START | DIO_WAIT, &bh);
+	if (error)
+		return error;
+
+	if (!(ip->i_di.di_flags & GFS_DIF_EA_INDIRECT))
+		error = ea_foreach_i(ip, bh, ea_call, data);
+	else {
+		struct buffer_head *eabh;
+		uint64_t *eablk, *end;
+
+		if (gfs_metatype_check(ip->i_sbd, bh, GFS_METATYPE_IN)) {
+			error = -EIO;
+			goto out;
+		}
+
+		eablk = (uint64_t *)(bh->b_data + sizeof(struct gfs_indirect));
+		end = eablk + ip->i_sbd->sd_inptrs;
+
+		for (; eablk < end; eablk++) {
+			uint64_t bn;
+
+			if (!*eablk)
+				break;
+			bn = gfs64_to_cpu(*eablk);
+
+			error = gfs_dread(ip->i_gl, bn,
+					  DIO_START | DIO_WAIT, &eabh);
+			if (error)
+				break;
+			error = ea_foreach_i(ip, eabh, ea_call, data);
+			brelse(eabh);
+			if (error)
+				break;
+		}
+	}
+
+ out:
+	brelse(bh);
+
+	return error;
+}
+
+struct ea_find {
+	struct gfs_ea_request *ef_er;
+	struct gfs_ea_location *ef_el;
+};
+
+/**
+ * ea_find_i -
+ * @ip:
+ * @bh:
+ * @ea:
+ * @prev:
+ * @private:
+ *
+ * Returns: -errno on error, 1 if search is over,
+ *          0 if search should continue
+ */
+
+static int
+ea_find_i(struct gfs_inode *ip,
+	  struct buffer_head *bh,
+	  struct gfs_ea_header *ea,
+	  struct gfs_ea_header *prev,
+	  void *private)
+{
+	struct ea_find *ef = (struct ea_find *)private;
+	struct gfs_ea_request *er = ef->ef_er;
+
+	if (ea->ea_type == GFS_EATYPE_UNUSED)
+		return 0;
+
+	if (ea->ea_type == er->er_type) {
+		if (ea->ea_name_len == er->er_name_len &&
+		    !memcmp(GFS_EA2NAME(ea), er->er_name, ea->ea_name_len)) {
+			struct gfs_ea_location *el = ef->ef_el;
+			get_bh(bh);
+			el->el_bh = bh;
+			el->el_ea = ea;
+			el->el_prev = prev;
+			return 1;
+		}
+	}
+
+#if 0
+	else if ((ip->i_di.di_flags & GFS_DIF_EA_PACKED) &&
+		 er->er_type == GFS_EATYPE_SYS)
+		return 1;
+#endif
+
+	return 0;
+}
+
+/**
+ * gfs_ea_find - find a matching eattr
+ * @ip:
+ * @er:
+ * @el:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_find(struct gfs_inode *ip,
+	    struct gfs_ea_request *er,
+	    struct gfs_ea_location *el)
+{
+	struct ea_find ef;
+	int error;
+
+	ef.ef_er = er;
+	ef.ef_el = el;
+
+	memset(el, 0, sizeof(struct gfs_ea_location));
+
+	error = ea_foreach(ip, ea_find_i, &ef);
+	if (error > 0)
+		return 0;
+
+	return error;
+}
+
+/**
+ * ea_dealloc_unstuffed -
+ * @ip:
+ * @bh:
+ * @ea:
+ * @prev:
+ * @private:
+ *
+ * Take advantage of the fact that all unstuffed blocks are
+ * allocated from the same RG.  But watch, this may not always
+ * be true.
+ *
+ * Returns: errno
+ */
+
+static int
+ea_dealloc_unstuffed(struct gfs_inode *ip,
+		     struct buffer_head *bh,
+		     struct gfs_ea_header *ea,
+		     struct gfs_ea_header *prev,
+		     void *private)
+{
+	int *leave = (int *)private;
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_rgrpd *rgd;
+	struct gfs_holder rg_gh;
+	struct buffer_head *dibh;
+	uint64_t *dataptrs, bn = 0;
+	uint64_t bstart = 0;
+	unsigned int blen = 0;
+	unsigned int x;
+	int error;
+
+	if (GFS_EA_IS_STUFFED(ea))
+		return 0;
+
+	dataptrs = GFS_EA2DATAPTRS(ea);
+	for (x = 0; x < ea->ea_num_ptrs; x++, dataptrs++)
+		if (*dataptrs) {
+			bn = gfs64_to_cpu(*dataptrs);
+			break;
+		}
+	if (!bn)
+		return 0;
+
+	rgd = gfs_blk2rgrpd(sdp, bn);
+	if (!rgd) {
+		gfs_consist_inode(ip);
+		return -EIO;
+	}
+
+	error = gfs_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &rg_gh);
+	if (error)
+		return error;
+
+	error = gfs_trans_begin(sdp, 2 + rgd->rd_ri.ri_length, 1);
+	if (error)
+		goto out_gunlock;
+
+	gfs_trans_add_bh(ip->i_gl, bh);
+
+	dataptrs = GFS_EA2DATAPTRS(ea);
+	for (x = 0; x < ea->ea_num_ptrs; x++, dataptrs++) {
+		if (!*dataptrs)
+			break;
+		bn = gfs64_to_cpu(*dataptrs);
+
+		if (bstart + blen == bn)
+			blen++;
+		else {
+			if (bstart)
+				gfs_metafree(ip, bstart, blen);
+			bstart = bn;
+			blen = 1;
+		}
+
+		*dataptrs = 0;
+		if (!ip->i_di.di_blocks)
+			gfs_consist_inode(ip);
+		ip->i_di.di_blocks--;
+	}
+	if (bstart)
+		gfs_metafree(ip, bstart, blen);
+
+	if (prev && !leave) {
+		uint32_t len;
+
+		len = GFS_EA_REC_LEN(prev) + GFS_EA_REC_LEN(ea);
+		prev->ea_rec_len = cpu_to_gfs32(len);
+
+		if (GFS_EA_IS_LAST(ea))
+			prev->ea_flags |= GFS_EAFLAG_LAST;
+	} else {
+		ea->ea_type = GFS_EATYPE_UNUSED;
+		ea->ea_num_ptrs = 0;
+	}
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (!error) {
+		ip->i_di.di_ctime = get_seconds();
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	gfs_trans_end(sdp);
+
+ out_gunlock:
+	gfs_glock_dq_uninit(&rg_gh);
+
+	return error;
+}
+
+/**
+ * ea_remove_unstuffed -
+ * @ip:
+ * @bh:
+ * @ea:
+ * @prev:
+ * @leave:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_remove_unstuffed(struct gfs_inode *ip,
+		    struct buffer_head *bh,
+		    struct gfs_ea_header *ea,
+		    struct gfs_ea_header *prev,
+		    int leave)
+{
+	struct gfs_alloc *al;
+	int error;
+
+	al = gfs_alloc_get(ip);
+
+	error = gfs_quota_hold_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto out_alloc;
+
+	error = gfs_rindex_hold(ip->i_sbd, &al->al_ri_gh);
+	if (error)
+		goto out_quota;
+
+	error = ea_dealloc_unstuffed(ip,
+				     bh, ea, prev,
+				     (leave) ? &error : NULL);
+
+	gfs_glock_dq_uninit(&al->al_ri_gh);
+
+ out_quota:
+	gfs_quota_unhold_m(ip);
+
+ out_alloc:
+	gfs_alloc_put(ip);
+
+	return error;
+}
+
+/**************************************************************************************************/
+
+/**
+ * gfs_ea_repack_i -
+ * @ip:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_repack_i(struct gfs_inode *ip)
+{
+	return -ENOSYS;
+}
+
+/**
+ * gfs_ea_repack -
+ * @ip:
+ *
+ * Returns: errno
+ */
+
+int gfs_ea_repack(struct gfs_inode *ip)
+{
+	struct gfs_holder gh;
+	int error;
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+	if (error)
+		return error;
+
+	/* Some sort of permissions checking would be nice */
+
+	error = gfs_ea_repack_i(ip);
+
+	gfs_glock_dq_uninit(&gh);
+
+	return error;
+}
+
+struct ea_list {
+	struct gfs_ea_request *ei_er;
+	unsigned int ei_size;
+};
+
+/**
+ * ea_list_i -
+ * @ip:
+ * @bh:
+ * @ea:
+ * @prev:
+ * @private:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_list_i(struct gfs_inode *ip,
+	  struct buffer_head *bh,
+	  struct gfs_ea_header *ea,
+	  struct gfs_ea_header *prev,
+	  void *private)
+{
+	struct ea_list *ei = (struct ea_list *)private;
+	struct gfs_ea_request *er = ei->ei_er;
+	unsigned int ea_size = GFS_EA_STRLEN(ea);
+
+	if (ea->ea_type == GFS_EATYPE_UNUSED)
+		return 0;
+
+	if (er->er_data_len) {
+		char *prefix;
+		unsigned int l;
+		char c = 0;
+
+		if (ei->ei_size + ea_size > er->er_data_len)
+			return -ERANGE;
+
+		if (ea->ea_type == GFS_EATYPE_USR) {
+			prefix = "user.";
+			l = 5;
+		} else {
+			prefix = "system.";
+			l = 7;
+		}
+
+		memcpy(er->er_data + ei->ei_size,
+		       prefix, l);
+		memcpy(er->er_data + ei->ei_size + l,
+		       GFS_EA2NAME(ea),
+		       ea->ea_name_len);
+		memcpy(er->er_data + ei->ei_size +
+		       ea_size - 1,
+		       &c, 1);
+	}
+
+	ei->ei_size += ea_size;
+
+	return 0;
+}
+
+/**
+ * gfs_ea_list -
+ * @ip:
+ * @er:
+ *
+ * Returns: actual size of data on success, -errno on error
+ */
+
+int
+gfs_ea_list(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	struct gfs_holder i_gh;
+	int error;
+
+	if (!er->er_data || !er->er_data_len) {
+		er->er_data = NULL;
+		er->er_data_len = 0;
+	}
+
+	error = gfs_glock_nq_init(ip->i_gl,
+				  LM_ST_SHARED, LM_FLAG_ANY,
+				  &i_gh);
+	if (error)
+		return error;
+
+	if (ip->i_di.di_eattr) {
+		struct ea_list ei = { .ei_er = er, .ei_size = 0 };
+
+		error = ea_foreach(ip, ea_list_i, &ei);
+		if (!error)
+			error = ei.ei_size;
+	}
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * ea_get_unstuffed - actually copies the unstuffed data into the
+ *                    request buffer
+ * @ip:
+ * @ea:
+ * @data:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_get_unstuffed(struct gfs_inode *ip, struct gfs_ea_header *ea,
+		 char *data)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head **bh;
+	unsigned int amount = GFS_EA_DATA_LEN(ea);
+	unsigned int nptrs = DIV_RU(amount, sdp->sd_jbsize);
+	uint64_t *dataptrs = GFS_EA2DATAPTRS(ea);
+	unsigned int x;
+	int error = 0;
+
+	bh = kmalloc(nptrs * sizeof(struct buffer_head *), GFP_KERNEL);
+	if (!bh)
+		return -ENOMEM;
+
+	for (x = 0; x < nptrs; x++) {
+		error = gfs_dread(ip->i_gl, gfs64_to_cpu(*dataptrs),
+				  DIO_START, bh + x);
+		if (error) {
+			while (x--)
+				brelse(bh[x]);
+			goto out;
+		}
+		dataptrs++;
+	}
+
+	for (x = 0; x < nptrs; x++) {
+		error = gfs_dreread(sdp, bh[x], DIO_WAIT);
+		if (error) {
+			for (; x < nptrs; x++)
+				brelse(bh[x]);
+			goto out;
+		}
+		if (gfs_metatype_check2(sdp, bh[x],
+					GFS_METATYPE_ED, GFS_METATYPE_EA)) {
+			for (; x < nptrs; x++)
+				brelse(bh[x]);
+			error = -EIO;
+			goto out;
+		}
+
+		memcpy(data,
+		       bh[x]->b_data + sizeof(struct gfs_meta_header),
+		       (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize);
+
+		amount -= sdp->sd_jbsize;
+		data += sdp->sd_jbsize;
+
+		brelse(bh[x]);
+	}
+
+ out:
+	kfree(bh);
+
+	return error;
+}
+
+/**
+ * gfs_ea_get_copy -
+ * @ip:
+ * @el:
+ * @data:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_get_copy(struct gfs_inode *ip,
+		struct gfs_ea_location *el,
+		char *data)
+{
+	if (GFS_EA_IS_STUFFED(el->el_ea)) {
+		memcpy(data,
+		       GFS_EA2DATA(el->el_ea),
+		       GFS_EA_DATA_LEN(el->el_ea));
+		return 0;
+	} else
+		return ea_get_unstuffed(ip, el->el_ea,
+					data);
+}
+
+/**
+ * gfs_ea_get_i -
+ * @ip:
+ * @er:
+ *
+ * Returns: actual size of data on success, -errno on error
+ */
+
+int
+gfs_ea_get_i(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	struct gfs_ea_location el;
+	int error;
+
+	if (!ip->i_di.di_eattr)
+		return -ENODATA;
+
+	error = gfs_ea_find(ip, er, &el);
+	if (error)
+		return error;
+	if (!el.el_ea)
+		return -ENODATA;
+
+	if (er->er_data_len) {
+		if (GFS_EA_DATA_LEN(el.el_ea) > er->er_data_len)
+			error =  -ERANGE;
+		else
+			error = gfs_ea_get_copy(ip, &el, er->er_data);
+	}
+	if (!error)
+		error = GFS_EA_DATA_LEN(el.el_ea);
+
+	brelse(el.el_bh);
+
+	return error;
+}
+
+/**
+ * gfs_ea_get -
+ * @ip:
+ * @er:
+ *
+ * Returns: actual size of data on success, -errno on error
+ */
+
+int
+gfs_ea_get(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	struct gfs_holder i_gh;
+	int error;
+
+	if (!er->er_name_len ||
+	    er->er_name_len > GFS_EA_MAX_NAME_LEN)
+		return -EINVAL;
+	if (!er->er_data || !er->er_data_len) {
+		er->er_data = NULL;
+		er->er_data_len = 0;
+	}
+
+	error = gfs_glock_nq_init(ip->i_gl,
+				  LM_ST_SHARED, LM_FLAG_ANY,
+				  &i_gh);
+	if (error)
+		return error;
+
+	error = gfs_ea_ops[er->er_type]->eo_get(ip, er);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * ea_alloc_blk - allocates a new block for extended attributes.
+ * @ip: A pointer to the inode that's getting extended attributes
+ * @bhp:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_alloc_blk(struct gfs_inode *ip,
+	     struct buffer_head **bhp)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_ea_header *ea;
+	uint64_t block;
+	int error;
+
+	error = gfs_metaalloc(ip, &block);
+	if (error)
+		return error;
+
+	error = gfs_dread(ip->i_gl, block,
+			  DIO_NEW | DIO_START | DIO_WAIT, bhp);
+	if (error)
+		return error;
+
+	gfs_trans_add_bh(ip->i_gl, *bhp);
+	gfs_metatype_set(*bhp, GFS_METATYPE_EA, GFS_FORMAT_EA);
+
+	ea = GFS_EA_BH2FIRST(*bhp);
+	ea->ea_rec_len = cpu_to_gfs32(sdp->sd_jbsize);
+	ea->ea_type = GFS_EATYPE_UNUSED;
+	ea->ea_flags = GFS_EAFLAG_LAST;
+	ea->ea_num_ptrs = 0;
+
+	ip->i_di.di_blocks++;
+
+	return 0;
+}
+
+/**
+ * ea_write - writes the request info to an ea, creating new blocks if
+ *            necessary
+ * @ip:  inode that is being modified
+ * @ea:  the location of the new ea in a block
+ * @er: the write request
+ *
+ * Note: does not update ea_rec_len or the GFS_EAFLAG_LAST bin of ea_flags
+ *
+ * returns : errno
+ */
+
+static int
+ea_write(struct gfs_inode *ip,
+	 struct gfs_ea_header *ea,
+	 struct gfs_ea_request *er)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+
+	ea->ea_data_len = cpu_to_gfs32(er->er_data_len);
+	ea->ea_name_len = er->er_name_len;
+	ea->ea_type = er->er_type;
+	ea->ea_pad = 0;
+
+	memcpy(GFS_EA2NAME(ea), er->er_name, er->er_name_len);
+
+	if (GFS_EAREQ_SIZE_STUFFED(er) <= sdp->sd_jbsize) {
+		ea->ea_num_ptrs = 0;
+		memcpy(GFS_EA2DATA(ea), er->er_data, er->er_data_len);
+	} else {
+		uint64_t *dataptr = GFS_EA2DATAPTRS(ea);
+		const char *data = er->er_data;
+		unsigned int data_len = er->er_data_len;
+		unsigned int copy;
+		unsigned int x;
+
+		ea->ea_num_ptrs = DIV_RU(er->er_data_len, sdp->sd_jbsize);
+		for (x = 0; x < ea->ea_num_ptrs; x++) {
+			struct buffer_head *bh;
+			uint64_t block;
+			int error;
+
+			error = gfs_metaalloc(ip, &block);
+			if (error)
+				return error;
+
+			error = gfs_dread(ip->i_gl, block,
+					  DIO_NEW | DIO_START | DIO_WAIT, &bh);
+			if (error)
+				return error;
+
+			gfs_trans_add_bh(ip->i_gl, bh);
+			gfs_metatype_set(bh, GFS_METATYPE_ED, GFS_FORMAT_ED);
+			ip->i_di.di_blocks++;
+
+			copy = (data_len > sdp->sd_jbsize) ? sdp->sd_jbsize : data_len;
+			memcpy(bh->b_data + sizeof(struct gfs_meta_header),
+			       data,
+			       copy);
+
+			*dataptr++ = cpu_to_gfs64((uint64_t)bh->b_blocknr);
+			data += copy;
+			data_len -= copy;
+
+			brelse(bh);
+		}
+
+		gfs_assert_withdraw(sdp, !data_len);
+	}
+
+	return 0;
+}
+
+typedef int (*ea_skeleton_call_t) (struct gfs_inode *ip,
+				   struct gfs_ea_request *er,
+				   void *private);
+/**
+ * ea_alloc_skeleton -
+ * @ip:
+ * @er:
+ * @blks:
+ * @skeleton_call:
+ * @private:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_alloc_skeleton(struct gfs_inode *ip, struct gfs_ea_request *er,
+		  unsigned int blks,
+		  ea_skeleton_call_t skeleton_call, void *private)
+{
+	struct gfs_alloc *al;
+	struct buffer_head *dibh;
+	int error;
+
+	al = gfs_alloc_get(ip);
+
+	error = gfs_quota_lock_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto out;
+
+	error = gfs_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid);
+	if (error)
+		goto out_gunlock_q;
+
+	al->al_requested_meta = blks;
+
+	error = gfs_inplace_reserve(ip);
+	if (error)
+		goto out_gunlock_q;
+
+	/* Trans may require:
+	   A modified dinode, multiple EA metadata blocks, and all blocks for a RG
+	   bitmap */
+
+	error = gfs_trans_begin(ip->i_sbd,
+				1 + blks + al->al_rgd->rd_ri.ri_length, 1);
+	if (error)
+		goto out_ipres;
+
+	error = skeleton_call(ip, er, private);
+	if (error)
+		goto out_end_trans;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (!error) {
+		if (er->er_mode) {
+			ip->i_vnode->i_mode = er->er_mode;
+			gfs_inode_attr_out(ip);
+		}
+		ip->i_di.di_ctime = get_seconds();
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+ out_end_trans:
+	gfs_trans_end(ip->i_sbd);
+
+ out_ipres:
+	gfs_inplace_release(ip);
+
+ out_gunlock_q:
+	gfs_quota_unlock_m(ip);
+
+ out:
+	gfs_alloc_put(ip);
+
+	return error;
+}
+
+/**
+ * ea_init_i - initializes a new eattr block
+ * @ip:
+ * @er:
+ * @private:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_init_i(struct gfs_inode *ip,
+	  struct gfs_ea_request *er,
+	  void *private)
+{
+	struct buffer_head *bh;
+	int error;
+
+	error = ea_alloc_blk(ip, &bh);
+	if (error)
+		return error;
+
+	ip->i_di.di_eattr = bh->b_blocknr;
+	error = ea_write(ip, GFS_EA_BH2FIRST(bh), er);
+
+	brelse(bh);
+
+	return error;
+}
+
+/**
+ * ea_init - initializes a new eattr block
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_init(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	unsigned int jbsize = ip->i_sbd->sd_jbsize;
+	unsigned int blks = 1;
+
+	if (GFS_EAREQ_SIZE_STUFFED(er) > jbsize)
+		blks += DIV_RU(er->er_data_len, jbsize);
+
+	return ea_alloc_skeleton(ip, er,
+				 blks,
+				 ea_init_i, NULL);
+}
+
+/**
+ * ea_split_ea -
+ * @ea:
+ *
+ * Returns: the new ea
+ */
+
+static struct gfs_ea_header *
+ea_split_ea(struct gfs_ea_header *ea)
+{
+	uint32_t ea_size = GFS_EA_SIZE(ea);
+	struct gfs_ea_header *new = (struct gfs_ea_header *)((char *)ea + ea_size);
+	uint32_t new_size = GFS_EA_REC_LEN(ea) - ea_size;
+	int last = ea->ea_flags & GFS_EAFLAG_LAST;
+
+	ea->ea_rec_len = cpu_to_gfs32(ea_size);
+	ea->ea_flags ^= last;
+
+	new->ea_rec_len = cpu_to_gfs32(new_size);
+	new->ea_flags = last;
+
+	return new;
+}
+
+/**
+ * ea_set_remove_stuffed -
+ * @ip:
+ * @ea:
+ *
+ */
+
+static void
+ea_set_remove_stuffed(struct gfs_inode *ip, struct gfs_ea_location *el)
+{
+	struct gfs_ea_header *ea = el->el_ea;
+	struct gfs_ea_header *prev = el->el_prev;
+	uint32_t len;
+
+	gfs_trans_add_bh(ip->i_gl, el->el_bh);
+
+	if (!prev || !GFS_EA_IS_STUFFED(ea)) {
+		ea->ea_type = GFS_EATYPE_UNUSED;
+		return;
+	} else if (GFS_EA2NEXT(prev) != ea) {
+		prev = GFS_EA2NEXT(prev);
+		gfs_assert_withdraw(ip->i_sbd, GFS_EA2NEXT(prev) == ea);
+	}
+
+	len = GFS_EA_REC_LEN(prev) + GFS_EA_REC_LEN(ea);
+	prev->ea_rec_len = cpu_to_gfs32(len);
+
+	if (GFS_EA_IS_LAST(ea))
+		prev->ea_flags |= GFS_EAFLAG_LAST;
+}
+
+struct ea_set {
+	int ea_split;
+
+	struct gfs_ea_request *es_er;
+	struct gfs_ea_location *es_el;
+
+	struct buffer_head *es_bh;
+	struct gfs_ea_header *es_ea;
+};
+
+/**
+ * ea_set_simple_noalloc -
+ * @ip:
+ * @ea:
+ * @es:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_set_simple_noalloc(struct gfs_inode *ip,
+		      struct buffer_head *bh,
+		      struct gfs_ea_header *ea,
+		      struct ea_set *es)
+{
+	struct gfs_ea_request *er = es->es_er;
+	int error;
+
+	error = gfs_trans_begin(ip->i_sbd, 3, 0);
+	if (error)
+		return error;
+
+	gfs_trans_add_bh(ip->i_gl, bh);
+
+	if (es->ea_split)
+		ea = ea_split_ea(ea);
+
+	ea_write(ip, ea, er);
+
+	if (es->es_el)
+		ea_set_remove_stuffed(ip, es->es_el);
+
+	{
+		struct buffer_head *dibh;
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (!error) {
+			if (er->er_mode) {
+				ip->i_vnode->i_mode = er->er_mode;
+				gfs_inode_attr_out(ip);
+			}
+			ip->i_di.di_ctime = get_seconds();
+			gfs_trans_add_bh(ip->i_gl, dibh);
+			gfs_dinode_out(&ip->i_di, dibh->b_data);
+			brelse(dibh);
+		}	
+	}
+
+	gfs_trans_end(ip->i_sbd);
+
+	return error;
+}
+
+/**
+ * ea_set_simple_alloc -
+ * @ip:
+ * @er:
+ * @private:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_set_simple_alloc(struct gfs_inode *ip,
+		    struct gfs_ea_request *er,
+		    void *private)
+{
+	struct ea_set *es = (struct ea_set *)private;
+	struct gfs_ea_header *ea = es->es_ea;
+	int error;
+
+	gfs_trans_add_bh(ip->i_gl, es->es_bh);
+
+	if (es->ea_split)
+		ea = ea_split_ea(ea);
+
+	error =  ea_write(ip, ea, er);
+	if (error)
+		return error;
+
+	if (es->es_el)
+		ea_set_remove_stuffed(ip, es->es_el);
+
+	return 0;
+}
+
+/**
+ * ea_set_simple -
+ * @ip:
+ * @el:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_set_simple(struct gfs_inode *ip,
+	      struct buffer_head *bh,
+	      struct gfs_ea_header *ea,
+	      struct gfs_ea_header *prev,
+	      void *private)
+{
+	struct ea_set *es = (struct ea_set *)private;
+	unsigned int size;
+	int stuffed;
+	int error;
+
+	stuffed = ea_calc_size(ip->i_sbd, es->es_er, &size);
+
+	if (ea->ea_type == GFS_EATYPE_UNUSED) {
+		if (GFS_EA_REC_LEN(ea) < size)
+			return 0;
+		if (!GFS_EA_IS_STUFFED(ea)) {
+			error = ea_remove_unstuffed(ip, bh, ea, prev, TRUE);
+			if (error)
+				return error;
+		}
+		es->ea_split = FALSE;
+	} else if (GFS_EA_REC_LEN(ea) - GFS_EA_SIZE(ea) >= size)
+		es->ea_split = TRUE;
+	else
+		return 0;
+
+	if (stuffed) {
+		error = ea_set_simple_noalloc(ip, bh, ea, es);
+		if (error)
+			return error;
+	} else {
+		unsigned int blks;
+
+		es->es_bh = bh;
+		es->es_ea = ea;
+		blks = 2 + DIV_RU(es->es_er->er_data_len,
+				  ip->i_sbd->sd_jbsize);
+
+		error = ea_alloc_skeleton(ip, es->es_er,
+					  blks,
+					  ea_set_simple_alloc, es);
+		if (error)
+			return error;
+	}
+
+	return 1;
+}
+
+/**
+ * ea_set_block -
+ * @ip:
+ * @er:
+ * @private:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_set_block(struct gfs_inode *ip,
+	     struct gfs_ea_request *er,
+	     void *private)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *indbh, *newbh;
+	uint64_t *eablk;
+	int error;
+
+	if (ip->i_di.di_flags & GFS_DIF_EA_INDIRECT) {
+		uint64_t *end;
+
+		error = gfs_dread(ip->i_gl, ip->i_di.di_eattr,
+				  DIO_START | DIO_WAIT, &indbh);
+		if (error)
+			return error;
+
+		if (gfs_metatype_check(sdp, indbh, GFS_METATYPE_IN)) {
+			error = -EIO;
+			goto out;
+		}
+
+		eablk = (uint64_t *)(indbh->b_data + sizeof(struct gfs_indirect));
+		end = eablk + sdp->sd_inptrs;
+
+		for (; eablk < end; eablk++)
+			if (!*eablk)
+				break;
+
+		if (eablk == end) {
+			error = -ENOSPC;
+			goto out;
+		}
+
+		gfs_trans_add_bh(ip->i_gl, indbh);
+	} else {
+		uint64_t blk;
+
+		error = gfs_metaalloc(ip, &blk);
+		if (error)
+			return error;
+
+		error = gfs_dread(ip->i_gl, blk,
+				  DIO_NEW | DIO_START | DIO_WAIT, &indbh);
+		if (error)
+			return error;
+
+		gfs_trans_add_bh(ip->i_gl, indbh);
+		gfs_metatype_set(indbh, GFS_METATYPE_IN, GFS_FORMAT_IN);
+		gfs_buffer_clear_tail(indbh, sizeof(struct gfs_meta_header));
+
+		eablk = (uint64_t *)(indbh->b_data + sizeof(struct gfs_indirect));
+		*eablk = cpu_to_gfs64(ip->i_di.di_eattr);
+		ip->i_di.di_eattr = blk;
+		ip->i_di.di_flags |= GFS_DIF_EA_INDIRECT;
+		ip->i_di.di_blocks++;
+
+		eablk++;
+	}
+
+	error = ea_alloc_blk(ip, &newbh);
+	if (error)
+		goto out;
+
+	*eablk = cpu_to_gfs64((uint64_t)newbh->b_blocknr);
+	error = ea_write(ip, GFS_EA_BH2FIRST(newbh), er);
+	brelse(newbh);
+	if (error)
+		goto out;
+
+	if (private)
+		ea_set_remove_stuffed(ip, (struct gfs_ea_location *)private);
+
+ out:
+	brelse(indbh);
+
+	return error;
+}
+
+/**
+ * ea_set_i -
+ * @ip:
+ * @el:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_set_i(struct gfs_inode *ip,
+	 struct gfs_ea_request *er,
+	 struct gfs_ea_location *el)
+{
+	{
+		struct ea_set es;
+		int error;
+
+		memset(&es, 0, sizeof(struct ea_set));
+		es.es_er = er;
+		es.es_el = el;
+
+		error = ea_foreach(ip, ea_set_simple, &es);
+		if (error > 0)
+			return 0;
+		if (error)
+			return error;
+	}
+	{
+		unsigned int blks = 2;
+		if (!(ip->i_di.di_flags & GFS_DIF_EA_INDIRECT))
+			blks++;
+		if (GFS_EAREQ_SIZE_STUFFED(er) > ip->i_sbd->sd_jbsize)
+			blks += DIV_RU(er->er_data_len,
+				       ip->i_sbd->sd_jbsize);
+
+		return ea_alloc_skeleton(ip, er, blks, ea_set_block, el);
+	}
+}
+
+/**
+ * ea_set_remove_unstuffed -
+ * @ip:
+ * @el:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_set_remove_unstuffed(struct gfs_inode *ip, struct gfs_ea_location *el)
+{
+	if (el->el_prev && GFS_EA2NEXT(el->el_prev) != el->el_ea) {
+		el->el_prev = GFS_EA2NEXT(el->el_prev);
+		gfs_assert_withdraw(ip->i_sbd,
+				    GFS_EA2NEXT(el->el_prev) == el->el_ea);
+	}
+
+	return ea_remove_unstuffed(ip, el->el_bh, el->el_ea, el->el_prev, FALSE);
+}
+
+/**
+ * gfs_ea_set_i -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_set_i(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	struct gfs_ea_location el;
+	int error;
+
+	if (!ip->i_di.di_eattr) {
+		if (er->er_flags & XATTR_REPLACE)
+			return -ENODATA;
+		return ea_init(ip, er);
+	}
+
+	error = gfs_ea_find(ip, er, &el);
+	if (error)
+		return error;
+
+	if (el.el_ea) {
+		if (IS_APPEND(ip->i_vnode)) {
+			brelse(el.el_bh);
+			return -EPERM;
+		}
+
+		error = -EEXIST;
+		if (!(er->er_flags & XATTR_CREATE)) {
+			int unstuffed = !GFS_EA_IS_STUFFED(el.el_ea);
+			error = ea_set_i(ip, er, &el);
+			if (!error && unstuffed)
+				ea_set_remove_unstuffed(ip, &el);
+		}
+
+		brelse(el.el_bh);
+	} else {
+		error = -ENODATA;
+		if (!(er->er_flags & XATTR_REPLACE))
+			error = ea_set_i(ip, er, NULL);
+	}
+
+	return error;
+}
+
+/**
+ * gfs_ea_set -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_set(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	struct gfs_holder i_gh;
+	int error;
+
+	if (!er->er_name_len ||
+	    er->er_name_len > GFS_EA_MAX_NAME_LEN)
+		return -EINVAL;
+	if (!er->er_data || !er->er_data_len) {
+		er->er_data = NULL;
+		er->er_data_len = 0;
+	}
+	error = gfs_ea_check_size(ip->i_sbd, er);
+	if (error)
+		return error;
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	if (IS_IMMUTABLE(ip->i_vnode))
+		error = -EPERM;
+	else
+		error = gfs_ea_ops[er->er_type]->eo_set(ip, er);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * ea_remove_stuffed -
+ * @ip:
+ * @el:
+ * @mode:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_remove_stuffed(struct gfs_inode *ip,
+		  struct gfs_ea_location *el)
+{
+	struct gfs_ea_header *ea = el->el_ea;
+	struct gfs_ea_header *prev = el->el_prev;
+	int error;
+
+	error = gfs_trans_begin(ip->i_sbd, 2, 0);
+	if (error)
+		return error;
+
+	gfs_trans_add_bh(ip->i_gl, el->el_bh);
+
+	if (prev) {
+		uint32_t len;
+
+		len = GFS_EA_REC_LEN(prev) + GFS_EA_REC_LEN(ea);
+		prev->ea_rec_len = cpu_to_gfs32(len);
+
+		if (GFS_EA_IS_LAST(ea))
+			prev->ea_flags |= GFS_EAFLAG_LAST;
+	} else
+		ea->ea_type = GFS_EATYPE_UNUSED;
+
+	{
+		struct buffer_head *dibh;
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (!error) {
+			ip->i_di.di_ctime = get_seconds();
+			gfs_trans_add_bh(ip->i_gl, dibh);
+			gfs_dinode_out(&ip->i_di, dibh->b_data);
+			brelse(dibh);
+		}	
+	}
+
+	gfs_trans_end(ip->i_sbd);
+
+	return error;
+}
+
+/**
+ * gfs_ea_remove_i -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_remove_i(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	struct gfs_ea_location el;
+	int error;
+
+	if (!ip->i_di.di_eattr)
+		return -ENODATA;
+
+	error = gfs_ea_find(ip, er, &el);
+	if (error)
+		return error;
+	if (!el.el_ea)
+		return -ENODATA;
+
+	if (GFS_EA_IS_STUFFED(el.el_ea))
+		error = ea_remove_stuffed(ip, &el);
+	else
+		error = ea_remove_unstuffed(ip, el.el_bh, el.el_ea, el.el_prev, FALSE);
+
+	brelse(el.el_bh);
+
+	return error;
+}
+
+/**
+ * gfs_ea_remove - sets (or creates or replaces) an extended attribute
+ * @ip: pointer to the inode of the target file
+ * @er: request information
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_remove(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	struct gfs_holder i_gh;
+	int error;
+
+	if (!er->er_name_len ||
+	    er->er_name_len > GFS_EA_MAX_NAME_LEN)
+		return -EINVAL;
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	if (IS_IMMUTABLE(ip->i_vnode) || IS_APPEND(ip->i_vnode))
+		error = -EPERM;
+	else
+		error = gfs_ea_ops[er->er_type]->eo_remove(ip, er);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_ea_acl_init -
+ * @ip:
+ * @er:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_acl_init(struct gfs_inode *ip, struct gfs_ea_request *er)
+{
+	int error;
+
+	if (!ip->i_di.di_eattr)
+		return ea_init_i(ip, er, NULL);
+
+	{
+		struct buffer_head *bh;
+		struct gfs_ea_header *ea;
+		unsigned int size;
+
+		ea_calc_size(ip->i_sbd, er, &size);
+
+		error = gfs_dread(ip->i_gl, ip->i_di.di_eattr,
+				  DIO_START | DIO_WAIT, &bh);
+		if (error)
+			return error;
+
+		if (gfs_metatype_check(ip->i_sbd, bh, GFS_METATYPE_EA)) {
+			brelse(bh);
+			return -EIO;
+		}
+
+		ea = GFS_EA_BH2FIRST(bh);
+		if (GFS_EA_REC_LEN(ea) - GFS_EA_SIZE(ea) >= size) {
+			ea = ea_split_ea(ea);
+			ea_write(ip, ea, er);
+			brelse(bh);
+			return 0;
+		}
+
+		brelse(bh);
+	}
+
+	error = ea_set_block(ip, er, NULL);
+	gfs_assert_withdraw(ip->i_sbd, error != -ENOSPC);
+	if (error)
+		return error;
+
+	{
+		struct buffer_head *dibh;
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (error)
+			return error;
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	return error;
+}
+
+/**
+ * ea_acl_chmod_unstuffed -
+ * @ip:
+ * @ea:
+ * @data:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_acl_chmod_unstuffed(struct gfs_inode *ip,
+		       struct gfs_ea_header *ea,
+		       char *data)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head **bh;
+	unsigned int amount = GFS_EA_DATA_LEN(ea);
+	unsigned int nptrs = DIV_RU(amount, sdp->sd_jbsize);
+	uint64_t *dataptrs = GFS_EA2DATAPTRS(ea);
+	unsigned int x;
+	int error;
+
+	bh = kmalloc(nptrs * sizeof(struct buffer_head *), GFP_KERNEL);
+	if (!bh)
+		return -ENOMEM;
+
+	error = gfs_trans_begin(sdp, 1 + nptrs, 0);
+	if (error)
+		goto out;
+
+	for (x = 0; x < nptrs; x++) {
+		error = gfs_dread(ip->i_gl, gfs64_to_cpu(*dataptrs),
+				  DIO_START, bh + x);
+		if (error) {
+			while (x--)
+				brelse(bh[x]);
+			goto fail;
+		}
+		dataptrs++;
+	}
+
+	for (x = 0; x < nptrs; x++) {
+		error = gfs_dreread(sdp, bh[x], DIO_WAIT);
+		if (error) {
+			for (; x < nptrs; x++)
+				brelse(bh[x]);
+			goto fail;
+		}
+		if (gfs_metatype_check2(sdp, bh[x],
+					GFS_METATYPE_ED, GFS_METATYPE_EA)) {
+			for (; x < nptrs; x++)
+				brelse(bh[x]);
+			error = -EIO;
+			goto fail;
+		}
+
+		gfs_trans_add_bh(ip->i_gl, bh[x]);
+
+		memcpy(bh[x]->b_data + sizeof(struct gfs_meta_header),
+		       data,
+		       (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize);
+
+		amount -= sdp->sd_jbsize;
+		data += sdp->sd_jbsize;
+
+		brelse(bh[x]);
+	}
+
+ out:
+	kfree(bh);
+
+	return error;
+
+ fail:
+	gfs_trans_end(sdp);
+	kfree(bh);
+
+	return error;
+}
+
+/**
+ * gfs_ea_acl_chmod -
+ * @ip:
+ * @el:
+ * @attr:
+ * @data:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_acl_chmod(struct gfs_inode *ip, struct gfs_ea_location *el,
+		 struct iattr *attr, char *data)
+{
+	struct buffer_head *dibh;
+	int error;
+
+	if (GFS_EA_IS_STUFFED(el->el_ea)) {
+		error = gfs_trans_begin(ip->i_sbd, 2, 0);
+		if (error)
+			return error;
+
+		gfs_trans_add_bh(ip->i_gl, el->el_bh);
+		memcpy(GFS_EA2DATA(el->el_ea),
+		       data,
+		       GFS_EA_DATA_LEN(el->el_ea));
+	} else
+		error = ea_acl_chmod_unstuffed(ip, el->el_ea, data);
+
+	if (error)
+		return error;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (!error) {
+		inode_setattr(ip->i_vnode, attr);
+		gfs_inode_attr_out(ip);
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	gfs_trans_end(ip->i_sbd);
+
+	return error;
+}
+
+/**
+ * ea_dealloc_indirect -
+ * @ip:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_dealloc_indirect(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_rgrp_list rlist;
+	struct buffer_head *indbh, *dibh;
+	uint64_t *eablk, *end;
+	unsigned int rg_blocks = 0;
+	uint64_t bstart = 0;
+	unsigned int blen = 0;
+	unsigned int x;
+	int error;
+
+	memset(&rlist, 0, sizeof(struct gfs_rgrp_list));
+
+	error = gfs_dread(ip->i_gl, ip->i_di.di_eattr,
+			  DIO_START | DIO_WAIT, &indbh);
+	if (error)
+		return error;
+
+	if (gfs_metatype_check(sdp, indbh, GFS_METATYPE_IN)) {
+		error = -EIO;
+		goto out;
+	}
+
+	eablk = (uint64_t *)(indbh->b_data + sizeof(struct gfs_indirect));
+	end = eablk + sdp->sd_inptrs;
+
+	for (; eablk < end; eablk++) {
+		uint64_t bn;
+
+		if (!*eablk)
+			break;
+		bn = gfs64_to_cpu(*eablk);
+
+		if (bstart + blen == bn)
+			blen++;
+		else {
+			if (bstart)
+				gfs_rlist_add(sdp, &rlist, bstart);
+			bstart = bn;
+			blen = 1;
+		}	
+	}
+	if (bstart)
+		gfs_rlist_add(sdp, &rlist, bstart);
+	else
+		goto out;
+
+	gfs_rlist_alloc(&rlist, LM_ST_EXCLUSIVE, 0);
+
+	for (x = 0; x < rlist.rl_rgrps; x++) {
+		struct gfs_rgrpd *rgd;
+		rgd = gl2rgd(rlist.rl_ghs[x].gh_gl);
+		rg_blocks += rgd->rd_ri.ri_length;
+	}
+
+	error = gfs_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs);
+	if (error)
+		goto out_rlist_free;
+
+	error = gfs_trans_begin(sdp, 2 + rg_blocks, 1);
+	if (error)
+		goto out_gunlock;
+
+	gfs_trans_add_bh(ip->i_gl, indbh);
+
+	eablk = (uint64_t *)(indbh->b_data + sizeof(struct gfs_indirect));
+	bstart = 0;
+	blen = 0;
+
+	for (; eablk < end; eablk++) {
+		uint64_t bn;
+
+		if (!*eablk)
+			break;
+		bn = gfs64_to_cpu(*eablk);
+
+		if (bstart + blen == bn)
+			blen++;
+		else {
+			if (bstart)
+				gfs_metafree(ip, bstart, blen);
+			bstart = bn;
+			blen = 1;
+		}
+
+		*eablk = 0;
+		if (!ip->i_di.di_blocks)
+			gfs_consist_inode(ip);
+		ip->i_di.di_blocks--;
+	}
+	if (bstart)
+		gfs_metafree(ip, bstart, blen);
+
+	ip->i_di.di_flags &= ~GFS_DIF_EA_INDIRECT;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (!error) {
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	gfs_trans_end(sdp);
+
+ out_gunlock:
+	gfs_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs);
+
+ out_rlist_free:
+	gfs_rlist_free(&rlist);
+
+ out:
+	brelse(indbh);
+
+	return error;
+}
+
+/**
+ * ea_dealloc_block -
+ * @ip:
+ *
+ * Returns: errno
+ */
+
+static int
+ea_dealloc_block(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	struct gfs_rgrpd *rgd;
+	struct buffer_head *dibh;
+	int error;
+
+	rgd = gfs_blk2rgrpd(sdp, ip->i_di.di_eattr);
+	if (!rgd) {
+		gfs_consist_inode(ip);
+		return -EIO;
+	}
+
+	error = gfs_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &al->al_rgd_gh);
+	if (error)
+		return error;
+
+	error = gfs_trans_begin(sdp, 1 + rgd->rd_ri.ri_length, 1);
+	if (error)
+		goto out_gunlock;
+
+	gfs_metafree(ip, ip->i_di.di_eattr, 1);
+
+	ip->i_di.di_eattr = 0;
+	if (!ip->i_di.di_blocks)
+		gfs_consist_inode(ip);
+	ip->i_di.di_blocks--;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (!error) {
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	gfs_trans_end(sdp);
+
+ out_gunlock:
+	gfs_glock_dq_uninit(&al->al_rgd_gh);
+
+	return error;
+}
+
+/**
+ * gfs_ea_dealloc - deallocate the extended attribute fork
+ * @ip: the inode
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ea_dealloc(struct gfs_inode *ip)
+{
+	struct gfs_alloc *al;
+	int error;
+
+	al = gfs_alloc_get(ip);
+
+	error = gfs_quota_hold_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto out_alloc;
+
+	error = gfs_rindex_hold(ip->i_sbd, &al->al_ri_gh);
+	if (error)
+		goto out_quota;
+
+	error = ea_foreach(ip, ea_dealloc_unstuffed, NULL);
+	if (error)
+		goto out_rindex;
+
+	if (ip->i_di.di_flags & GFS_DIF_EA_INDIRECT) {
+		error = ea_dealloc_indirect(ip);
+		if (error)
+			goto out_rindex;
+	}
+
+	error = ea_dealloc_block(ip);
+
+ out_rindex:
+	gfs_glock_dq_uninit(&al->al_ri_gh);
+
+ out_quota:
+	gfs_quota_unhold_m(ip);
+
+ out_alloc:
+	gfs_alloc_put(ip);
+
+	return error;
+}
+
+/**
+ * gfs_get_eattr_meta - return all the eattr blocks of a file
+ * @dip: the directory
+ * @ub: the structure representing the user buffer to copy to
+ *
+ * Returns: errno
+ */
+
+int
+gfs_get_eattr_meta(struct gfs_inode *ip, struct gfs_user_buffer *ub)
+{
+	struct buffer_head *bh;
+	int error;
+
+	error = gfs_dread(ip->i_gl, ip->i_di.di_eattr,
+			  DIO_START | DIO_WAIT, &bh);
+	if (error)
+		return error;
+
+	gfs_add_bh_to_ub(ub, bh);
+
+	if (ip->i_di.di_flags & GFS_DIF_EA_INDIRECT) {
+		struct buffer_head *eabh;
+		uint64_t *eablk, *end;
+
+		if (gfs_metatype_check(ip->i_sbd, bh, GFS_METATYPE_IN)) {
+			error = -EIO;
+			goto out;
+		}
+
+		eablk = (uint64_t *)(bh->b_data + sizeof(struct gfs_indirect));
+		end = eablk + ip->i_sbd->sd_inptrs;
+
+		for (; eablk < end; eablk++) {
+			uint64_t bn;
+
+			if (!*eablk)
+				break;
+			bn = gfs64_to_cpu(*eablk);
+
+			error = gfs_dread(ip->i_gl, bn,
+					  DIO_START | DIO_WAIT, &eabh);
+			if (error)
+				break;
+			gfs_add_bh_to_ub(ub, eabh);
+			brelse(eabh);
+			if (error)
+				break;
+		}
+	}
+
+ out:
+	brelse(bh);
+
+	return error;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/eattr.h linux-2.6.9.debug/fs/gfs/eattr.h
--- linux-2.6.9.orig/fs/gfs/eattr.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/eattr.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,95 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __EATTR_DOT_H__
+#define __EATTR_DOT_H__
+
+#define GFS_EA_REC_LEN(ea) gfs32_to_cpu((ea)->ea_rec_len)
+#define GFS_EA_DATA_LEN(ea) gfs32_to_cpu((ea)->ea_data_len)
+
+#define GFS_EA_SIZE(ea) \
+MAKE_MULT8(sizeof(struct gfs_ea_header) + \
+	   (ea)->ea_name_len + \
+	   ((GFS_EA_IS_STUFFED(ea)) ? \
+	    GFS_EA_DATA_LEN(ea) : \
+	    (sizeof(uint64_t) * (ea)->ea_num_ptrs)))
+#define GFS_EA_STRLEN(ea) \
+((((ea)->ea_type == GFS_EATYPE_USR) ? 5 : 7) + \
+ (ea)->ea_name_len + 1)
+
+#define GFS_EA_IS_STUFFED(ea) (!(ea)->ea_num_ptrs)
+#define GFS_EA_IS_LAST(ea) ((ea)->ea_flags & GFS_EAFLAG_LAST)
+
+#define GFS_EAREQ_SIZE_STUFFED(er) \
+MAKE_MULT8(sizeof(struct gfs_ea_header) + \
+	   (er)->er_name_len + (er)->er_data_len)
+#define GFS_EAREQ_SIZE_UNSTUFFED(sdp, er) \
+MAKE_MULT8(sizeof(struct gfs_ea_header) + \
+	   (er)->er_name_len + \
+	   sizeof(uint64_t) * DIV_RU((er)->er_data_len, (sdp)->sd_jbsize))
+
+#define GFS_EA2NAME(ea) ((char *)((struct gfs_ea_header *)(ea) + 1))
+#define GFS_EA2DATA(ea) (GFS_EA2NAME(ea) + (ea)->ea_name_len)
+#define GFS_EA2DATAPTRS(ea) \
+((uint64_t *)(GFS_EA2NAME(ea) + MAKE_MULT8((ea)->ea_name_len)))
+#define GFS_EA2NEXT(ea) \
+((struct gfs_ea_header *)((char *)(ea) + GFS_EA_REC_LEN(ea)))
+#define GFS_EA_BH2FIRST(bh) \
+((struct gfs_ea_header *)((bh)->b_data + \
+			  sizeof(struct gfs_meta_header)))
+
+struct gfs_ea_request {
+	char *er_name;
+	char *er_data;
+	unsigned int er_name_len;
+	unsigned int er_data_len;
+	unsigned int er_type; /* GFS_EATYPE_... */
+	int er_flags;
+	mode_t er_mode;
+};
+
+struct gfs_ea_location {
+	struct buffer_head *el_bh;
+	struct gfs_ea_header *el_ea;
+	struct gfs_ea_header *el_prev;
+};
+
+int gfs_ea_repack(struct gfs_inode *ip);
+
+int gfs_ea_get_i(struct gfs_inode *ip, struct gfs_ea_request *er);
+int gfs_ea_set_i(struct gfs_inode *ip, struct gfs_ea_request *er);
+int gfs_ea_remove_i(struct gfs_inode *ip, struct gfs_ea_request *er);
+
+int gfs_ea_list(struct gfs_inode *ip, struct gfs_ea_request *er);
+int gfs_ea_get(struct gfs_inode *ip, struct gfs_ea_request *er);
+int gfs_ea_set(struct gfs_inode *ip, struct gfs_ea_request *er);
+int gfs_ea_remove(struct gfs_inode *ip, struct gfs_ea_request *er);
+
+int gfs_ea_dealloc(struct gfs_inode *ip);
+
+int gfs_get_eattr_meta(struct gfs_inode *ip, struct gfs_user_buffer *ub);
+
+/* Exported to acl.c */
+
+int gfs_ea_check_size(struct gfs_sbd *sdp, struct gfs_ea_request *er);
+int gfs_ea_find(struct gfs_inode *ip,
+		struct gfs_ea_request *er,
+		struct gfs_ea_location *el);
+int gfs_ea_get_copy(struct gfs_inode *ip,
+		    struct gfs_ea_location *el,
+		    char *data);
+int gfs_ea_acl_init(struct gfs_inode *ip, struct gfs_ea_request *er);
+int gfs_ea_acl_chmod(struct gfs_inode *ip, struct gfs_ea_location *el,
+		     struct iattr *attr, char *data);
+
+#endif /* __EATTR_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/file.c linux-2.6.9.debug/fs/gfs/file.c
--- linux-2.6.9.orig/fs/gfs/file.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/file.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,450 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/uaccess.h>
+
+#include "gfs.h"
+#include "bmap.h"
+#include "dio.h"
+#include "file.h"
+#include "inode.h"
+#include "trans.h"
+
+/**
+ * gfs_copy2mem - Trivial copy function for gfs_readi()
+ * @bh: The buffer to copy from, or NULL meaning zero the buffer
+ * @buf: The buffer to copy/zero
+ * @offset: The offset in the buffer to copy from
+ * @size: The amount of data to copy/zero
+ *
+ * Returns: errno
+ */
+
+int
+gfs_copy2mem(struct buffer_head *bh, void **buf, unsigned int offset,
+	     unsigned int size)
+{
+	char **p = (char **)buf;
+
+	if (bh)
+		memcpy(*p, bh->b_data + offset, size);
+	else
+		memset(*p, 0, size);
+
+	*p += size;
+
+	return 0;
+}
+
+/**
+ * gfs_copy2user - Copy data to user space
+ * @bh: The buffer
+ * @buf: The destination of the data
+ * @offset: The offset into the buffer
+ * @size: The amount of data to copy
+ *
+ * Returns: errno
+ */
+
+int
+gfs_copy2user(struct buffer_head *bh, void **buf,
+	      unsigned int offset, unsigned int size)
+{
+	char **p = (char **)buf;
+	int error;
+
+	if (bh)
+		error = copy_to_user(*p, bh->b_data + offset, size);
+	else
+		error = clear_user(*p, size);
+
+	if (error)
+		error = -EFAULT;
+	else
+		*p += size;
+
+	return error;
+}
+
+/**
+ * gfs_readi - Read a file
+ * @ip: The GFS Inode
+ * @buf: The buffer to place result into
+ * @offset: File offset to begin reading from
+ * @size: Amount of data to transfer
+ * @copy_fn: Function to actually perform the copy
+ *
+ * The @copy_fn only copies a maximum of a single block at once so
+ * we are safe calling it with int arguments. It is done so that
+ * we don't needlessly put 64bit arguments on the stack and it
+ * also makes the code in the @copy_fn nicer too.
+ *
+ * Returns: The amount of data actually copied or the error
+ */
+
+int
+gfs_readi(struct gfs_inode *ip, void *buf,
+	  uint64_t offset, unsigned int size,
+	  read_copy_fn_t copy_fn)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *bh;
+	uint64_t lblock, dblock;
+	unsigned int o;
+	uint32_t extlen = 0;
+	unsigned int amount;
+	int not_new = 0;
+	int journaled = gfs_is_jdata(ip);
+	int copied = 0;
+	int error = 0;
+
+	if (offset >= ip->i_di.di_size)
+		return 0;
+
+	if ((offset + size) > ip->i_di.di_size)
+		size = ip->i_di.di_size - offset;
+
+	if (!size)
+		return 0;
+
+	if (journaled) {
+		lblock = offset;
+		o = do_div(lblock, sdp->sd_jbsize);
+	} else {
+		lblock = offset >> sdp->sd_sb.sb_bsize_shift;
+		o = offset & (sdp->sd_sb.sb_bsize - 1);
+	}
+
+	if (gfs_is_stuffed(ip))
+		o += sizeof(struct gfs_dinode);
+	else if (journaled)
+		o += sizeof(struct gfs_meta_header);
+
+	while (copied < size) {
+		amount = size - copied;
+		if (amount > sdp->sd_sb.sb_bsize - o)
+			amount = sdp->sd_sb.sb_bsize - o;
+
+		if (!extlen) {
+			if (!gfs_is_stuffed(ip)) {
+				error = gfs_block_map(ip, lblock, &not_new,
+						      &dblock, &extlen);
+				if (error)
+					goto fail;
+			} else if (!lblock) {
+				dblock = ip->i_num.no_addr;
+				extlen = 1;
+			} else
+				dblock = 0;
+		}
+
+		if (extlen > 1)
+			gfs_start_ra(ip->i_gl, dblock, extlen);
+
+		if (dblock) {
+			error = gfs_get_data_buffer(ip, dblock, not_new, &bh);
+			if (error)
+				goto fail;
+
+			dblock++;
+			extlen--;
+		} else
+			bh = NULL;
+
+		error = copy_fn(bh, &buf, o, amount);
+		if (bh)
+			brelse(bh);
+		if (error)
+			goto fail;
+
+		copied += amount;
+		lblock++;
+
+		o = (journaled) ? sizeof(struct gfs_meta_header) : 0;
+	}
+
+	return copied;
+
+ fail:
+	return (copied) ? copied : error;
+}
+
+/**
+ * gfs_copy_from_mem - Trivial copy function for gfs_writei()
+ * @ip: The file to write to
+ * @bh: The buffer to copy to or clear
+ * @buf: The buffer to copy from
+ * @offset: The offset in the buffer to write to
+ * @size: The amount of data to write
+ * @new: Flag indicating that remaining space in the buffer should be zeroed
+ *
+ * Returns: errno
+ */
+
+int
+gfs_copy_from_mem(struct gfs_inode *ip, struct buffer_head *bh, void **buf,
+		  unsigned int offset, unsigned int size, int new)
+{
+	char **p = (char **)buf;
+	int error = 0;
+
+	/* The dinode block always gets journaled */
+	if (bh->b_blocknr == ip->i_num.no_addr) {
+		if (gfs_assert_warn(ip->i_sbd, !new))
+			return -EIO;
+		gfs_trans_add_bh(ip->i_gl, bh);
+		memcpy(bh->b_data + offset, *p, size);
+
+	/* Data blocks for journaled files get written added to the journal */
+	} else if (gfs_is_jdata(ip)) {
+		gfs_trans_add_bh(ip->i_gl, bh);
+		memcpy(bh->b_data + offset, *p, size);
+		if (new)
+			gfs_buffer_clear_ends(bh, offset, size, TRUE);
+
+	/* Non-journaled data blocks get written to in-place disk blocks */
+	} else {
+		memcpy(bh->b_data + offset, *p, size);
+		if (new)
+			gfs_buffer_clear_ends(bh, offset, size, FALSE);
+		error = gfs_dwrite(ip->i_sbd, bh, DIO_DIRTY);
+	}
+
+	if (!error)
+		*p += size;
+
+	return error;
+}
+
+/**
+ * gfs_copy_from_user - Copy bytes from user space for gfs_writei()
+ * @ip: The file to write to
+ * @bh: The buffer to copy to or clear
+ * @buf: The buffer to copy from
+ * @offset: The offset in the buffer to write to
+ * @size: The amount of data to write
+ * @new: Flag indicating that remaining space in the buffer should be zeroed
+ *
+ * Returns: errno
+ */
+
+int
+gfs_copy_from_user(struct gfs_inode *ip, struct buffer_head *bh, void **buf,
+		   unsigned int offset, unsigned int size, int new)
+{
+	char **p = (char **)buf;
+	int error = 0;
+
+	/* the dinode block always gets journaled */
+	if (bh->b_blocknr == ip->i_num.no_addr) {
+		if (gfs_assert_warn(ip->i_sbd, !new))
+			return -EIO;
+		gfs_trans_add_bh(ip->i_gl, bh);
+		if (copy_from_user(bh->b_data + offset, *p, size))
+			error = -EFAULT;
+
+	/* Data blocks for journaled files get written added to the journal */
+	} else if (gfs_is_jdata(ip)) {
+		gfs_trans_add_bh(ip->i_gl, bh);
+		if (copy_from_user(bh->b_data + offset, *p, size))
+			error = -EFAULT;
+		if (new) {
+			gfs_buffer_clear_ends(bh, offset, size, TRUE);
+			if (error)
+				memset(bh->b_data + offset, 0, size);
+		}
+
+	/* non-journaled data blocks get written to in-place disk blocks */
+	} else {
+		if (copy_from_user(bh->b_data + offset, *p, size))
+			error = -EFAULT;
+		if (error) {
+			if (new)
+				gfs_buffer_clear(bh);
+			gfs_dwrite(ip->i_sbd, bh, DIO_DIRTY);
+		} else {
+			if (new)
+				gfs_buffer_clear_ends(bh, offset, size, FALSE);
+			error = gfs_dwrite(ip->i_sbd, bh, DIO_DIRTY);
+		}
+	}
+
+	if (!error)
+		*p += size;
+
+	return error;
+}
+
+/**
+ * gfs_writei - Write bytes to a file
+ * @ip: The GFS inode
+ * @buf: The buffer containing information to be written
+ * @offset: The file offset to start writing at
+ * @size: The amount of data to write
+ * @copy_fn: Function to do the actual copying
+ *
+ * Returns: The number of bytes correctly written or error code
+ */
+
+int
+gfs_writei(struct gfs_inode *ip, void *buf,
+	   uint64_t offset, unsigned int size,
+	   write_copy_fn_t copy_fn,
+           struct kiocb *iocb)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct buffer_head *dibh, *bh;
+	uint64_t lblock, dblock;
+	unsigned int o;
+	uint32_t extlen = 0;
+	unsigned int amount;
+	int new;
+	int journaled = gfs_is_jdata(ip);
+	const uint64_t start = offset;
+	int copied = 0;
+	int error = 0;
+
+	if (!size)
+		return 0;
+
+	if (gfs_is_stuffed(ip) &&
+	    ((start + size) > (sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode)))) {
+		error = gfs_unstuff_dinode(ip, gfs_unstuffer_async, NULL);
+		if (error)
+			return error;
+	}
+
+	if (journaled) {
+		lblock = offset;
+		o = do_div(lblock, sdp->sd_jbsize);
+	} else {
+		lblock = offset >> sdp->sd_sb.sb_bsize_shift;
+		o = offset & (sdp->sd_sb.sb_bsize - 1);
+	}
+
+	if (gfs_is_stuffed(ip))
+		o += sizeof(struct gfs_dinode);
+	else if (journaled)
+		o += sizeof(struct gfs_meta_header);
+
+	while (copied < size) {
+		amount = size - copied;
+		if (amount > sdp->sd_sb.sb_bsize - o)
+			amount = sdp->sd_sb.sb_bsize - o;
+
+		if (!extlen) {
+			if (!gfs_is_stuffed(ip)) {
+				new = TRUE;
+				error = gfs_block_map(ip, lblock, &new, &dblock, &extlen);
+				if (error)
+					goto fail;
+			} else {
+				new = FALSE;
+				dblock = ip->i_num.no_addr;
+				extlen = 1;
+			}
+		}
+
+		if (journaled && extlen > 1)
+			gfs_start_ra(ip->i_gl, dblock, extlen);
+
+		error = gfs_get_data_buffer(ip, dblock,
+					    (amount == sdp->sd_sb.sb_bsize) ? TRUE : new,
+					    &bh);
+		if (error)
+			goto fail;
+
+		error = copy_fn(ip, bh, &buf, o, amount, new);
+		brelse(bh);
+		if (error)
+			goto fail;
+
+		copied += amount;
+		lblock++;
+		dblock++;
+		extlen--;
+
+		o = (journaled) ? sizeof(struct gfs_meta_header) : 0;
+	}
+
+ out:
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		return error;
+
+	if (ip->i_di.di_size < start + copied)
+		ip->i_di.di_size = start + copied;
+	ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+	brelse(dibh);
+
+	return copied;
+
+ fail:
+	if (copied)
+		goto out;
+	return error;
+}
+
+/*
+ * gfs_zero_blocks - zero out disk blocks via gfs_writei()
+ * @ip: The file to write to
+ * @bh: The buffer to clear
+ * @buf: The pseudo buffer (not used but added to keep interface unchanged)
+ * @offset: The offset in the buffer to write to
+ * @size: The size to zero out
+ * @new: Flag indicating that remaining space in the buffer should be zeroed
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int
+gfs_zero_blocks(struct gfs_inode *ip, struct buffer_head *bh, void **buf,
+                unsigned int offset, unsigned int size, int new)
+{
+	int error = 0;
+
+	/* The dinode block always gets journaled */
+	if (bh->b_blocknr == ip->i_num.no_addr) {
+		if (gfs_assert_warn(ip->i_sbd, !new))
+			return -EIO;
+		gfs_trans_add_bh(ip->i_gl, bh);
+		memset((bh)->b_data + offset, 0, size);
+
+	/* Data blocks for journaled files get written added to the journal */
+	} else if (gfs_is_jdata(ip)) {
+		gfs_trans_add_bh(ip->i_gl, bh);
+		memset((bh)->b_data + offset, 0, size);
+		if (new)
+			gfs_buffer_clear_ends(bh, offset, size, TRUE);
+
+	/* Non-journaled data blocks get written to in-place disk blocks */
+	} else {
+		memset((bh)->b_data + offset, 0, size);
+		if (new)
+			gfs_buffer_clear_ends(bh, offset, size, FALSE);
+		error = gfs_dwrite(ip->i_sbd, bh, DIO_DIRTY);
+	}
+
+	return error;
+}
+
diff -pruN linux-2.6.9.orig/fs/gfs/file.h linux-2.6.9.debug/fs/gfs/file.h
--- linux-2.6.9.orig/fs/gfs/file.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/file.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,55 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __FILE_DOT_H__
+#define __FILE_DOT_H__
+
+typedef int (*read_copy_fn_t) (struct buffer_head *bh, void **buf,
+			       unsigned int offset, unsigned int size);
+typedef int (*write_copy_fn_t) (struct gfs_inode *ip, struct buffer_head *bh,
+				void **buf, unsigned int offset,
+				unsigned int size, int new);
+
+int gfs_copy2mem(struct buffer_head *bh, void **buf,
+		 unsigned int offset, unsigned int size);
+int gfs_copy2user(struct buffer_head *bh, void **buf,
+		  unsigned int offset, unsigned int size);
+int gfs_readi(struct gfs_inode *ip, void *buf, uint64_t offset,
+	      unsigned int size, read_copy_fn_t copy_fn);
+
+int gfs_copy_from_mem(struct gfs_inode *ip, struct buffer_head *bh, void **buf,
+		      unsigned int offset, unsigned int size, int new);
+int gfs_copy_from_user(struct gfs_inode *ip, struct buffer_head *bh, void **buf,
+		       unsigned int offset, unsigned int size, int new);
+int gfs_writei(struct gfs_inode *ip, void *buf, uint64_t offset,
+               unsigned int size, write_copy_fn_t copy_fn,
+               struct kiocb *iocb);
+
+int gfs_zero_blocks(struct gfs_inode *ip, struct buffer_head *bh, void **buf,
+		    unsigned int offset, unsigned int size, int new);
+
+static __inline__ int
+gfs_internal_read(struct gfs_inode *ip, char *buf, uint64_t offset,
+		  unsigned int size)
+{
+	return gfs_readi(ip, buf, offset, size, gfs_copy2mem);
+}
+
+static __inline__ int
+gfs_internal_write(struct gfs_inode *ip, char *buf, uint64_t offset,
+		   unsigned int size)
+{
+	return gfs_writei(ip, buf, offset, size, gfs_copy_from_mem, NULL);
+}
+
+#endif /* __FILE_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/fixed_div64.h linux-2.6.9.debug/fs/gfs/fixed_div64.h
--- linux-2.6.9.orig/fs/gfs/fixed_div64.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/fixed_div64.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ *
+ * Additional munging:
+ * Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+ */
+
+#ifndef __FIXED_DIV64_DOT_H__
+#define __FIXED_DIV64_DOT_H__
+
+#include <asm/div64.h>
+
+#if defined __i386__
+/* For ia32 we need to pull some tricks to get past various versions
+ * of the compiler which do not like us using do_div in the middle
+ * of large functions.
+ */
+static inline __u32 fixed_div64_do_div(void *a, __u32 b, int n)
+{
+	__u32	mod;
+
+	switch (n) {
+		case 4:
+			mod = *(__u32 *)a % b;
+			*(__u32 *)a = *(__u32 *)a / b;
+			return mod;
+		case 8:
+			{
+			unsigned long __upper, __low, __high, __mod;
+			__u64	c = *(__u64 *)a;
+			__upper = __high = c >> 32;
+			__low = c;
+			if (__high) {
+				__upper = __high % (b);
+				__high = __high / (b);
+			}
+			asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (b), "0" (__low), "1" (__upper));
+			asm("":"=A" (c):"a" (__low),"d" (__high));
+			*(__u64 *)a = c;
+			return __mod;
+			}
+	}
+
+	/* NOTREACHED */
+	return 0;
+}
+
+/* Side effect free 64 bit mod operation */
+static inline __u32 fixed_div64_do_mod(void *a, __u32 b, int n)
+{
+	switch (n) {
+		case 4:
+			return *(__u32 *)a % b;
+		case 8:
+			{
+			unsigned long __upper, __low, __high, __mod;
+			__u64	c = *(__u64 *)a;
+			__upper = __high = c >> 32;
+			__low = c;
+			if (__high) {
+				__upper = __high % (b);
+				__high = __high / (b);
+			}
+			asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (b), "0" (__low), "1" (__upper));
+			asm("":"=A" (c):"a" (__low),"d" (__high));
+			return __mod;
+			}
+	}
+
+	/* NOTREACHED */
+	return 0;
+}
+#else
+static inline __u32 fixed_div64_do_div(void *a, __u32 b, int n)
+{
+	__u32	mod;
+
+	switch (n) {
+		case 4:
+			mod = *(__u32 *)a % b;
+			*(__u32 *)a = *(__u32 *)a / b;
+			return mod;
+		case 8:
+			mod = do_div(*(__u64 *)a, b);
+			return mod;
+	}
+
+	/* NOTREACHED */
+	return 0;
+}
+
+/* Side effect free 64 bit mod operation */
+static inline __u32 fixed_div64_do_mod(void *a, __u32 b, int n)
+{
+	switch (n) {
+		case 4:
+			return *(__u32 *)a % b;
+		case 8:
+			{
+			__u64	c = *(__u64 *)a;
+			return do_div(c, b);
+			}
+	}
+
+	/* NOTREACHED */
+	return 0;
+}
+#endif
+
+#undef do_div
+#define do_div(a, b)	fixed_div64_do_div(&(a), (b), sizeof(a))
+#define do_mod(a, b)	fixed_div64_do_mod(&(a), (b), sizeof(a))
+
+#endif /* __FIXED_DIV64_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/format.h linux-2.6.9.debug/fs/gfs/format.h
--- linux-2.6.9.orig/fs/gfs/format.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/format.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,30 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __FORMAT_DOT_H__
+#define __FORMAT_DOT_H__
+
+static const uint32_t gfs_old_fs_formats[] = {
+	1308,
+	1307,
+	1306,
+	1305,
+	0
+};
+
+static const uint32_t gfs_old_multihost_formats[] = {
+	1400,
+	0
+};
+
+#endif /* __FORMAT_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/gfs.h linux-2.6.9.debug/fs/gfs/gfs.h
--- linux-2.6.9.orig/fs/gfs/gfs.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/gfs.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,95 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __GFS_DOT_H__
+#define __GFS_DOT_H__
+
+#define GFS_RELEASE_NAME "2.6.9-60.3"
+
+#include <linux/lm_interface.h>
+#include <linux/gfs_ondisk.h>
+
+#include "fixed_div64.h"
+#include "lvb.h"
+#include "incore.h"
+#include "util.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define NO_CREATE (0)
+#define CREATE (1)
+
+#if (BITS_PER_LONG == 64)
+#define PRIu64 "lu"
+#define PRId64 "ld"
+#define PRIo64 "lo"
+#define PRIx64 "lx"
+#define PRIX64 "lX"
+#define SCNu64 "lu"
+#define SCNd64 "ld"
+#define SCNo64 "lo"
+#define SCNx64 "lx"
+#define SCNX64 "lX"
+#else
+#define PRIu64 "Lu"
+#define PRId64 "Ld"
+#define PRIo64 "Lo"
+#define PRIx64 "Lx"
+#define PRIX64 "LX"
+#define SCNu64 "Lu"
+#define SCNd64 "Ld"
+#define SCNo64 "Lo"
+#define SCNx64 "Lx"
+#define SCNX64 "LX"
+#endif
+
+/*  Divide num by den.  Round up if there is a remainder.  */
+#define DIV_RU(num, den) (((num) + (den) - 1) / (den))
+#define MAKE_MULT8(x) (((x) + 7) & ~7)
+
+#define GFS_FAST_NAME_SIZE (8)
+
+#define vfs2sdp(sb) ((struct gfs_sbd *)(sb)->s_fs_info)
+#define vn2ip(inode) ((struct gfs_inode *)(inode)->u.generic_ip)
+#define vf2fp(file) ((struct gfs_file *)(file)->private_data)
+#define bh2bd(bh) ((struct gfs_bufdata *)(bh)->b_private)
+
+/* A process can build only one transaction at a time */
+#define current_transaction ((struct gfs_trans *)(current->journal_info))
+
+#define gl2ip(gl) ((struct gfs_inode *)(gl)->gl_object)
+#define gl2rgd(gl) ((struct gfs_rgrpd *)(gl)->gl_object)
+#define gl2gl(gl) ((struct gfs_glock *)(gl)->gl_object)
+
+#define gfs_printf(fmt, args...) \
+do { \
+	if (buf) { \
+		int gspf_left = size - *count, gspf_out; \
+		if (gspf_left <= 0) \
+			goto out; \
+		gspf_out = snprintf(buf + *count, gspf_left, fmt, ##args); \
+		if (gspf_out < gspf_left) \
+			*count += gspf_out; \
+		else \
+			goto out; \
+	} else \
+		printk(fmt, ##args); \
+} while (0)
+
+#endif /* __GFS_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/glock.c linux-2.6.9.debug/fs/gfs/glock.c
--- linux-2.6.9.orig/fs/gfs/glock.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/glock.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,2953 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/uaccess.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "lm.h"
+#include "lops.h"
+#include "quota.h"
+#include "recovery.h"
+
+/*  Must be kept in sync with the beginning of struct gfs_glock  */
+struct glock_plug {
+	struct list_head gl_list;
+	unsigned long gl_flags;
+};
+
+struct greedy {
+	struct gfs_holder gr_gh;
+	struct work_struct gr_work;
+};
+
+typedef void (*glock_examiner) (struct gfs_glock * gl);
+
+/**
+ * relaxed_state_ok - is a requested lock compatible with the current lock mode?
+ * @actual: the current state of the lock
+ * @requested: the lock state that was requested by the caller
+ * @flags: the modifier flags passed in by the caller
+ *
+ * Returns: TRUE if the locks are compatible, FALSE otherwise
+ *
+ * It's often possible that a holder B may request the lock in SHARED mode,
+ * while another holder A (on this same node) has the lock in EXCLUSIVE mode
+ * (node must hold the glock in EXCLUSIVE mode for this situation, of course).
+ * This is okay to grant, in some cases, since both holders would have access
+ * to the in-core up-to-date cached data that the EX holder would write to disk.
+ * This is the default behavior.
+ *
+ * The EXACT flag disallows this behavior, though.  A SHARED request would
+ * compatible only with a SHARED lock with this flag.
+ *
+ * The ANY flag provides broader permission to grant the lock to a holder,
+ * whatever the requested state is, as long as the lock is locked in any mode.
+ */
+
+static __inline__ int
+relaxed_state_ok(unsigned int actual, unsigned requested, int flags)
+{
+	if (actual == requested)
+		return TRUE;
+
+	if (flags & GL_EXACT)
+		return FALSE;
+
+	if (actual == LM_ST_EXCLUSIVE && requested == LM_ST_SHARED)
+		return TRUE;
+
+	if (actual != LM_ST_UNLOCKED && (flags & LM_FLAG_ANY))
+		return TRUE;
+
+	return FALSE;
+}
+
+/**
+ * gl_hash() - Turn glock number into hash bucket number
+ * @lock: The glock number
+ *
+ * Returns: The number of the corresponding hash bucket
+ */
+
+static unsigned int
+gl_hash(struct lm_lockname *name)
+{
+	unsigned int h;
+
+	h = gfs_hash(&name->ln_number, sizeof(uint64_t));
+	h = gfs_hash_more(&name->ln_type, sizeof(unsigned int), h);
+	h &= GFS_GL_HASH_MASK;
+
+	return h;
+}
+
+/**
+ * glock_hold() - increment reference count on glock
+ * @gl: The glock to hold
+ *
+ */
+
+static __inline__ void
+glock_hold(struct gfs_glock *gl)
+{
+	gfs_assert(gl->gl_sbd, atomic_read(&gl->gl_count) > 0,);
+	atomic_inc(&gl->gl_count);
+}
+
+/**
+ * glock_put() - Decrement reference count on glock
+ * @gl: The glock to put
+ *
+ */
+
+static __inline__ void
+glock_put(struct gfs_glock *gl)
+{
+	if (atomic_read(&gl->gl_count) == 1)
+		gfs_glock_schedule_for_reclaim(gl);
+	gfs_assert(gl->gl_sbd, atomic_read(&gl->gl_count) > 0,);
+	atomic_dec(&gl->gl_count);
+}
+
+/**
+ * queue_empty - check to see if a glock's queue is empty
+ * @gl: the glock
+ * @head: the head of the queue to check
+ *
+ * Returns: TRUE if the queue is empty
+ */
+
+static __inline__ int
+queue_empty(struct gfs_glock *gl, struct list_head *head)
+{
+	int empty;
+	spin_lock(&gl->gl_spin);
+	empty = list_empty(head);
+	spin_unlock(&gl->gl_spin);
+	return empty;
+}
+
+/**
+ * search_bucket() - Find struct gfs_glock by lock number
+ * @bucket: the bucket to search
+ * @name: The lock name
+ *
+ * Returns: NULL, or the struct gfs_glock with the requested number
+ */
+
+static struct gfs_glock *
+search_bucket(struct gfs_gl_hash_bucket *bucket, struct lm_lockname *name)
+{
+	struct list_head *tmp, *head;
+	struct gfs_glock *gl;
+
+	for (head = &bucket->hb_list, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		gl = list_entry(tmp, struct gfs_glock, gl_list);
+
+		if (test_bit(GLF_PLUG, &gl->gl_flags))
+			continue;
+		if (!lm_name_equal(&gl->gl_name, name))
+			continue;
+
+		atomic_inc(&gl->gl_count);
+
+		return gl;
+	}
+
+	return NULL;
+}
+
+/**
+ * gfs_glock_find() - Find glock by lock number
+ * @sdp: The GFS superblock
+ * @name: The lock name
+ *
+ * Figure out what bucket the lock is in, acquire the read lock on
+ * it and call search_bucket().
+ *
+ * Returns: NULL, or the struct gfs_glock with the requested number
+ */
+
+struct gfs_glock *
+gfs_glock_find(struct gfs_sbd *sdp, struct lm_lockname *name)
+{
+	struct gfs_gl_hash_bucket *bucket = &sdp->sd_gl_hash[gl_hash(name)];
+	struct gfs_glock *gl;
+
+	read_lock(&bucket->hb_lock);
+	gl = search_bucket(bucket, name);
+	read_unlock(&bucket->hb_lock);
+
+	return gl;
+}
+
+/**
+ * glock_free() - Perform a few checks and then release struct gfs_glock
+ * @gl: The glock to release
+ *
+ * Also calls lock module to release its internal structure for this glock.
+ *
+ */
+
+static void
+glock_free(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct inode *aspace = gl->gl_aspace;
+
+	gfs_assert_warn(sdp, list_empty(&gl->gl_list));
+	gfs_assert_warn(sdp, atomic_read(&gl->gl_count) == 1);
+	gfs_assert_warn(sdp, list_empty(&gl->gl_holders));
+	gfs_assert_warn(sdp, list_empty(&gl->gl_waiters1));
+        gfs_assert_warn(sdp, list_empty(&gl->gl_waiters2));
+	gfs_assert_warn(sdp, list_empty(&gl->gl_waiters3));
+	gfs_assert_warn(sdp, gl->gl_state == LM_ST_UNLOCKED);
+	gfs_assert_warn(sdp, !gl->gl_object);
+	gfs_assert_warn(sdp, !gl->gl_lvb);
+	gfs_assert_warn(sdp, list_empty(&gl->gl_reclaim));
+
+	gfs_lm_put_lock(sdp, gl->gl_lock);
+
+	if (aspace)
+		gfs_aspace_put(aspace);
+
+	kmem_cache_free(gfs_glock_cachep, gl);
+
+	atomic_dec(&sdp->sd_glock_count);
+}
+
+/**
+ * gfs_glock_get() - Get a glock, or create one if one doesn't exist
+ * @sdp: The GFS superblock
+ * @number: the lock number
+ * @glops: The glock_operations to use
+ * @create: If FALSE, don't create the glock if it doesn't exist
+ * @glp: the glock is returned here
+ *
+ * This does not lock a glock, just finds/creates structures for one.
+ * 
+ * Returns: errno
+ */
+
+int
+gfs_glock_get(struct gfs_sbd *sdp,
+	      uint64_t number, struct gfs_glock_operations *glops,
+	      int create, struct gfs_glock **glp)
+{
+	struct lm_lockname name;
+	struct gfs_glock *gl, *tmp;
+	struct gfs_gl_hash_bucket *bucket;
+	int error;
+
+	/* Look for pre-existing glock in hash table */
+	name.ln_number = number;
+	name.ln_type = glops->go_type;
+	bucket = &sdp->sd_gl_hash[gl_hash(&name)];
+
+	read_lock(&bucket->hb_lock);
+	gl = search_bucket(bucket, &name);
+	read_unlock(&bucket->hb_lock);
+
+	if (gl || !create) {
+		*glp = gl;
+		return 0;
+	}
+
+	/* None found; create a new one */
+	gl = kmem_cache_alloc(gfs_glock_cachep, GFP_KERNEL);
+	if (!gl)
+		return -ENOMEM;
+
+	memset(gl, 0, sizeof(struct gfs_glock));
+
+	INIT_LIST_HEAD(&gl->gl_list);
+	gl->gl_name = name;
+	atomic_set(&gl->gl_count, 1);
+
+	spin_lock_init(&gl->gl_spin);
+
+	gl->gl_state = LM_ST_UNLOCKED;
+	INIT_LIST_HEAD(&gl->gl_holders);
+	INIT_LIST_HEAD(&gl->gl_waiters1);
+	INIT_LIST_HEAD(&gl->gl_waiters2);
+	INIT_LIST_HEAD(&gl->gl_waiters3);
+
+	gl->gl_ops = glops;
+
+	INIT_LE(&gl->gl_new_le, &gfs_glock_lops);
+	INIT_LE(&gl->gl_incore_le, &gfs_glock_lops);
+
+	gl->gl_bucket = bucket;
+	INIT_LIST_HEAD(&gl->gl_reclaim);
+
+	gl->gl_sbd = sdp;
+
+	INIT_LIST_HEAD(&gl->gl_ail_bufs);
+
+	/* If this glock protects actual on-disk data or metadata blocks,
+	   create a VFS inode to manage the pages/buffers holding them. */
+	if (glops == &gfs_inode_glops ||
+	    glops == &gfs_rgrp_glops ||
+	    glops == &gfs_meta_glops) {
+		gl->gl_aspace = gfs_aspace_get(sdp);
+		if (!gl->gl_aspace) {
+			error = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	/* Ask lock module to find/create its structure for this lock
+	   (but this doesn't lock the inter-node lock yet) */
+	error = gfs_lm_get_lock(sdp, &name, &gl->gl_lock);
+	if (error)
+		goto fail_aspace;
+
+	atomic_inc(&sdp->sd_glock_count);
+
+	/* Double-check, in case another process created the glock, and has
+	   put it in the hash table while we were preparing this one */
+	write_lock(&bucket->hb_lock);
+	tmp = search_bucket(bucket, &name);
+	if (tmp) {
+		/* Somebody beat us to it; forget the one we prepared */
+		write_unlock(&bucket->hb_lock);
+		glock_free(gl);
+		gl = tmp;
+	} else {
+		/* Add our glock to hash table */
+		list_add_tail(&gl->gl_list, &bucket->hb_list);
+		write_unlock(&bucket->hb_lock);
+	}
+
+	*glp = gl;
+
+	return 0;
+
+ fail_aspace:
+	if (gl->gl_aspace)
+		gfs_aspace_put(gl->gl_aspace);
+
+ fail:
+	kmem_cache_free(gfs_glock_cachep, gl);	
+
+	return error;
+}
+
+/**
+ * gfs_glock_hold() - As glock_hold(), but suitable for exporting
+ * @gl: The glock to hold
+ *
+ */
+
+void
+gfs_glock_hold(struct gfs_glock *gl)
+{
+	glock_hold(gl);
+}
+
+/**
+ * gfs_glock_put() - As glock_put(), but suitable for exporting
+ * @gl: The glock to put
+ *
+ */
+
+void
+gfs_glock_put(struct gfs_glock *gl)
+{
+	glock_put(gl);
+}
+
+/**
+ * gfs_holder_init - initialize a struct gfs_holder in the default way
+ * @gl: the glock 
+ * @state: the state we're requesting
+ * @flags: the modifier flags
+ * @gh: the holder structure
+ *
+ */
+
+void
+gfs_holder_init(struct gfs_glock *gl, unsigned int state, int flags,
+		struct gfs_holder *gh)
+{
+	memset(gh, 0, sizeof(struct gfs_holder));
+
+	INIT_LIST_HEAD(&gh->gh_list);
+	gh->gh_gl = gl;
+	gh->gh_owner = current;
+	gh->gh_state = state;
+	gh->gh_flags = flags;
+
+	if (gh->gh_state == LM_ST_EXCLUSIVE)
+		gh->gh_flags |= GL_LOCAL_EXCL;
+
+	init_completion(&gh->gh_wait);
+
+	glock_hold(gl);
+}
+
+/**
+ * gfs_holder_reinit - reinitialize a struct gfs_holder so we can requeue it
+ * @state: the state we're requesting
+ * @flags: the modifier flags
+ * @gh: the holder structure
+ *
+ * Preserve holder's associated glock and owning process.
+ * Reset all holder state flags (we're starting a new request from scratch),
+ *   except for HIF_ALLOCED.
+ * Don't do glock_hold() again (it was done in gfs_holder_init()).
+ * Don't mess with the glock.
+ *
+ * Rules:
+ *   Holder must have been gfs_holder_init()d already
+ *   Holder must *not* be in glock's holder list or wait queues now
+ */
+
+void
+gfs_holder_reinit(unsigned int state, int flags, struct gfs_holder *gh)
+{
+	int alloced;
+
+	gfs_assert_warn(gh->gh_gl->gl_sbd,
+			list_empty(&gh->gh_list));
+
+	gh->gh_state = state;
+	gh->gh_flags = flags;
+
+	if (gh->gh_state == LM_ST_EXCLUSIVE)
+		gh->gh_flags |= GL_LOCAL_EXCL;
+
+	alloced = test_bit(HIF_ALLOCED, &gh->gh_iflags);
+	memset(&gh->gh_iflags, 0, sizeof(unsigned long));
+	if (alloced)
+		set_bit(HIF_ALLOCED, &gh->gh_iflags);
+}
+
+/**
+ * gfs_holder_uninit - uninitialize a holder structure (drop reference on glock)
+ * @gh: the holder structure
+ *
+ */
+
+void
+gfs_holder_uninit(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+
+	gfs_assert_warn(gl->gl_sbd, list_empty(&gh->gh_list));
+	gh->gh_gl = NULL;
+
+	glock_put(gl);
+}
+
+/**
+ * gfs_holder_get - get a struct gfs_holder structure
+ * @gl: the glock 
+ * @state: the state we're requesting
+ * @flags: the modifier flags
+ *
+ * Figure out how big an impact this function has.  Either:
+ * 1) Replace it with a cache of structures hanging off the struct gfs_sbd
+ * 2) Leave it like it is
+ *
+ * Returns: the holder structure, NULL on ENOMEM
+ */
+
+struct gfs_holder *
+gfs_holder_get(struct gfs_glock *gl, unsigned int state, int flags)
+{
+	struct gfs_holder *gh;
+
+	gh = kmalloc(sizeof(struct gfs_holder), GFP_KERNEL);
+	if (!gh)
+		return NULL;
+
+	gfs_holder_init(gl, state, flags, gh);
+	set_bit(HIF_ALLOCED, &gh->gh_iflags);
+
+	return gh;
+}
+
+/**
+ * gfs_holder_put - get rid of a struct gfs_holder structure
+ * @gh: the holder structure
+ *
+ */
+
+void
+gfs_holder_put(struct gfs_holder *gh)
+{
+	if (gfs_assert_warn(gh->gh_gl->gl_sbd,
+			    test_bit(HIF_ALLOCED, &gh->gh_iflags)))
+		return;
+	gfs_holder_uninit(gh);
+	kfree(gh);
+}
+
+/**
+ * handle_recurse - put other holder structures (marked recursive) into the holders list
+ * @gh: the holder structure
+ *
+ */
+
+static void
+handle_recurse(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct list_head *tmp, *head, *next;
+	struct gfs_holder *tmp_gh;
+	int found = FALSE;
+
+	if (gfs_assert_warn(sdp, gh->gh_owner))
+		return;
+
+	for (head = &gl->gl_waiters3, tmp = head->next, next = tmp->next;
+	     tmp != head;
+	     tmp = next, next = tmp->next) {
+		tmp_gh = list_entry(tmp, struct gfs_holder, gh_list);
+		if (tmp_gh->gh_owner != gh->gh_owner)
+			continue;
+
+		gfs_assert_warn(sdp, test_bit(HIF_RECURSE, &tmp_gh->gh_iflags));
+
+		list_move_tail(&tmp_gh->gh_list, &gl->gl_holders);
+		tmp_gh->gh_error = 0;
+		set_bit(HIF_HOLDER, &tmp_gh->gh_iflags);
+
+		complete(&tmp_gh->gh_wait);
+
+		found = TRUE;
+	}
+
+	gfs_assert_warn(sdp, found);
+}
+
+/**
+ * do_unrecurse - a recursive holder was just dropped of the waiters3 list
+ * @gh: the holder
+ *
+ * If there is only one other recursive holder, clear its HIF_RECURSE bit
+ *   (it's no longer a recursive request).
+ * If there is more than one, leave them alone (they're recursive!).
+ *
+ */
+
+static void
+do_unrecurse(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct list_head *tmp, *head;
+	struct gfs_holder *tmp_gh, *last_gh = NULL;
+	int found = FALSE;
+
+	if (gfs_assert_warn(sdp, gh->gh_owner))
+		return;
+
+	for (head = &gl->gl_waiters3, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		tmp_gh = list_entry(tmp, struct gfs_holder, gh_list);
+		if (tmp_gh->gh_owner != gh->gh_owner)
+			continue;
+
+		gfs_assert_warn(sdp, test_bit(HIF_RECURSE, &tmp_gh->gh_iflags));
+
+		/* found more than one */
+		if (found)
+			return;
+
+		found = TRUE;
+		last_gh = tmp_gh;
+	}
+
+	/* found just one */
+	if (!gfs_assert_warn(sdp, found))
+		clear_bit(HIF_RECURSE, &last_gh->gh_iflags);
+}
+
+/**
+ * rq_mutex - process a mutex request in the queue
+ * @gh: the glock holder
+ *
+ * Returns: TRUE if the queue is blocked (always, since there can be only one
+ *      holder of the mutex).
+ *
+ * See lock_on_glock()
+ */
+
+static int
+rq_mutex(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+
+	list_del_init(&gh->gh_list);
+	/*  gh->gh_error never examined.  */
+	set_bit(GLF_LOCK, &gl->gl_flags);
+	complete(&gh->gh_wait);
+
+	return TRUE;
+}
+
+/**
+ * rq_promote - process a promote request in the queue
+ * @gh: the glock holder
+ *
+ * Acquire a new inter-node lock, or change a lock state to more restrictive.
+ *
+ * Returns: TRUE if the queue is blocked
+ */
+
+static int
+rq_promote(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+	int recurse;
+
+	if (!relaxed_state_ok(gl->gl_state, gh->gh_state, gh->gh_flags)) {
+		if (list_empty(&gl->gl_holders)) {
+			gl->gl_req_gh = gh;
+			set_bit(GLF_LOCK, &gl->gl_flags);
+			spin_unlock(&gl->gl_spin);
+
+			/* If we notice a lot of glocks in reclaim list, free
+			   up memory for 2 of them before locking a new one */
+			if (atomic_read(&sdp->sd_reclaim_count) >
+			    gfs_tune_get(sdp, gt_reclaim_limit) &&
+			    !(gh->gh_flags & LM_FLAG_PRIORITY)) {
+				gfs_reclaim_glock(sdp);
+				gfs_reclaim_glock(sdp);
+			}
+
+			glops->go_xmote_th(gl, gh->gh_state,
+					   gh->gh_flags);
+
+			spin_lock(&gl->gl_spin);
+		}
+		return TRUE;
+	}
+
+	if (list_empty(&gl->gl_holders)) {
+		set_bit(HIF_FIRST, &gh->gh_iflags);
+		set_bit(GLF_LOCK, &gl->gl_flags);
+		recurse = FALSE;
+	} else {
+		struct gfs_holder *next_gh;
+		if (gh->gh_flags & GL_LOCAL_EXCL)
+			return TRUE;
+		next_gh = list_entry(gl->gl_holders.next, struct gfs_holder, gh_list);
+		if (next_gh->gh_flags & GL_LOCAL_EXCL)
+			 return TRUE;
+		recurse = test_bit(HIF_RECURSE, &gh->gh_iflags);
+	}
+
+	list_move_tail(&gh->gh_list, &gl->gl_holders);
+	gh->gh_error = 0;
+	set_bit(HIF_HOLDER, &gh->gh_iflags);
+
+	if (recurse)
+		handle_recurse(gh);
+
+	complete(&gh->gh_wait);
+
+	return FALSE;
+}
+
+/**
+ * rq_demote - process a demote request in the queue
+ * @gh: the glock holder
+ *
+ * Returns: TRUE if the queue is blocked
+ *
+ * Unlock an inter-node lock, or change a lock state to less restrictive.
+ * If the glock is already the same as the holder's requested state, or is
+ *   UNLOCKED, no lock module request is required.
+ * Otherwise, we need to ask lock module to unlock or change locked state
+ *   of the glock.
+ * If requested state is UNLOCKED, or current glock state is SHARED or
+ *   DEFERRED (neither of which have a less restrictive state other than
+ *   UNLOCK), we call go_drop_th() to unlock the lock.
+ * Otherwise (i.e. requested is SHARED or DEFERRED, and current is EXCLUSIVE),
+ *   we can continue to hold the lock, and just ask for a new state;
+ *   we call go_xmote_th() to change state.
+ *
+ * Must be called with glock's gl->gl_spin locked.
+ */
+
+static int
+rq_demote(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+
+	if (!list_empty(&gl->gl_holders))
+		return TRUE;
+
+	if (gl->gl_state == gh->gh_state || gl->gl_state == LM_ST_UNLOCKED) {
+		list_del_init(&gh->gh_list);
+		gh->gh_error = 0;
+		spin_unlock(&gl->gl_spin);
+		if (test_bit(HIF_DEALLOC, &gh->gh_iflags))
+			gfs_holder_put(gh);
+		else
+			complete(&gh->gh_wait);
+		spin_lock(&gl->gl_spin);
+	} else {
+		gl->gl_req_gh = gh;
+		set_bit(GLF_LOCK, &gl->gl_flags);
+		spin_unlock(&gl->gl_spin);
+
+		if (gh->gh_state == LM_ST_UNLOCKED ||
+		    gl->gl_state != LM_ST_EXCLUSIVE)
+			/* Unlock */
+			glops->go_drop_th(gl);
+		else
+			/* Change state while holding lock */
+			glops->go_xmote_th(gl, gh->gh_state, gh->gh_flags);
+
+		spin_lock(&gl->gl_spin);
+	}
+
+	return FALSE;
+}
+
+/**
+ * rq_greedy - process a queued request to drop greedy status
+ * @gh: the glock holder
+ *
+ * Returns: TRUE if the queue is blocked
+ */
+
+static int
+rq_greedy(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+
+	list_del_init(&gh->gh_list);
+	/*  gh->gh_error never examined.  */
+	clear_bit(GLF_GREEDY, &gl->gl_flags);
+	spin_unlock(&gl->gl_spin);
+
+	gfs_holder_uninit(gh);
+	kfree(container_of(gh, struct greedy, gr_gh));
+
+	spin_lock(&gl->gl_spin);		
+
+	return FALSE;
+}
+
+/**
+ * run_queue - process holder structures on the glock's wait queues
+ * @gl: the glock
+ *
+ * Rules:
+ *   Caller must hold gl->gl_spin.
+ */
+
+static void
+run_queue(struct gfs_glock *gl)
+{
+	struct gfs_holder *gh;
+	int blocked = TRUE;
+
+	for (;;) {
+		/* Another process is manipulating the glock structure;
+		   we can't do anything now */
+		if (test_bit(GLF_LOCK, &gl->gl_flags))
+			break;
+
+		/* Waiting to manipulate the glock structure */
+		if (!list_empty(&gl->gl_waiters1)) {
+			gh = list_entry(gl->gl_waiters1.next,
+					struct gfs_holder, gh_list);
+
+			if (test_bit(HIF_MUTEX, &gh->gh_iflags))
+				blocked = rq_mutex(gh);
+			else
+				gfs_assert_warn(gl->gl_sbd, FALSE);
+
+		/* Waiting to demote the lock, or drop greedy status */
+		} else if (!list_empty(&gl->gl_waiters2) &&
+			   !test_bit(GLF_SKIP_WAITERS2, &gl->gl_flags)) {
+			gh = list_entry(gl->gl_waiters2.next,
+					struct gfs_holder, gh_list);
+
+			if (test_bit(HIF_DEMOTE, &gh->gh_iflags))
+				blocked = rq_demote(gh);
+			else if (test_bit(HIF_GREEDY, &gh->gh_iflags))
+				blocked = rq_greedy(gh);
+			else
+				gfs_assert_warn(gl->gl_sbd, FALSE);
+
+		/* Waiting to promote the lock */
+		} else if (!list_empty(&gl->gl_waiters3)) {
+			gh = list_entry(gl->gl_waiters3.next,
+					struct gfs_holder, gh_list);
+
+			if (test_bit(HIF_PROMOTE, &gh->gh_iflags))
+				blocked = rq_promote(gh);
+			else
+				gfs_assert_warn(gl->gl_sbd, FALSE);
+
+		} else
+			break;
+
+		if (blocked)
+			break;
+	}
+}
+
+/**
+ * lock_on_glock - acquire a local lock on a glock (structure)
+ * @gl: the glock
+ *
+ * Gives caller exclusive access to manipulate a glock structure.
+ * Has nothing to do with inter-node lock state or GL_LOCAL_EXCL!
+ *
+ * If structure already locked, places temporary holder structure on glock's
+ * wait-for-exclusive-access queue, and blocks until exclusive access granted.
+ */
+
+static void
+lock_on_glock(struct gfs_glock *gl)
+{
+	struct gfs_holder gh;
+
+	gfs_holder_init(gl, 0, 0, &gh);
+	set_bit(HIF_MUTEX, &gh.gh_iflags);
+
+	spin_lock(&gl->gl_spin);
+	if (test_and_set_bit(GLF_LOCK, &gl->gl_flags))
+		list_add_tail(&gh.gh_list, &gl->gl_waiters1);
+	else
+		complete(&gh.gh_wait);
+	spin_unlock(&gl->gl_spin);
+
+	wait_for_completion(&gh.gh_wait);
+	gfs_holder_uninit(&gh);
+}
+
+/**
+ * trylock_on_glock - try to acquire a local lock on a glock (structure)
+ * @gl: the glock
+ *
+ * Returns: TRUE if the glock is acquired
+ *
+ * Tries to give caller exclusive access to manipulate a glock structure.
+ * Has nothing to do with inter-node lock state or LOCAL_EXCL!
+ *
+ * If structure already locked, does not block to wait; returns FALSE.
+ */
+
+static int
+trylock_on_glock(struct gfs_glock *gl)
+{
+	int acquired = TRUE;
+
+	spin_lock(&gl->gl_spin);
+	if (test_and_set_bit(GLF_LOCK, &gl->gl_flags))
+		acquired = FALSE;
+	spin_unlock(&gl->gl_spin);
+
+	return acquired;
+}
+
+/**
+ * unlock_on_glock - release a local lock on a glock (structure)
+ * @gl: the glock
+ *
+ * Caller is done manipulating glock structure.
+ * Service any others waiting for exclusive access.
+ */
+
+static void
+unlock_on_glock(struct gfs_glock *gl)
+{
+	spin_lock(&gl->gl_spin);
+	clear_bit(GLF_LOCK, &gl->gl_flags);
+	run_queue(gl);
+	spin_unlock(&gl->gl_spin);
+}
+
+/**
+ * handle_callback - add a demote request to a lock's queue
+ * @gl: the glock
+ * @state: the state the caller wants us to change to
+ *
+ * Called when we learn that another node needs a lock held by this node,
+ *   or when this node simply wants to drop a lock as soon as it's done with
+ *   it (NOCACHE flag), or dump a glock out of glock cache (reclaim it).
+ *
+ * We are told the @state that will satisfy the needs of the caller, so
+ *   we can ask for a demote to that state.
+ *
+ * If another demote request is already on the queue for a different state, just
+ *   set its request to UNLOCK (and don't bother queueing a request for us).
+ *   This consolidates LM requests and moves the lock to the least restrictive
+ *   state, so it will be compatible with whatever reason we were called.
+ *   No need to be too smart here.  Demotes between the shared and deferred
+ *   states will often fail, so don't even try.
+ *
+ * Otherwise, queue a demote request to the requested state.
+ */
+
+static void
+handle_callback(struct gfs_glock *gl, unsigned int state)
+{
+	struct list_head *tmp, *head;
+	struct gfs_holder *gh, *new_gh = NULL;
+
+	if (gfs_assert_warn(gl->gl_sbd, state != LM_ST_EXCLUSIVE))
+		return;
+
+ restart:
+	spin_lock(&gl->gl_spin);
+
+	/* If another queued demote request is for a different state,
+	   set its request to UNLOCKED */
+	for (head = &gl->gl_waiters2, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		gh = list_entry(tmp, struct gfs_holder, gh_list);
+		if (test_bit(HIF_DEMOTE, &gh->gh_iflags) &&
+		    gl->gl_req_gh != gh) {
+			if (gh->gh_state != state)
+				gh->gh_state = LM_ST_UNLOCKED;
+			goto out;
+		}
+	}
+
+	/* pass 2; add new holder to glock's demote request queue */
+	if (new_gh) {
+		list_add_tail(&new_gh->gh_list, &gl->gl_waiters2);
+		new_gh = NULL;
+
+	/* pass 1; set up a new holder struct for a demote request, then
+	   check again to see if another process added a demote request
+	   while we were preparing this one. */
+	} else {
+		spin_unlock(&gl->gl_spin);
+
+		RETRY_MALLOC(new_gh = gfs_holder_get(gl, state, LM_FLAG_TRY),
+			     new_gh);
+		set_bit(HIF_DEMOTE, &new_gh->gh_iflags);
+		set_bit(HIF_DEALLOC, &new_gh->gh_iflags);
+		new_gh->gh_owner = NULL;
+
+		goto restart;
+	}
+
+ out:
+	spin_unlock(&gl->gl_spin);
+
+	if (new_gh)
+		gfs_holder_put(new_gh);
+}
+
+/**
+ * state_change - record that the glock is now in a different state
+ * @gl: the glock
+ * @new_state the new state
+ *
+ */
+
+static void
+state_change(struct gfs_glock *gl, unsigned int new_state)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	int held1, held2;
+
+	held1 = (gl->gl_state != LM_ST_UNLOCKED);
+	held2 = (new_state != LM_ST_UNLOCKED);
+
+	if (held1 != held2) {
+		if (held2) {
+			atomic_inc(&sdp->sd_glock_held_count);
+			glock_hold(gl);
+		} else {
+			atomic_dec(&sdp->sd_glock_held_count);
+			glock_put(gl);
+		}
+	}
+
+	gl->gl_state = new_state;
+}
+
+/**
+ * xmote_bh - Called after the lock module is done acquiring a lock
+ * @gl: The glock in question
+ * @ret: the int returned from the lock module
+ *
+ */
+
+static void
+xmote_bh(struct gfs_glock *gl, unsigned int ret)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+	struct gfs_holder *gh = gl->gl_req_gh;
+	int prev_state = gl->gl_state;
+	int op_done = TRUE;
+
+	gfs_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags));
+	gfs_assert_warn(sdp, queue_empty(gl, &gl->gl_holders));
+	gfs_assert_warn(sdp, !(ret & LM_OUT_ASYNC));
+
+	state_change(gl, ret & LM_OUT_ST_MASK);
+
+	if (prev_state != LM_ST_UNLOCKED && !(ret & LM_OUT_CACHEABLE)) {
+		if (glops->go_inval)
+			glops->go_inval(gl, DIO_METADATA | DIO_DATA);
+	} else if (gl->gl_state == LM_ST_DEFERRED) {
+		/* We might not want to do this here.
+		   Look at moving to the inode glops. */
+		if (glops->go_inval)
+			glops->go_inval(gl, DIO_DATA);
+	}
+
+	/*  Deal with each possible exit condition  */
+
+	if (!gh)
+		gl->gl_stamp = jiffies;
+
+	else if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) {
+		spin_lock(&gl->gl_spin);
+		list_del_init(&gh->gh_list);
+		gh->gh_error = -EIO;
+		if (test_bit(HIF_RECURSE, &gh->gh_iflags))
+			do_unrecurse(gh);
+		spin_unlock(&gl->gl_spin);
+
+	} else if (test_bit(HIF_DEMOTE, &gh->gh_iflags)) {
+		spin_lock(&gl->gl_spin);
+		list_del_init(&gh->gh_list);
+		if (gl->gl_state == gh->gh_state ||
+		    gl->gl_state == LM_ST_UNLOCKED)
+			gh->gh_error = 0;
+		else {
+			if (gfs_assert_warn(sdp, gh->gh_flags &
+					    (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) == -1)
+				printk("GFS: fsid=%s: ret = 0x%.8X\n",
+				       sdp->sd_fsname, ret);
+			gh->gh_error = GLR_TRYFAILED;
+		}
+		spin_unlock(&gl->gl_spin);
+
+		if (ret & LM_OUT_CANCELED)
+			handle_callback(gl, LM_ST_UNLOCKED); /* Lame */
+
+	} else if (ret & LM_OUT_CANCELED) {
+		spin_lock(&gl->gl_spin);
+		list_del_init(&gh->gh_list);
+		gh->gh_error = GLR_CANCELED;
+		if (test_bit(HIF_RECURSE, &gh->gh_iflags))
+			do_unrecurse(gh);
+		spin_unlock(&gl->gl_spin);
+
+	} else if (relaxed_state_ok(gl->gl_state, gh->gh_state, gh->gh_flags)) {
+		spin_lock(&gl->gl_spin);
+		list_move_tail(&gh->gh_list, &gl->gl_holders);
+		gh->gh_error = 0;
+		set_bit(HIF_HOLDER, &gh->gh_iflags);
+		spin_unlock(&gl->gl_spin);
+
+		set_bit(HIF_FIRST, &gh->gh_iflags);
+
+		op_done = FALSE;
+
+	} else if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) {
+		spin_lock(&gl->gl_spin);
+		list_del_init(&gh->gh_list);
+		gh->gh_error = GLR_TRYFAILED;
+		if (test_bit(HIF_RECURSE, &gh->gh_iflags))
+			do_unrecurse(gh);
+		spin_unlock(&gl->gl_spin);
+
+	} else {
+		if (gfs_assert_withdraw(sdp, FALSE) == -1)
+			printk("GFS: fsid=%s: ret = 0x%.8X\n",
+			       sdp->sd_fsname, ret);
+	}
+
+	if (glops->go_xmote_bh)
+		glops->go_xmote_bh(gl);
+
+	if (op_done) {
+		spin_lock(&gl->gl_spin);
+		gl->gl_req_gh = NULL;
+		gl->gl_req_bh = NULL;
+		clear_bit(GLF_LOCK, &gl->gl_flags);
+		run_queue(gl);
+		spin_unlock(&gl->gl_spin);
+	}
+
+	glock_put(gl);
+
+	if (gh) {
+		if (test_bit(HIF_DEALLOC, &gh->gh_iflags))
+			gfs_holder_put(gh);
+		else
+			complete(&gh->gh_wait);
+	}
+}
+
+/**
+ * gfs_glock_xmote_th - Call into the lock module to acquire or change a glock
+ * @gl: The glock in question
+ * @state: the requested state
+ * @flags: modifier flags to the lock call
+ *
+ * Used to acquire a new glock, or to change an already-acquired glock to
+ *   more/less restrictive state (other than LM_ST_UNLOCKED).
+ *
+ * *Not* used to unlock a glock; use gfs_glock_drop_th() for that.
+ */
+
+void
+gfs_glock_xmote_th(struct gfs_glock *gl, unsigned int state, int flags)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+	int lck_flags = flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB |
+				 LM_FLAG_NOEXP | LM_FLAG_ANY |
+				 LM_FLAG_PRIORITY);
+	unsigned int lck_ret;
+
+	gfs_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags));
+	gfs_assert_warn(sdp, queue_empty(gl, &gl->gl_holders));
+	gfs_assert_warn(sdp, state != LM_ST_UNLOCKED);
+	gfs_assert_warn(sdp, state != gl->gl_state);
+
+	/* Current state EX, may need to sync log/data/metadata to disk */
+	if (gl->gl_state == LM_ST_EXCLUSIVE) {
+		if (glops->go_sync)
+			glops->go_sync(gl, DIO_METADATA | DIO_DATA);
+	}
+
+	glock_hold(gl);
+	gl->gl_req_bh = xmote_bh;
+
+	atomic_inc(&sdp->sd_lm_lock_calls);
+
+	lck_ret = gfs_lm_lock(sdp, gl->gl_lock,
+			      gl->gl_state, state,
+			      lck_flags);
+
+	if (lck_ret & LM_OUT_ASYNC)
+		gfs_assert_warn(sdp, lck_ret == LM_OUT_ASYNC);
+	else
+		xmote_bh(gl, lck_ret);
+}
+
+/**
+ * drop_bh - Called after a lock module unlock completes
+ * @gl: the glock
+ * @ret: the return status
+ *
+ * Doesn't wake up the process waiting on the struct gfs_holder (if any)
+ * Doesn't drop the reference on the glock the top half took out
+ *
+ */
+
+static void
+drop_bh(struct gfs_glock *gl, unsigned int ret)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+	struct gfs_holder *gh = gl->gl_req_gh;
+
+	clear_bit(GLF_PREFETCH, &gl->gl_flags);
+
+	gfs_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags));
+	gfs_assert_warn(sdp, queue_empty(gl, &gl->gl_holders));
+	gfs_assert_warn(sdp, !ret);
+
+	state_change(gl, LM_ST_UNLOCKED);
+
+	if (glops->go_inval)
+		glops->go_inval(gl, DIO_METADATA | DIO_DATA);
+
+	if (gh) {
+		spin_lock(&gl->gl_spin);
+		list_del_init(&gh->gh_list);
+		gh->gh_error = 0;
+		spin_unlock(&gl->gl_spin);
+	}
+
+	if (glops->go_drop_bh)
+		glops->go_drop_bh(gl);
+
+	spin_lock(&gl->gl_spin);
+	gl->gl_req_gh = NULL;
+	gl->gl_req_bh = NULL;
+	clear_bit(GLF_LOCK, &gl->gl_flags);
+	run_queue(gl);
+	spin_unlock(&gl->gl_spin);
+
+	glock_put(gl);
+
+	if (gh) {
+		if (test_bit(HIF_DEALLOC, &gh->gh_iflags))
+			gfs_holder_put(gh);
+		else
+			complete(&gh->gh_wait);
+	}
+}
+
+/**
+ * gfs_glock_drop_th - call into the lock module to unlock a lock 
+ * @gl: the glock
+ *
+ */
+
+void
+gfs_glock_drop_th(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+	unsigned int ret;
+
+	gfs_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags));
+	gfs_assert_warn(sdp, queue_empty(gl, &gl->gl_holders));
+	gfs_assert_warn(sdp, gl->gl_state != LM_ST_UNLOCKED);
+
+	/* Leaving state EX, may need to sync log/data/metadata to disk */
+	if (gl->gl_state == LM_ST_EXCLUSIVE) {
+		if (glops->go_sync)
+			glops->go_sync(gl, DIO_METADATA | DIO_DATA);
+	}
+
+	glock_hold(gl);
+	gl->gl_req_bh = drop_bh;
+
+	atomic_inc(&sdp->sd_lm_unlock_calls);
+
+	ret = gfs_lm_unlock(sdp, gl->gl_lock, gl->gl_state);
+
+	if (!ret)
+		drop_bh(gl, ret);
+	else
+		gfs_assert_warn(sdp, ret == LM_OUT_ASYNC);
+}
+
+/**
+ * do_cancels - cancel requests for locks stuck waiting on an expire flag
+ * @gh: the LM_FLAG_PRIORITY holder waiting to acquire the lock
+ *
+ * Don't cancel GL_NOCANCEL requests.
+ */
+
+static void
+do_cancels(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+
+	spin_lock(&gl->gl_spin);
+
+	while (gl->gl_req_gh != gh &&
+	       !test_bit(HIF_HOLDER, &gh->gh_iflags) &&
+	       !list_empty(&gh->gh_list)) {
+		if (gl->gl_req_bh &&
+		    !(gl->gl_req_gh &&
+		      (gl->gl_req_gh->gh_flags & GL_NOCANCEL))) {
+			spin_unlock(&gl->gl_spin);
+			gfs_lm_cancel(gl->gl_sbd, gl->gl_lock);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ / 10);
+			spin_lock(&gl->gl_spin);
+		} else {
+			spin_unlock(&gl->gl_spin);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ / 10);
+			spin_lock(&gl->gl_spin);
+		}
+	}
+
+	spin_unlock(&gl->gl_spin);
+}
+
+/**
+ * glock_wait_internal - wait on a glock acquisition
+ * @gh: the glock holder
+ *
+ * Returns: 0 on success
+ */
+
+static int
+glock_wait_internal(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+	int error = 0;
+
+	if (test_bit(HIF_ABORTED, &gh->gh_iflags))
+		return -EIO;
+
+	if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) {
+		spin_lock(&gl->gl_spin);
+		if (gl->gl_req_gh != gh &&
+		    !test_bit(HIF_HOLDER, &gh->gh_iflags) &&
+		    !list_empty(&gh->gh_list)) {
+			list_del_init(&gh->gh_list);
+			gh->gh_error = GLR_TRYFAILED;
+			if (test_bit(HIF_RECURSE, &gh->gh_iflags))
+				do_unrecurse(gh);
+			run_queue(gl);
+			spin_unlock(&gl->gl_spin);
+			return GLR_TRYFAILED;
+		}
+		spin_unlock(&gl->gl_spin);
+	}
+
+	if (gh->gh_flags & LM_FLAG_PRIORITY)
+		do_cancels(gh);
+
+	wait_for_completion(&gh->gh_wait);
+
+	if (gh->gh_error)
+		return gh->gh_error;
+
+	gfs_assert_withdraw(sdp, test_bit(HIF_HOLDER, &gh->gh_iflags));
+	gfs_assert_withdraw(sdp, relaxed_state_ok(gl->gl_state,
+						  gh->gh_state,
+						  gh->gh_flags));
+
+	if (test_bit(HIF_FIRST, &gh->gh_iflags)) {
+		gfs_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags));
+
+		if (glops->go_lock) {
+			error = glops->go_lock(gl, gh->gh_flags);
+			if (error) {
+				spin_lock(&gl->gl_spin);
+				list_del_init(&gh->gh_list);
+				gh->gh_error = error;
+				if (test_and_clear_bit(HIF_RECURSE, &gh->gh_iflags))
+					do_unrecurse(gh);
+				spin_unlock(&gl->gl_spin);
+			}
+		}
+
+		spin_lock(&gl->gl_spin);
+		gl->gl_req_gh = NULL;
+		gl->gl_req_bh = NULL;
+		clear_bit(GLF_LOCK, &gl->gl_flags);
+		if (test_bit(HIF_RECURSE, &gh->gh_iflags))
+			handle_recurse(gh);
+		run_queue(gl);
+		spin_unlock(&gl->gl_spin);
+	}
+
+	return error;
+}
+
+/**
+ * add_to_queue - Add a holder to the wait-for-promotion queue or holder list
+ *       (according to recursion)
+ * @gh: the holder structure to add
+ *
+ * If the hold requestor's process already has a granted lock (on holder list),
+ *   and this new request is compatible, go ahead and grant it, adding this
+ *   new holder to the glock's holder list. 
+ *
+ * If the hold requestor's process has earlier requested a lock, and is still
+ *   waiting for it to be granted, and this new request is compatible with
+ *   the earlier one, they can be handled at the same time when the request
+ *   is finally granted.  Mark both (all) with RECURSE flags, and add new
+ *   holder to wait-for-promotion queue.
+ *
+ * If there is no previous holder from this process (on holder list or wait-
+ *   for-promotion queue), simply add new holder to wait-for-promotion queue.
+ */
+
+static void
+add_to_queue(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct list_head *tmp, *head;
+	struct gfs_holder *tmp_gh;
+
+	if (gh->gh_owner) {
+		/* Search through glock's holders list to see if this process
+		     already holds a granted lock. */
+		for (head = &gl->gl_holders, tmp = head->next;
+		     tmp != head;
+		     tmp = tmp->next) {
+			tmp_gh = list_entry(tmp, struct gfs_holder, gh_list);
+			if (tmp_gh->gh_owner == gh->gh_owner) {
+				/* Make sure pre-existing holder is compatible
+				   with this new one. */
+				if (gfs_assert_warn(sdp, (gh->gh_flags & LM_FLAG_ANY) ||
+						    !(tmp_gh->gh_flags & LM_FLAG_ANY)) ||
+				    gfs_assert_warn(sdp, (tmp_gh->gh_flags & GL_LOCAL_EXCL) ||
+						    !(gh->gh_flags & GL_LOCAL_EXCL)) ||
+				    gfs_assert_warn(sdp, relaxed_state_ok(gl->gl_state,
+									  gh->gh_state,
+									  gh->gh_flags)))
+					goto fail;
+
+				/* We're good!  Grant the hold. */
+				list_add_tail(&gh->gh_list, &gl->gl_holders);
+				set_bit(HIF_HOLDER, &gh->gh_iflags);
+
+				gh->gh_error = 0;
+				complete(&gh->gh_wait);
+
+				return;
+			}
+		}
+
+		/* If not, Search through glock's wait-for-promotion list to
+		   see if this process already is waiting for a grant. */
+		for (head = &gl->gl_waiters3, tmp = head->next;
+		     tmp != head;
+		     tmp = tmp->next) {
+			tmp_gh = list_entry(tmp, struct gfs_holder, gh_list);
+			if (tmp_gh->gh_owner == gh->gh_owner) {
+				/* Yes, make sure it is compatible with new */
+				if (gfs_assert_warn(sdp, test_bit(HIF_PROMOTE,
+								  &tmp_gh->gh_iflags)) ||
+				    gfs_assert_warn(sdp, (gh->gh_flags & LM_FLAG_ANY) ||
+						    !(tmp_gh->gh_flags & LM_FLAG_ANY)) ||
+				    gfs_assert_warn(sdp, (tmp_gh->gh_flags & GL_LOCAL_EXCL) ||
+						    !(gh->gh_flags & GL_LOCAL_EXCL)) ||
+				    gfs_assert_warn(sdp, relaxed_state_ok(tmp_gh->gh_state,
+									  gh->gh_state,
+									  gh->gh_flags)))
+					goto fail;
+
+				/* OK, make sure they're marked, so
+				 * when one gets granted, the other will too. */
+				set_bit(HIF_RECURSE, &gh->gh_iflags);
+				set_bit(HIF_RECURSE, &tmp_gh->gh_iflags);
+
+				list_add_tail(&gh->gh_list, &gl->gl_waiters3);
+
+				return;
+			}
+		}
+	}
+
+	/* Else, no recursion ...
+	   If high priority request, add to head of promote queue, else tail */
+	if (gh->gh_flags & LM_FLAG_PRIORITY)
+		list_add(&gh->gh_list, &gl->gl_waiters3);
+	else
+		list_add_tail(&gh->gh_list, &gl->gl_waiters3);
+
+	return;
+
+ fail:
+	set_bit(HIF_ABORTED, &gh->gh_iflags);
+}
+
+/**
+ * gfs_glock_nq - enqueue a struct gfs_holder onto a glock (acquire a glock)
+ * @gh: the holder structure
+ *
+ * if (gh->gh_flags & GL_ASYNC), this never returns an error
+ *
+ * Returns: 0, GLR_TRYFAILED, or errno on failure
+ *
+ * Rules:
+ *   @gh must not be already attached to a glock.
+ *   Don't ask for UNLOCKED state (use gfs_glock_dq() for that).
+ *   LM_FLAG_ANY (liberal) and GL_EXACT (restrictive) are mutually exclusive.
+ */
+
+int
+gfs_glock_nq(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	int error = 0;
+
+	atomic_inc(&sdp->sd_glock_nq_calls);
+
+ restart:
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) ||
+	    gfs_assert_warn(sdp, list_empty(&gh->gh_list)) ||
+	    gfs_assert_warn(sdp, gh->gh_state != LM_ST_UNLOCKED) ||
+	    gfs_assert_warn(sdp, (gh->gh_flags & (LM_FLAG_ANY | GL_EXACT)) !=
+			    (LM_FLAG_ANY | GL_EXACT))) {
+		set_bit(HIF_ABORTED, &gh->gh_iflags);
+		return -EIO;
+	}
+
+	set_bit(HIF_PROMOTE, &gh->gh_iflags);
+
+	spin_lock(&gl->gl_spin);
+	add_to_queue(gh);
+	run_queue(gl);
+	spin_unlock(&gl->gl_spin);
+
+	if (!(gh->gh_flags & GL_ASYNC)) {
+		error = glock_wait_internal(gh);
+		if (error == GLR_CANCELED) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ);
+			goto restart;
+		}
+	}
+
+	clear_bit(GLF_PREFETCH, &gl->gl_flags);
+
+	return error;
+}
+
+/**
+ * gfs_glock_poll - poll to see if an async request has been completed
+ * @gh: the holder
+ *
+ * Returns: TRUE if the request is ready to be gfs_glock_wait()ed on
+ */
+
+int
+gfs_glock_poll(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	int ready = FALSE;
+
+	gfs_assert_warn(gl->gl_sbd, gh->gh_flags & GL_ASYNC);
+
+	spin_lock(&gl->gl_spin);
+
+	if (test_bit(HIF_HOLDER, &gh->gh_iflags))
+		ready = TRUE;
+	else if (list_empty(&gh->gh_list)) {
+		if (gh->gh_error == GLR_CANCELED) {
+			spin_unlock(&gl->gl_spin);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ);
+			if (gfs_glock_nq(gh))
+				return TRUE;
+			return FALSE;
+		} else
+			ready = TRUE;
+	}
+
+	spin_unlock(&gl->gl_spin);
+
+	return ready;
+}
+
+/**
+ * gfs_glock_wait - wait for a lock acquisition that ended in a GLR_ASYNC
+ * @gh: the holder structure
+ *
+ * Returns: 0, GLR_TRYFAILED, or errno on failure
+ */
+
+int
+gfs_glock_wait(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	int error;
+
+        gfs_assert_warn(gl->gl_sbd, gh->gh_flags & GL_ASYNC);
+
+	error = glock_wait_internal(gh);
+	if (error == GLR_CANCELED) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ);
+		gh->gh_flags &= ~GL_ASYNC;
+		error = gfs_glock_nq(gh);
+	}
+
+	return error;
+}
+
+/**
+ * gfs_glock_dq - dequeue a struct gfs_holder from a glock (release a glock)
+ * @gh: the glock holder
+ *
+ * This releases a local process' hold on a glock, and services other waiters.
+ * If this is the last holder on this node, calls glock operation go_unlock(),
+ *    and go_sync() if requested by glock's GL_SYNC flag.
+ * If glock's GL_NOCACHE flag is set, requests demotion to unlock the inter-
+ *    node lock now, rather than caching the glock for later use.
+ * Otherwise, this function does *not* release the glock at inter-node scope.
+ *   The glock will stay in glock cache until:
+ *   --  This node uses it again (extending residence in glock cache), or
+ *   --  Another node asks (via callback) for the lock, or
+ *   --  The glock sits unused in glock cache for a while, and the cleanup
+ *         daemons (gfs_scand and gfs_glockd) reclaim it.
+ */
+
+void
+gfs_glock_dq(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+	struct list_head *pos;
+	struct gfs_holder *tmp_gh = NULL;
+	int count = 0;
+
+	atomic_inc(&gl->gl_sbd->sd_glock_dq_calls);
+
+	gfs_assert_withdraw(sdp, !queue_empty(gl, &gh->gh_list));
+	gfs_assert_withdraw(sdp, test_bit(HIF_HOLDER, &gh->gh_iflags));
+
+	if (gh->gh_flags & GL_SYNC)
+		set_bit(GLF_SYNC, &gl->gl_flags);
+
+	/* Don't cache glock; request demote to unlock at inter-node scope */
+	if (gh->gh_flags & GL_NOCACHE) {
+		list_for_each(pos, &gl->gl_holders) {
+			tmp_gh = list_entry(pos, struct gfs_holder, gh_list);
+			++count;
+		}
+		if (tmp_gh == gh && count == 1)
+		handle_callback(gl, LM_ST_UNLOCKED);
+	}
+
+	lock_on_glock(gl);
+
+	spin_lock(&gl->gl_spin);
+	list_del_init(&gh->gh_list);
+
+	/* If last holder, do appropriate glock operations, set cache timer */
+	if (list_empty(&gl->gl_holders)) {
+		spin_unlock(&gl->gl_spin);
+
+		if (glops->go_unlock)
+			glops->go_unlock(gl, gh->gh_flags);
+
+		/* Do "early" sync, if requested by holder */
+		if (test_bit(GLF_SYNC, &gl->gl_flags)) {
+			if (glops->go_sync)
+				glops->go_sync(gl,
+					       DIO_METADATA |
+					       DIO_DATA |
+					       DIO_INVISIBLE);
+		}
+
+		gl->gl_stamp = jiffies;
+
+		spin_lock(&gl->gl_spin);
+	}
+
+	clear_bit(GLF_LOCK, &gl->gl_flags);
+	run_queue(gl);
+	spin_unlock(&gl->gl_spin);
+}
+
+/**
+ * gfs_glock_prefetch - Try to prefetch a glock
+ * @gl: the glock
+ * @state: the state to prefetch in 
+ * @flags: flags passed to go_xmote_th()
+ *
+ * Bypass request queues of glock (i.e. no holder involved), and directly call
+ *   go_xmote_th() to ask lock module for lock, to put in glock cache for
+ *   later use.
+ *
+ * Will not prefetch the lock (no need to) if a process on this node is already
+ *   interested in the lock, or if it's sitting in glock cache in a compatible
+ *   state.
+ *
+ * Rules:
+ *   Don't ask for UNLOCKED state (use gfs_glock_dq() for that).
+ *   LM_FLAG_ANY (liberal) and GL_EXACT (restrictive) are mutually exclusive.
+ */
+
+void
+gfs_glock_prefetch(struct gfs_glock *gl, unsigned int state, int flags)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+
+	if (gfs_assert_warn(sdp, state != LM_ST_UNLOCKED) ||
+	    gfs_assert_warn(sdp, (flags & (LM_FLAG_ANY | GL_EXACT)) !=
+			    (LM_FLAG_ANY | GL_EXACT)))
+		return;
+
+	spin_lock(&gl->gl_spin);
+
+	/* Should we prefetch? */
+	if (test_bit(GLF_LOCK, &gl->gl_flags) ||
+	    !list_empty(&gl->gl_holders) ||
+	    !list_empty(&gl->gl_waiters1) ||
+	    !list_empty(&gl->gl_waiters2) ||
+	    !list_empty(&gl->gl_waiters3) ||
+	    relaxed_state_ok(gl->gl_state, state, flags)) {
+		spin_unlock(&gl->gl_spin);
+		return;
+	}
+
+	/* Let bottom half know we're prefetching, ask lock module for lock */
+	set_bit(GLF_PREFETCH, &gl->gl_flags);
+
+	if (gfs_assert_warn(sdp, !gl->gl_req_gh))
+		gl->gl_req_gh = NULL;
+	set_bit(GLF_LOCK, &gl->gl_flags);
+	spin_unlock(&gl->gl_spin);
+
+	glops->go_xmote_th(gl, state, flags);
+
+	atomic_inc(&gl->gl_sbd->sd_glock_prefetch_calls);
+}
+
+/**
+ * gfs_glock_force_drop - Force a glock to be uncached
+ * @gl: the glock
+ *
+ */
+
+void
+gfs_glock_force_drop(struct gfs_glock *gl)
+{
+	struct gfs_holder gh;
+
+	gfs_holder_init(gl, LM_ST_UNLOCKED, 0, &gh);
+	set_bit(HIF_DEMOTE, &gh.gh_iflags);
+	gh.gh_owner = NULL;
+
+	spin_lock(&gl->gl_spin);
+	list_add_tail(&gh.gh_list, &gl->gl_waiters2);
+	run_queue(gl);
+	spin_unlock(&gl->gl_spin);
+
+	wait_for_completion(&gh.gh_wait);
+	gfs_holder_uninit(&gh);
+}
+
+/**
+ * greedy_work -
+ * @data:
+ *
+ */
+
+static void
+greedy_work(void *data)
+{
+	struct greedy *gr = (struct greedy *)data;
+	struct gfs_holder *gh = &gr->gr_gh;
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+
+	clear_bit(GLF_SKIP_WAITERS2, &gl->gl_flags);
+
+	if (glops->go_greedy)
+		glops->go_greedy(gl);
+
+	spin_lock(&gl->gl_spin);
+
+	if (list_empty(&gl->gl_waiters2)) {
+		clear_bit(GLF_GREEDY, &gl->gl_flags);
+		spin_unlock(&gl->gl_spin);
+		gfs_holder_uninit(gh);
+		kfree(gr);
+	} else {
+		glock_hold(gl);
+		list_add_tail(&gh->gh_list, &gl->gl_waiters2);
+		run_queue(gl);
+		spin_unlock(&gl->gl_spin);
+		glock_put(gl);
+	}
+}
+
+/**
+ * gfs_glock_be_greedy -
+ * @gl:
+ * @time:
+ *
+ * Returns: 0 if go_greedy will be called, 1 otherwise
+ */
+
+int
+gfs_glock_be_greedy(struct gfs_glock *gl, unsigned int time)
+{
+	struct greedy *gr;
+	struct gfs_holder *gh;
+
+	if (!time ||
+	    gl->gl_sbd->sd_args.ar_localcaching ||
+	    test_and_set_bit(GLF_GREEDY, &gl->gl_flags))
+		return 1;
+
+	gr = kmalloc(sizeof(struct greedy), GFP_KERNEL);
+	if (!gr) {
+		clear_bit(GLF_GREEDY, &gl->gl_flags);
+		return 1;
+	}
+	gh = &gr->gr_gh;
+
+	gfs_holder_init(gl, 0, 0, gh);
+	set_bit(HIF_GREEDY, &gh->gh_iflags);
+	gh->gh_owner = NULL;
+	INIT_WORK(&gr->gr_work, greedy_work, gr);
+
+	set_bit(GLF_SKIP_WAITERS2, &gl->gl_flags);
+	schedule_delayed_work(&gr->gr_work, time);
+
+	return 0;
+}
+
+/**
+ * gfs_glock_nq_init - intialize a holder and enqueue it on a glock
+ * @gl: the glock 
+ * @state: the state we're requesting
+ * @flags: the modifier flags
+ * @gh: the holder structure
+ *
+ * Returns: 0, GLR_*, or errno
+ */
+
+int
+gfs_glock_nq_init(struct gfs_glock *gl, unsigned int state, int flags,
+		  struct gfs_holder *gh)
+{
+	int error;
+
+	gfs_holder_init(gl, state, flags, gh);
+
+	error = gfs_glock_nq(gh);
+	if (error)
+		gfs_holder_uninit(gh);
+
+	return error;
+}
+
+/**
+ * gfs_glock_dq_uninit - dequeue a holder from a glock and initialize it
+ * @gh: the holder structure
+ *
+ */
+
+void
+gfs_glock_dq_uninit(struct gfs_holder *gh)
+{
+	gfs_glock_dq(gh);
+	gfs_holder_uninit(gh);
+}
+
+/**
+ * gfs_glock_nq_num - acquire a glock based on lock number
+ * @sdp: the filesystem
+ * @number: the lock number
+ * @glops: the glock operations for the type of glock
+ * @state: the state to acquire the glock in
+ * @flags: modifier flags for the aquisition
+ * @gh: the struct gfs_holder
+ *
+ * Returns: errno
+ */
+
+int
+gfs_glock_nq_num(struct gfs_sbd *sdp,
+		 uint64_t number, struct gfs_glock_operations *glops,
+		 unsigned int state, int flags, struct gfs_holder *gh)
+{
+	struct gfs_glock *gl;
+	int error;
+
+	error = gfs_glock_get(sdp, number, glops, CREATE, &gl);
+	if (!error) {
+		error = gfs_glock_nq_init(gl, state, flags, gh);
+		glock_put(gl);
+	}
+
+	return error;
+}
+
+/**
+ * glock_compare - Compare two struct gfs_glock structures for sorting
+ * @arg_a: the first structure
+ * @arg_b: the second structure
+ *
+ */
+
+static int
+glock_compare(const void *arg_a, const void *arg_b)
+{
+	struct gfs_holder *gh_a = *(struct gfs_holder **)arg_a;
+	struct gfs_holder *gh_b = *(struct gfs_holder **)arg_b;
+	struct lm_lockname *a = &gh_a->gh_gl->gl_name;
+	struct lm_lockname *b = &gh_b->gh_gl->gl_name;
+	int ret = 0;
+
+	if (a->ln_number > b->ln_number)
+		ret = 1;
+	else if (a->ln_number < b->ln_number)
+		ret = -1;
+	else {
+		if (gh_a->gh_state == LM_ST_SHARED &&
+		    gh_b->gh_state == LM_ST_EXCLUSIVE)
+			ret = 1;
+		else if (!(gh_a->gh_flags & GL_LOCAL_EXCL) &&
+			 (gh_b->gh_flags & GL_LOCAL_EXCL))
+			ret = 1;
+	}
+
+	return ret;
+}
+
+/**
+ * nq_m_sync - synchonously acquire more than one glock in deadlock free order
+ * @num_gh: the number of structures
+ * @ghs: an array of struct gfs_holder structures
+ *
+ * Returns: 0 on success (all glocks acquired), errno on failure (no glocks acquired)
+ */
+
+static int
+nq_m_sync(unsigned int num_gh, struct gfs_holder *ghs, struct gfs_holder **p)
+{
+	unsigned int x;
+	int error = 0;
+
+	for (x = 0; x < num_gh; x++)
+		p[x] = &ghs[x];
+
+	gfs_sort(p, num_gh, sizeof(struct gfs_holder *), glock_compare);
+
+	for (x = 0; x < num_gh; x++) {
+		p[x]->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC);
+
+		error = gfs_glock_nq(p[x]);
+		if (error) {
+			while (x--)
+				gfs_glock_dq(p[x]);
+			break;
+		}
+	}
+
+	return error;
+}
+
+/**
+ * gfs_glock_nq_m - acquire multiple glocks
+ * @num_gh: the number of structures
+ * @ghs: an array of struct gfs_holder structures
+ *
+ * Figure out how big an impact this function has.  Either:
+ * 1) Replace this code with code that calls gfs_glock_prefetch()
+ * 2) Forget async stuff and just call nq_m_sync()
+ * 3) Leave it like it is
+ *
+ * Returns: 0 on success (all glocks acquired), errno on failure (no glocks acquired)
+ */
+
+int
+gfs_glock_nq_m(unsigned int num_gh, struct gfs_holder *ghs)
+{
+	int *e;
+	unsigned int x;
+	int borked = FALSE, serious = 0;
+	int error = 0;
+
+	if (!num_gh)
+		return 0;
+
+	/* For just one gh, do request synchronously */
+	if (num_gh == 1) {
+		ghs->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC);
+		return gfs_glock_nq(ghs);
+	}
+
+	/* using sizeof(struct gfs_holder *) instead of sizeof(int), because
+	 * we're also using this memory for nq_m_sync and ints should never be
+	 * larger than pointers.... I hope
+	 */
+	e = kmalloc(num_gh * sizeof(struct gfs_holder *), GFP_KERNEL);
+	if (!e)
+		return -ENOMEM;
+
+	/* Send off asynchronous requests */
+	for (x = 0; x < num_gh; x++) {
+		ghs[x].gh_flags |= LM_FLAG_TRY | GL_ASYNC;
+		error = gfs_glock_nq(&ghs[x]);
+		if (error) {
+			borked = TRUE;
+			serious = error;
+			num_gh = x;
+			break;
+		}
+	}
+
+	/* Wait for all to complete */
+	for (x = 0; x < num_gh; x++) {
+		error = e[x] = glock_wait_internal(&ghs[x]);
+		if (error) {
+			borked = TRUE;
+			if (error != GLR_TRYFAILED && error != GLR_CANCELED)
+				serious = error;
+		}
+	}
+
+	/* If all good, done! */
+	if (!borked) {
+		kfree(e);
+		return 0;
+	}
+
+	for (x = 0; x < num_gh; x++)
+		if (!e[x])
+			gfs_glock_dq(&ghs[x]);
+
+	if (serious)
+		error = serious;
+	else {
+		for (x = 0; x < num_gh; x++)
+			gfs_holder_reinit(ghs[x].gh_state, ghs[x].gh_flags,
+					  &ghs[x]);
+		error = nq_m_sync(num_gh, ghs, (struct gfs_holder **)e);
+	}
+
+	kfree(e);
+	return error;
+}
+
+/**
+ * gfs_glock_dq_m - release multiple glocks
+ * @num_gh: the number of structures
+ * @ghs: an array of struct gfs_holder structures
+ *
+ */
+
+void
+gfs_glock_dq_m(unsigned int num_gh, struct gfs_holder *ghs)
+{
+	unsigned int x;
+
+	for (x = 0; x < num_gh; x++)
+		gfs_glock_dq(&ghs[x]);
+}
+
+/**
+ * gfs_glock_prefetch_num - prefetch a glock based on lock number
+ * @sdp: the filesystem
+ * @number: the lock number
+ * @glops: the glock operations for the type of glock
+ * @state: the state to acquire the glock in
+ * @flags: modifier flags for the aquisition
+ *
+ * Returns: errno
+ */
+
+void
+gfs_glock_prefetch_num(struct gfs_sbd *sdp,
+		       uint64_t number, struct gfs_glock_operations *glops,
+		       unsigned int state, int flags)
+{
+	struct gfs_glock *gl;
+	int error;
+
+	if (atomic_read(&sdp->sd_reclaim_count) < gfs_tune_get(sdp, gt_reclaim_limit)) {
+		error = gfs_glock_get(sdp, number, glops, CREATE, &gl);
+		if (!error) {
+			gfs_glock_prefetch(gl, state, flags);
+			glock_put(gl);
+		}
+	}
+}
+
+/**
+ * gfs_lvb_hold - attach a LVB from a glock
+ * @gl: The glock in question
+ *
+ */
+
+int
+gfs_lvb_hold(struct gfs_glock *gl)
+{
+	int error;
+
+	lock_on_glock(gl);
+
+	if (!atomic_read(&gl->gl_lvb_count)) {
+		gfs_assert_warn(gl->gl_sbd, !gl->gl_lvb);
+		error = gfs_lm_hold_lvb(gl->gl_sbd, gl->gl_lock, &gl->gl_lvb);
+		if (error) {
+			unlock_on_glock(gl);
+			return error;
+		}
+		glock_hold(gl);
+	}
+	atomic_inc(&gl->gl_lvb_count);
+
+	unlock_on_glock(gl);
+
+	return 0;
+}
+
+/**
+ * gfs_lvb_unhold - detach a LVB from a glock
+ * @gl: The glock in question
+ * 
+ */
+
+void
+gfs_lvb_unhold(struct gfs_glock *gl)
+{
+	glock_hold(gl);
+
+	lock_on_glock(gl);
+
+	if (!gfs_assert_warn(gl->gl_sbd, atomic_read(&gl->gl_lvb_count) > 0) &&
+	    atomic_dec_and_test(&gl->gl_lvb_count)) {
+		gfs_assert_warn(gl->gl_sbd, gl->gl_lvb);
+		gfs_lm_unhold_lvb(gl->gl_sbd, gl->gl_lock, gl->gl_lvb);
+		gl->gl_lvb = NULL;
+		glock_put(gl);
+	}
+
+	unlock_on_glock(gl);
+
+	glock_put(gl);
+}
+
+/**
+ * gfs_lvb_sync - sync a LVB
+ * @gl: The glock in question
+ * 
+ */
+
+void
+gfs_lvb_sync(struct gfs_glock *gl)
+{
+	if (gfs_assert_warn(gl->gl_sbd, atomic_read(&gl->gl_lvb_count)))
+		return;
+
+	lock_on_glock(gl);
+
+	if (!gfs_assert_warn(gl->gl_sbd, gfs_glock_is_held_excl(gl)))
+		gfs_lm_sync_lvb(gl->gl_sbd, gl->gl_lock, gl->gl_lvb);
+
+	unlock_on_glock(gl);
+}
+
+/**
+ * blocking_cb -
+ * @sdp:
+ * @name:
+ * @state:
+ *
+ */
+
+void
+blocking_cb(struct gfs_sbd *sdp, struct lm_lockname *name, unsigned int state)
+{
+	struct gfs_glock *gl;
+
+	gl = gfs_glock_find(sdp, name);
+	if (!gl)
+		return;
+
+	if (gl->gl_ops->go_callback)
+		gl->gl_ops->go_callback(gl, state);
+	handle_callback(gl, state);
+
+	spin_lock(&gl->gl_spin);
+	run_queue(gl);
+	spin_unlock(&gl->gl_spin);
+
+	glock_put(gl);
+}
+
+/**
+ * gfs_glock_cb - Callback used by locking module
+ * @fsdata: Pointer to the superblock
+ * @type: Type of callback
+ * @data: Type dependent data pointer
+ *
+ * Called by the locking module when it wants to tell us something.
+ * Either we need to drop a lock, one of our ASYNC requests completed, or
+ *   another client expired (crashed/died) and we need to recover its journal.
+ * If another node needs a lock held by this node, we queue a request to demote
+ *   our lock to a state compatible with that needed by the other node.  
+ *   For example, if the other node needs EXCLUSIVE, we request UNLOCKED.
+ *   SHARED and DEFERRED modes can be shared with other nodes, so we request
+ *   accordingly.
+ * Once all incompatible holders on this node are done with the lock, the
+ *   queued request will cause run_queue() to call the lock module to demote
+ *   our lock to a compatible state, allowing the other node to grab the lock.
+ */
+
+void
+gfs_glock_cb(lm_fsdata_t *fsdata, unsigned int type, void *data)
+{
+	struct gfs_sbd *sdp = (struct gfs_sbd *)fsdata;
+
+	atomic_inc(&sdp->sd_lm_callbacks);
+
+	switch (type) {
+	case LM_CB_NEED_E:
+		blocking_cb(sdp, (struct lm_lockname *)data, LM_ST_UNLOCKED);
+		return;
+
+	case LM_CB_NEED_D:
+		blocking_cb(sdp, (struct lm_lockname *)data, LM_ST_DEFERRED);
+		return;
+
+	case LM_CB_NEED_S:
+		blocking_cb(sdp, (struct lm_lockname *)data, LM_ST_SHARED);
+		return;
+
+	case LM_CB_ASYNC: {
+		struct lm_async_cb *async = (struct lm_async_cb *)data;
+		struct gfs_glock *gl;
+
+		gl = gfs_glock_find(sdp, &async->lc_name);
+		if (gfs_assert_warn(sdp, gl))
+			return;
+		if (!gfs_assert_warn(sdp, gl->gl_req_bh))
+			gl->gl_req_bh(gl, async->lc_ret);
+		glock_put(gl);
+
+		return;
+	}
+
+	case LM_CB_NEED_RECOVERY:
+		gfs_add_dirty_j(sdp, *(unsigned int *)data);
+		if (test_bit(SDF_RECOVERD_RUN, &sdp->sd_flags))
+			wake_up_process(sdp->sd_recoverd_process);
+		return;
+
+	case LM_CB_DROPLOCKS:
+		gfs_gl_hash_clear(sdp, FALSE);
+		gfs_quota_scan(sdp);
+		return;
+
+	default:
+		gfs_assert_warn(sdp, FALSE);
+		return;
+	}
+}
+
+/**
+ * gfs_try_toss_inode - try to remove a particular GFS inode struct from cache
+ * sdp: the filesystem
+ * inum: the inode number
+ *
+ * Look for the glock protecting the inode of interest.
+ * If no process is manipulating or holding the glock, see if the glock
+ *   has a gfs_inode attached.
+ * If gfs_inode has no references, unhold its iopen glock, release any
+ *   indirect addressing buffers, and destroy the gfs_inode.
+ */
+
+void
+gfs_try_toss_inode(struct gfs_sbd *sdp, struct gfs_inum *inum)
+{
+	struct gfs_glock *gl;
+	struct gfs_inode *ip;
+	int error;
+
+	error = gfs_glock_get(sdp,
+			      inum->no_formal_ino, &gfs_inode_glops,
+			      NO_CREATE, &gl);
+	if (error || !gl)
+		return;
+
+	if (!trylock_on_glock(gl))
+		goto out;
+
+	if (!queue_empty(gl, &gl->gl_holders))
+		goto out_unlock;
+
+	ip = gl2ip(gl);
+	if (!ip)
+		goto out_unlock;
+
+	if (atomic_read(&ip->i_count))
+		goto out_unlock;
+
+	gfs_inode_destroy(ip);
+
+ out_unlock:
+	unlock_on_glock(gl);
+
+ out:
+	glock_put(gl);
+}
+
+/**
+ * gfs_iopen_go_callback - Try to kick the inode/vnode associated with an iopen glock from memory
+ * @io_gl: the iopen glock
+ * @state: the state into which the glock should be put
+ *
+ */
+
+void
+gfs_iopen_go_callback(struct gfs_glock *io_gl, unsigned int state)
+{
+	struct gfs_glock *i_gl;
+	struct gfs_inode *ip;
+
+	if (state != LM_ST_UNLOCKED)
+		return;
+
+	spin_lock(&io_gl->gl_spin);
+	i_gl = gl2gl(io_gl);
+	if (i_gl) {
+		glock_hold(i_gl);
+		spin_unlock(&io_gl->gl_spin);
+	} else {
+		spin_unlock(&io_gl->gl_spin);
+		return;
+	}
+
+	if (trylock_on_glock(i_gl)) {
+		if (queue_empty(i_gl, &i_gl->gl_holders)) {
+			ip = gl2ip(i_gl);
+			if (ip) {
+				gfs_try_toss_vnode(ip);
+				unlock_on_glock(i_gl);
+				gfs_glock_schedule_for_reclaim(i_gl);
+				goto out;
+			}
+		}
+		unlock_on_glock(i_gl);
+	}
+
+ out:
+	glock_put(i_gl);
+}
+
+/**
+ * demote_ok - Check to see if it's ok to unlock a glock (to remove it
+ *       from glock cache)
+ * @gl: the glock
+ *
+ * Called when trying to reclaim glocks, once it's determined that the glock
+ *   has no holders on this node.
+ *
+ * Returns: TRUE if it's ok
+ *
+ * It's not okay if:
+ * --  glock is STICKY
+ * --  PREFETCHed glock has not been given enough chance to be used
+ * --  glock-type-specific test says "no"
+ */
+
+static int
+demote_ok(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock_operations *glops = gl->gl_ops;
+	int demote = TRUE;
+
+	if (test_bit(GLF_STICKY, &gl->gl_flags))
+		demote = FALSE;
+	else if (test_bit(GLF_PREFETCH, &gl->gl_flags))
+		demote = time_after_eq(jiffies,
+				       gl->gl_stamp +
+				       gfs_tune_get(sdp, gt_prefetch_secs) * HZ);
+	else if (glops->go_demote_ok)
+		demote = glops->go_demote_ok(gl);
+
+	return demote;
+}
+
+/**
+ * gfs_glock_schedule_for_reclaim - Add a glock to the reclaim list
+ * @gl: the glock
+ *
+ */
+
+void
+gfs_glock_schedule_for_reclaim(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+
+	spin_lock(&sdp->sd_reclaim_lock);
+	if (list_empty(&gl->gl_reclaim)) {
+		glock_hold(gl);
+		list_add(&gl->gl_reclaim, &sdp->sd_reclaim_list);
+		atomic_inc(&sdp->sd_reclaim_count);
+	}
+	spin_unlock(&sdp->sd_reclaim_lock);
+
+	wake_up(&sdp->sd_reclaim_wchan);
+}
+
+/**
+ * gfs_reclaim_glock - process the next glock on the filesystem's reclaim list
+ * @sdp: the filesystem
+ *
+ * Called from gfs_glockd() glock reclaim daemon, or when promoting a
+ *   (different) glock and we notice that there are a lot of glocks in the
+ *   reclaim list.
+ *
+ * Remove glock from filesystem's reclaim list, update reclaim statistics.
+ * If no holders (might have gotten added since glock was placed on reclaim
+ *   list):
+ *   --  Destroy any now-unused inode protected by glock
+ *         (and release hold on iopen glock).
+ *   --  Ask for demote to UNLOCKED to enable removal of glock from glock cache.
+ *
+ * If no further interest in glock struct, remove it from glock cache, and
+ *   free it from memory.  (During normal operation, this is the only place
+ *   that this is done).
+ *
+ * Glock-type-specific considerations for permission to demote are handled
+ *   in demote_ok().  This includes how long to retain a glock in cache after it
+ *   is no longer held by any process.
+ */
+
+void
+gfs_reclaim_glock(struct gfs_sbd *sdp)
+{
+	struct gfs_glock *gl;
+	struct gfs_gl_hash_bucket *bucket;
+
+	spin_lock(&sdp->sd_reclaim_lock);
+
+	/* Nothing to reclaim?  Done! */
+	if (list_empty(&sdp->sd_reclaim_list)) {
+		spin_unlock(&sdp->sd_reclaim_lock);
+		return;
+	}
+
+	/* Remove next victim from reclaim list */
+	gl = list_entry(sdp->sd_reclaim_list.next,
+			struct gfs_glock, gl_reclaim);
+	list_del_init(&gl->gl_reclaim);
+
+	spin_unlock(&sdp->sd_reclaim_lock);
+
+	atomic_dec(&sdp->sd_reclaim_count);
+	atomic_inc(&sdp->sd_reclaimed);
+
+	if (trylock_on_glock(gl)) {
+		if (queue_empty(gl, &gl->gl_holders)) {
+			/* Inode glock-type-specific; free unused gfs inode,
+			   and release hold on iopen glock */
+			if (gl->gl_ops == &gfs_inode_glops) {
+				struct gfs_inode *ip = gl2ip(gl);
+				if (ip && !atomic_read(&ip->i_count))
+					gfs_inode_destroy(ip);
+			}
+			/* Generic (including inodes); try to unlock glock */
+			if (gl->gl_state != LM_ST_UNLOCKED &&
+			    demote_ok(gl))
+				handle_callback(gl, LM_ST_UNLOCKED);
+		}
+		unlock_on_glock(gl);
+	}
+
+	bucket = gl->gl_bucket;
+
+	/* If glock struct's only remaining reference is from being put on
+	   the reclaim list, remove glock from hash table (sd_gl_hash),
+	   and free the glock's memory */
+	write_lock(&bucket->hb_lock);
+	if (atomic_read(&gl->gl_count) == 1) {
+		list_del_init(&gl->gl_list);
+		write_unlock(&bucket->hb_lock);
+		glock_free(gl);
+	} else {
+		write_unlock(&bucket->hb_lock);
+		glock_put(gl);  /* see gfs_glock_schedule_for_reclaim() */
+	}
+}
+
+/**
+ * examine_bucket - Call a function for glock in a hash bucket
+ * @examiner: the function 
+ * @sdp: the filesystem
+ * @bucket: the bucket
+ *
+ * Returns: TRUE if the bucket has entries
+ */
+
+static int
+examine_bucket(glock_examiner examiner,
+	       struct gfs_sbd *sdp, struct gfs_gl_hash_bucket *bucket)
+{
+	struct glock_plug plug;
+	struct list_head *tmp;
+	struct gfs_glock *gl;
+	int entries;
+
+	/* Add "plug" to end of bucket list, work back up list from there */
+	memset(&plug.gl_flags, 0, sizeof(unsigned long));
+	set_bit(GLF_PLUG, &plug.gl_flags);
+
+	write_lock(&bucket->hb_lock);
+	list_add(&plug.gl_list, &bucket->hb_list);
+	write_unlock(&bucket->hb_lock);
+
+	/* Look at each bucket entry */
+	for (;;) {
+		write_lock(&bucket->hb_lock);
+
+		/* Work back up list from plug */
+		for (;;) {
+			tmp = plug.gl_list.next;
+
+			/* Top of list; we're done */
+			if (tmp == &bucket->hb_list) {
+				list_del(&plug.gl_list);
+				entries = !list_empty(&bucket->hb_list);
+				write_unlock(&bucket->hb_lock);
+				return entries;
+			}
+			gl = list_entry(tmp, struct gfs_glock, gl_list);
+
+			/* Move plug up list */
+			list_move(&plug.gl_list, &gl->gl_list);
+
+			if (test_bit(GLF_PLUG, &gl->gl_flags))
+				continue;
+
+			/* glock_hold; examiner must glock_put() */
+			atomic_inc(&gl->gl_count);
+
+			break;
+		}
+
+		write_unlock(&bucket->hb_lock);
+
+		examiner(gl);
+	}
+}
+
+/**
+ * scan_glock - look at a glock and see if we can reclaim it
+ * @gl: the glock to look at
+ *
+ * Called via examine_bucket() when trying to release glocks from glock cache,
+ *   during normal operation (i.e. not unmount time).
+ * 
+ * Place glock on filesystem's reclaim list if, on this node:
+ * --  No process is manipulating glock struct, and
+ * --  No current holders, and either:
+ *     --  GFS incore inode, protected by glock, is no longer in use, or
+ *     --  Glock-type-specific demote_ok glops gives permission
+ */
+
+static void
+scan_glock(struct gfs_glock *gl)
+{
+	if (trylock_on_glock(gl)) {
+		if (queue_empty(gl, &gl->gl_holders)) {
+			/* Inode glock-type-specific; reclaim glock if gfs inode
+			   no longer in use. */
+			if (gl->gl_ops == &gfs_inode_glops) {
+				struct gfs_inode *ip = gl2ip(gl);
+				if (ip && !atomic_read(&ip->i_count)) {
+					unlock_on_glock(gl);
+					gfs_glock_schedule_for_reclaim(gl);
+					goto out;
+				}
+			}
+			/* Generic (including inodes not scheduled above) */
+			if (gl->gl_state != LM_ST_UNLOCKED &&
+			    demote_ok(gl)) {
+				unlock_on_glock(gl);
+				gfs_glock_schedule_for_reclaim(gl);
+				goto out;
+			}
+		}
+
+		unlock_on_glock(gl);
+	}
+
+ out:
+	glock_put(gl);  /* see examine_bucket() */
+}
+
+/**
+ * gfs_scand_internal - Look for glocks and inodes to toss from memory
+ * @sdp: the filesystem
+ *
+ * Invokes scan_glock() for each glock in each cache bucket.
+ *
+ * Steps of reclaiming a glock:
+ * --  scan_glock() places eligible glocks on filesystem's reclaim list.
+ * --  gfs_reclaim_glock() processes list members, attaches demotion requests
+ *     to wait queues of glocks still locked at inter-node scope.
+ * --  Demote to UNLOCKED state (if not already unlocked).
+ * --  gfs_reclaim_lock() cleans up glock structure.
+ */
+
+void
+gfs_scand_internal(struct gfs_sbd *sdp)
+{
+	unsigned int x;
+
+	for (x = 0; x < GFS_GL_HASH_SIZE; x++) {
+		examine_bucket(scan_glock, sdp, &sdp->sd_gl_hash[x]);
+		cond_resched();
+	}
+}
+
+/**
+ * clear_glock - look at a glock and see if we can free it from glock cache
+ * @gl: the glock to look at
+ *
+ * Called via examine_bucket() when unmounting the filesystem, or
+ *   when inter-node lock manager requests DROPLOCKS because it is running
+ *   out of capacity.
+ *
+ * Similar to gfs_reclaim_glock(), except does *not*:
+ *   --  Consult demote_ok() for permission
+ *   --  Increment sdp->sd_reclaimed statistic
+ *
+ */
+
+static void
+clear_glock(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_gl_hash_bucket *bucket = gl->gl_bucket;
+
+	spin_lock(&sdp->sd_reclaim_lock);
+	if (!list_empty(&gl->gl_reclaim)) {
+		list_del_init(&gl->gl_reclaim);
+		atomic_dec(&sdp->sd_reclaim_count);
+		glock_put(gl);  /* see gfs_glock_schedule_for_reclaim() */
+	}
+	spin_unlock(&sdp->sd_reclaim_lock);
+
+	if (trylock_on_glock(gl)) {
+		if (queue_empty(gl, &gl->gl_holders)) {
+			/* Inode glock-type-specific; free unused gfs inode,
+			   and release hold on iopen glock */
+			if (gl->gl_ops == &gfs_inode_glops) {
+				struct gfs_inode *ip = gl2ip(gl);
+				if (ip && !atomic_read(&ip->i_count))
+					gfs_inode_destroy(ip);
+			}
+			/* Generic (including inodes); unlock glock */
+			if (gl->gl_state != LM_ST_UNLOCKED)
+				handle_callback(gl, LM_ST_UNLOCKED);
+		}
+
+		unlock_on_glock(gl);
+	}
+
+	/* If glock struct's only remaining reference is from examine_bucket(),
+	   remove glock from hash table (sd_gl_hash), and free glock's memory */
+	write_lock(&bucket->hb_lock);
+	if (atomic_read(&gl->gl_count) == 1) {
+		list_del_init(&gl->gl_list);
+		write_unlock(&bucket->hb_lock);
+		glock_free(gl);
+	} else {
+		write_unlock(&bucket->hb_lock);
+		glock_put(gl);   /* see examine_bucket() */
+	}
+}
+
+/**
+ * gfs_gl_hash_clear - Empty out the glock hash table
+ * @sdp: the filesystem
+ * @wait: wait until it's all gone
+ *
+ * Called when unmounting the filesystem, or when inter-node lock manager
+ *   requests DROPLOCKS because it is running out of capacity.
+ */
+
+void
+gfs_gl_hash_clear(struct gfs_sbd *sdp, int wait)
+{
+	unsigned long t;
+	unsigned int x;
+	int cont;
+
+	t = jiffies;
+
+	for (;;) {
+		cont = FALSE;
+
+		for (x = 0; x < GFS_GL_HASH_SIZE; x++)
+			if (examine_bucket(clear_glock, sdp, &sdp->sd_gl_hash[x]))
+				cont = TRUE;
+
+		if (!wait || !cont)
+			break;
+
+		if (time_after_eq(jiffies, t + gfs_tune_get(sdp, gt_stall_secs) * HZ)) {
+			printk("GFS: fsid=%s: Unmount seems to be stalled. Dumping lock state...\n",
+			       sdp->sd_fsname);
+			gfs_dump_lockstate(sdp, NULL);
+			t = jiffies;
+		}
+
+		invalidate_inodes(sdp->sd_vfs);
+		yield();
+	}
+}
+
+/*
+ *  Diagnostic routines to help debug distributed deadlock
+ */
+
+/**
+ * dump_holder - print information about a glock holder
+ * @str: a string naming the type of holder
+ * @gh: the glock holder
+ * @buf: the buffer
+ * @size: the size of the buffer
+ * @count: where we are in the buffer
+ *
+ * Returns: 0 on success, -ENOBUFS when we run out of space
+ */
+
+static int
+dump_holder(char *str, struct gfs_holder *gh,
+	    char *buf, unsigned int size, unsigned int *count)
+{
+	unsigned int x;
+	int error = -ENOBUFS;
+
+	gfs_printf("  %s\n", str);
+	gfs_printf("    owner = %ld\n",
+		   (gh->gh_owner) ? (long)gh->gh_owner->pid : -1);
+	gfs_printf("    gh_state = %u\n", gh->gh_state);
+	gfs_printf("    gh_flags =");
+	for (x = 0; x < 32; x++)
+		if (gh->gh_flags & (1 << x))
+			gfs_printf(" %u", x);
+	gfs_printf(" \n");
+	gfs_printf("    error = %d\n", gh->gh_error);
+	gfs_printf("    gh_iflags =");
+	for (x = 0; x < 32; x++)
+		if (test_bit(x, &gh->gh_iflags))
+			gfs_printf(" %u", x);
+	gfs_printf(" \n");
+
+	error = 0;
+
+ out:
+	return error;
+}
+
+/**
+ * dump_inode - print information about an inode
+ * @ip: the inode
+ * @buf: the buffer
+ * @size: the size of the buffer
+ * @count: where we are in the buffer
+ *
+ * Returns: 0 on success, -ENOBUFS when we run out of space
+ */
+
+static int
+dump_inode(struct gfs_inode *ip,
+	   char *buf, unsigned int size, unsigned int *count)
+{
+	unsigned int x;
+	int error = -ENOBUFS;
+
+	gfs_printf("  Inode:\n");
+	gfs_printf("    num = %" PRIu64 "/%" PRIu64 "\n",
+		    ip->i_num.no_formal_ino, ip->i_num.no_addr);
+	gfs_printf("    type = %u\n", ip->i_di.di_type);
+	gfs_printf("    i_count = %d\n", atomic_read(&ip->i_count));
+	gfs_printf("    i_flags =");
+	for (x = 0; x < 32; x++)
+		if (test_bit(x, &ip->i_flags))
+			gfs_printf(" %u", x);
+	gfs_printf(" \n");
+	gfs_printf("    vnode = %s\n", (ip->i_vnode) ? "yes" : "no");
+
+	error = 0;
+
+ out:
+	return error;
+}
+
+/**
+ * dump_glock - print information about a glock
+ * @gl: the glock
+ * @buf: the buffer
+ * @size: the size of the buffer
+ * @count: where we are in the buffer
+ *
+ * Returns: 0 on success, -ENOBUFS when we run out of space
+ */
+
+static int
+dump_glock(struct gfs_glock *gl,
+	   char *buf, unsigned int size, unsigned int *count)
+{
+	struct list_head *head, *tmp;
+	struct gfs_holder *gh;
+	unsigned int x;
+	int error = -ENOBUFS;
+
+	spin_lock(&gl->gl_spin);
+
+	gfs_printf("Glock (%u, %" PRIu64 ")\n",
+		    gl->gl_name.ln_type,
+		    gl->gl_name.ln_number);
+	gfs_printf("  gl_flags =");
+	for (x = 0; x < 32; x++)
+		if (test_bit(x, &gl->gl_flags))
+			gfs_printf(" %u", x);
+	gfs_printf(" \n");
+	gfs_printf("  gl_count = %d\n", atomic_read(&gl->gl_count));
+	gfs_printf("  gl_state = %u\n", gl->gl_state);
+	gfs_printf("  req_gh = %s\n", (gl->gl_req_gh) ? "yes" : "no");
+	gfs_printf("  req_bh = %s\n", (gl->gl_req_bh) ? "yes" : "no");
+	gfs_printf("  lvb_count = %d\n", atomic_read(&gl->gl_lvb_count));
+	gfs_printf("  object = %s\n", (gl->gl_object) ? "yes" : "no");
+	gfs_printf("  new_le = %s\n", (gl->gl_new_le.le_trans) ? "yes" : "no");
+	gfs_printf("  incore_le = %s\n", (gl->gl_incore_le.le_trans) ? "yes" : "no");
+	gfs_printf("  reclaim = %s\n",
+		    (list_empty(&gl->gl_reclaim)) ? "no" : "yes");
+	if (gl->gl_aspace)
+		gfs_printf("  aspace = %lu\n",
+			    gl->gl_aspace->i_mapping->nrpages);
+	else
+		gfs_printf("  aspace = no\n");
+	gfs_printf("  ail_bufs = %s\n",
+		   (list_empty(&gl->gl_ail_bufs)) ? "no" : "yes");
+	if (gl->gl_req_gh) {
+		error = dump_holder("Request", gl->gl_req_gh, buf, size, count);
+		if (error)
+			goto out;
+	}
+	for (head = &gl->gl_holders, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		gh = list_entry(tmp, struct gfs_holder, gh_list);
+		error = dump_holder("Holder", gh, buf, size, count);
+		if (error)
+			goto out;
+	}
+	for (head = &gl->gl_waiters1, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		gh = list_entry(tmp, struct gfs_holder, gh_list);
+		error = dump_holder("Waiter1", gh, buf, size, count);
+		if (error)
+			goto out;
+	}
+	for (head = &gl->gl_waiters2, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		gh = list_entry(tmp, struct gfs_holder, gh_list);
+		error = dump_holder("Waiter2", gh, buf, size, count);
+		if (error)
+			goto out;
+	}
+	for (head = &gl->gl_waiters3, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		gh = list_entry(tmp, struct gfs_holder, gh_list);
+		error = dump_holder("Waiter3", gh, buf, size, count);
+		if (error)
+			goto out;
+	}
+	if (gl->gl_ops == &gfs_inode_glops && gl2ip(gl)) {
+		if (!test_bit(GLF_LOCK, &gl->gl_flags) &&
+		    list_empty(&gl->gl_holders)) {
+			error = dump_inode(gl2ip(gl), buf, size, count);
+			if (error)
+				goto out;
+		} else {
+			error = -ENOBUFS;
+			gfs_printf("  Inode: busy\n");
+		}
+	}
+
+	error = 0;
+
+ out:
+	spin_unlock(&gl->gl_spin);
+
+	return error;
+}
+
+/**
+ * gfs_dump_lockstate - print out the current lockstate
+ * @sdp: the filesystem
+ * @ub: the buffer to copy the information into
+ *
+ * If @ub is NULL, dump the lockstate to the console.
+ *
+ */
+
+int
+gfs_dump_lockstate(struct gfs_sbd *sdp, struct gfs_user_buffer *ub)
+{
+	struct gfs_gl_hash_bucket *bucket;
+	struct list_head *tmp, *head;
+	struct gfs_glock *gl;
+	char *buf = NULL;
+	unsigned int size = gfs_tune_get(sdp, gt_lockdump_size);
+	unsigned int x, count;
+	int error = 0;
+
+	if (ub) {
+		buf = kmalloc(size, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+	}
+
+	for (x = 0; x < GFS_GL_HASH_SIZE; x++) {
+		bucket = &sdp->sd_gl_hash[x];
+		count = 0;
+
+		read_lock(&bucket->hb_lock);
+
+		for (head = &bucket->hb_list, tmp = head->next;
+		     tmp != head;
+		     tmp = tmp->next) {
+			gl = list_entry(tmp, struct gfs_glock, gl_list);
+
+			if (test_bit(GLF_PLUG, &gl->gl_flags))
+				continue;
+
+			error = dump_glock(gl, buf, size, &count);
+			if (error)
+				break;
+		}
+
+		read_unlock(&bucket->hb_lock);
+
+		if (error)
+			break;
+
+		if (ub) {
+			if (ub->ub_count + count > ub->ub_size) {
+				error = -ENOMEM;
+				break;
+			}
+			if (copy_to_user(ub->ub_data + ub->ub_count, buf, count)) {
+				error = -EFAULT;
+				break;
+			}
+			ub->ub_count += count;
+		}
+	}
+
+	if (ub)
+		kfree(buf);
+
+	return error;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/glock.h linux-2.6.9.debug/fs/gfs/glock.h
--- linux-2.6.9.orig/fs/gfs/glock.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/glock.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,150 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __GFS_GLOCK_DOT_H__
+#define __GFS_GLOCK_DOT_H__
+
+/* Flags for lock requests; used in gfs_holder gh_flag field. */
+/*	These are defined in lm_interface.h, commented out here.
+#define LM_FLAG_TRY       (0x00000001)
+#define LM_FLAG_TRY_1CB   (0x00000002)
+#define LM_FLAG_NOEXP     (0x00000004)
+#define LM_FLAG_ANY       (0x00000008)
+#define LM_FLAG_PRIORITY  (0x00000010)
+	These are defined here. */
+#define GL_LOCAL_EXCL     (0x00000020) /* Only one holder may be granted the
+                                        * lock on this node, even if SHARED */
+#define GL_ASYNC          (0x00000040) /* Don't block waiting for lock ...
+                                        * must poll to wait for grant */
+#define GL_EXACT          (0x00000080) /* Requested state must == current state
+                                        * for lock to be granted */
+#define GL_SKIP           (0x00000100) /* Don't read from disk after grant */
+#define GL_ATIME          (0x00000200) /* Update inode's ATIME after grant */
+#define GL_NOCACHE        (0x00000400) /* Release glock when done, don't cache */
+#define GL_SYNC           (0x00000800) /* Sync to disk when no more holders */
+#define GL_NOCANCEL       (0x00001000) /* Don't ever cancel this request */
+
+#define GLR_TRYFAILED     (13)
+#define GLR_CANCELED      (14)
+
+static __inline__ int
+gfs_glock_is_locked_by_me(struct gfs_glock *gl)
+{
+	struct list_head *tmp, *head;
+	struct gfs_holder *gh;
+	int locked = FALSE;
+
+	/* Look in glock's list of holders for one with current task as owner */
+	spin_lock(&gl->gl_spin);
+	for (head = &gl->gl_holders, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		gh = list_entry(tmp, struct gfs_holder, gh_list);
+		if (gh->gh_owner == current) {
+			locked = TRUE;
+			break;
+		}
+	}
+	spin_unlock(&gl->gl_spin);
+
+	return locked;
+}
+static __inline__ int
+gfs_glock_is_held_excl(struct gfs_glock *gl)
+{
+	return (gl->gl_state == LM_ST_EXCLUSIVE);
+}
+static __inline__ int
+gfs_glock_is_held_dfrd(struct gfs_glock *gl)
+{
+	return (gl->gl_state == LM_ST_DEFERRED);
+}
+static __inline__ int
+gfs_glock_is_held_shrd(struct gfs_glock *gl)
+{
+	return (gl->gl_state == LM_ST_SHARED);
+}
+
+static __inline__ int
+gfs_glock_is_blocking(struct gfs_glock *gl)
+{
+	int ret;
+	spin_lock(&gl->gl_spin);
+	ret = !list_empty(&gl->gl_waiters2) || !list_empty(&gl->gl_waiters3);
+	spin_unlock(&gl->gl_spin);
+	return ret;
+}
+
+struct gfs_glock *gfs_glock_find(struct gfs_sbd *sdp,
+				 struct lm_lockname *name);
+int gfs_glock_get(struct gfs_sbd *sdp,
+		  uint64_t number, struct gfs_glock_operations *glops,
+		  int create, struct gfs_glock **glp);
+void gfs_glock_hold(struct gfs_glock *gl);
+void gfs_glock_put(struct gfs_glock *gl);
+
+void gfs_holder_init(struct gfs_glock *gl, unsigned int state, int flags,
+		     struct gfs_holder *gh);
+void gfs_holder_reinit(unsigned int state, int flags, struct gfs_holder *gh);
+void gfs_holder_uninit(struct gfs_holder *gh);
+struct gfs_holder *gfs_holder_get(struct gfs_glock *gl, unsigned int state,
+				  int flags);
+void gfs_holder_put(struct gfs_holder *gh);
+
+void gfs_glock_xmote_th(struct gfs_glock *gl, unsigned int state, int flags);
+void gfs_glock_drop_th(struct gfs_glock *gl);
+
+int gfs_glock_nq(struct gfs_holder *gh);
+int gfs_glock_poll(struct gfs_holder *gh);
+int gfs_glock_wait(struct gfs_holder *gh);
+void gfs_glock_dq(struct gfs_holder *gh);
+
+void gfs_glock_prefetch(struct gfs_glock *gl, unsigned int state, int flags);
+void gfs_glock_force_drop(struct gfs_glock *gl);
+
+int gfs_glock_be_greedy(struct gfs_glock *gl, unsigned int time);
+
+int gfs_glock_nq_init(struct gfs_glock *gl, unsigned int state, int flags,
+		      struct gfs_holder *gh);
+void gfs_glock_dq_uninit(struct gfs_holder *gh);
+int gfs_glock_nq_num(struct gfs_sbd *sdp,
+		     uint64_t number, struct gfs_glock_operations *glops,
+		     unsigned int state, int flags, struct gfs_holder *gh);
+
+int gfs_glock_nq_m(unsigned int num_gh, struct gfs_holder *ghs);
+void gfs_glock_dq_m(unsigned int num_gh, struct gfs_holder *ghs);
+
+void gfs_glock_prefetch_num(struct gfs_sbd *sdp,
+			    uint64_t number, struct gfs_glock_operations *glops,
+			    unsigned int state, int flags);
+
+/*  Lock Value Block functions  */
+
+int gfs_lvb_hold(struct gfs_glock *gl);
+void gfs_lvb_unhold(struct gfs_glock *gl);
+void gfs_lvb_sync(struct gfs_glock *gl);
+
+void gfs_glock_cb(lm_fsdata_t *fsdata, unsigned int type, void *data);
+
+void gfs_try_toss_inode(struct gfs_sbd *sdp, struct gfs_inum *inum);
+void gfs_iopen_go_callback(struct gfs_glock *gl, unsigned int state);
+
+void gfs_glock_schedule_for_reclaim(struct gfs_glock *gl);
+void gfs_reclaim_glock(struct gfs_sbd *sdp);
+
+void gfs_scand_internal(struct gfs_sbd *sdp);
+void gfs_gl_hash_clear(struct gfs_sbd *sdp, int wait);
+
+int gfs_dump_lockstate(struct gfs_sbd *sdp, struct gfs_user_buffer *ub);
+
+#endif /* __GFS_GLOCK_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/glops.c linux-2.6.9.debug/fs/gfs/glops.c
--- linux-2.6.9.orig/fs/gfs/glops.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/glops.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,675 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "log.h"
+#include "page.h"
+#include "recovery.h"
+#include "rgrp.h"
+
+/**
+ * meta_go_sync - sync out the metadata for this glock
+ * @gl: the glock
+ * @flags: DIO_*
+ *
+ * Used for meta and rgrp glocks.
+ *
+ * Called when demoting (gfs_glock_xmote_th()) or unlocking
+ * (gfs_glock_drop_th() an EX glock at inter-node scope.  We must flush
+ * to disk all dirty buffers/pages relating to this glock, and must not
+ * not return to caller to demote/unlock the glock until I/O is complete.
+ *
+ * This is *not* called from gfs_glock_dq(), because GL_SYNC flag is not
+ * currently used for anything but inode glocks.
+ */
+
+static void
+meta_go_sync(struct gfs_glock *gl, int flags)
+{
+	if (!(flags & DIO_METADATA))
+		return;
+
+	if (test_bit(GLF_DIRTY, &gl->gl_flags)) {
+		gfs_log_flush_glock(gl);
+		gfs_sync_buf(gl, flags | DIO_START | DIO_WAIT | DIO_CHECK);
+	}
+
+	/* We've synced everything, clear SYNC request and DIRTY flags */
+	clear_bit(GLF_DIRTY, &gl->gl_flags);
+	clear_bit(GLF_SYNC, &gl->gl_flags);
+}
+
+/**
+ * meta_go_inval - invalidate the metadata for this glock
+ * @gl: the glock
+ * @flags: 
+ *
+ */
+
+static void
+meta_go_inval(struct gfs_glock *gl, int flags)
+{
+	if (!(flags & DIO_METADATA))
+		return;
+
+	gfs_inval_buf(gl);
+	gl->gl_vn++;
+}
+
+/**
+ * meta_go_demote_ok - Check to see if it's ok to unlock a meta glock
+ * @gl: the glock
+ *
+ * Returns: TRUE if we have no cached data; ok to demote meta glock
+ *
+ * Called when trying to dump (reclaim) a glock from the glock cache, after
+ *   determining that there is currently no holder on this node for this glock,
+ *   and before placing LM_ST_UNLOCKED request on glock's wait-for-demote queue.
+ * Note that callbacks from other nodes that need a lock do *not*
+ *   seek permission from this function before requesting a demote.
+ *   Nor do glocks obtained with the following flags (see demote_ok()):
+ *   --  GL_NOCACHE:  gets unlocked (and not cached) immediately after use
+ *   --  GLF_STICKY:  equivalent to always getting "FALSE" from this function
+ *   --  GLF_PREFETCH:  uses its own timeout
+ *
+ * For glocks that protect on-disk data (meta, inode, and rgrp glocks), disk
+ *   accesses are slow, while lock manipulation is usually fast.  Releasing
+ *   a lock means that we:
+ *   --  Must sync memory-cached write data to disk immediately, before another
+ *       node can be granted the lock (at which point that node must read the
+ *       data from disk).
+ *   --  Must invalidate memory-cached data that we had read from or written
+ *       to disk.  Another node can change it if we don't have a lock, so it's
+ *       now useless to us.
+ *
+ * Then, if we re-acquire the lock again in the future, we:
+ *   --  Must (re-)read (perhaps unchanged) data from disk into memory.
+ *
+ * All of these are painful, so it pays to retain a glock in our glock cache
+ *   as long as we have cached data (even though we have no active holders
+ *   for this lock on this node currently), unless/until another node needs
+ *   to change it.  This allows Linux block I/O to sync write data to disk in
+ *   a "lazy" way, rather than forcing an immediate sync (and resultant WAIT),
+ *   and retains current data in memory as long as possible.
+ *
+ * This also helps GFS respond to memory pressure.  There is no mechanism for
+ *   the Linux virtual memory manager to directly call into GFS to ask it to
+ *   drop locks.  So, we take a hint from what the VM does to the page cache.
+ *   When that cache is trimmed (and we see no more pages relating to this
+ *   glock), we trim the glock cache as well, by releasing this lock.
+ */
+
+static int
+meta_go_demote_ok(struct gfs_glock *gl)
+{
+	return (gl->gl_aspace->i_mapping->nrpages) ? FALSE : TRUE;
+}
+
+/**
+ * inode_go_xmote_th - promote/demote (but don't unlock) an inode glock
+ * @gl: the glock
+ * @state: the requested state
+ * @flags: the flags passed into gfs_glock()
+ *
+ * Acquire a new glock, or change an already-acquired glock to
+ *   more/less restrictive state (other than LM_ST_UNLOCKED).
+ */
+
+static void
+inode_go_xmote_th(struct gfs_glock *gl, unsigned int state, int flags)
+{
+	if (gl->gl_state != LM_ST_UNLOCKED)
+		gfs_inval_pte(gl);
+	gfs_glock_xmote_th(gl, state, flags);
+}
+
+/**
+ * inode_go_xmote_bh - After promoting/demoting (but not unlocking)
+ *      an inode glock
+ * @gl: the glock
+ *
+ * FIXME: This will be really broken when (no_formal_ino != no_addr)
+ *        and gl_name.ln_number no longer refers to the dinode block #.
+ *
+ * If we've just acquired the inter-node lock for an inode,
+ *   read the dinode block from disk (but don't wait for I/O completion).
+ * Exceptions (don't read if):
+ *    Glock state is UNLOCKED.
+ *    Glock's requesting holder's GL_SKIP flag is set.
+ */
+
+static void
+inode_go_xmote_bh(struct gfs_glock *gl)
+{
+	struct gfs_holder *gh = gl->gl_req_gh;
+	struct buffer_head *bh;
+	int error;
+
+	if (gl->gl_state != LM_ST_UNLOCKED &&
+	    (!gh || !(gh->gh_flags & GL_SKIP))) {
+		error = gfs_dread(gl, gl->gl_name.ln_number, DIO_START, &bh);
+		if (!error)
+			brelse(bh);
+	}
+}
+
+/**
+ * inode_go_drop_th - unlock an inode glock
+ * @gl: the glock
+ *
+ * Invoked from rq_demote().
+ * Another node needs the lock in EXCLUSIVE mode, or lock (unused for too long)
+ *   is being purged from our node's glock cache; we're dropping lock.
+ */
+
+static void
+inode_go_drop_th(struct gfs_glock *gl)
+{
+	gfs_inval_pte(gl);
+	gfs_glock_drop_th(gl);
+}
+
+/**
+ * inode_go_sync - Sync the dirty data and/or metadata for an inode glock
+ * @gl: the glock protecting the inode
+ * @flags: DIO_METADATA -- sync inode's metadata
+ *         DIO_DATA     -- sync inode's data
+ *         DIO_INVISIBLE --  don't clear glock's DIRTY flag when done
+ *
+ * If DIO_INVISIBLE:
+ *   1) Called from gfs_glock_dq(), when releasing the last holder for an EX
+ *   glock (but glock is still in our glock cache in EX state, and might
+ *   stay there for a few minutes).  Holder had GL_SYNC flag set, asking
+ *   for early sync (i.e. now, instead of later when we release the EX at
+ *   inter-node scope).  GL_SYNC is currently used only for inodes in
+ *   special cases, so inode is the only type of glock for which
+ *   DIO_INVISIBLE would apply.
+ *   2) Called from depend_sync_one() to sync deallocated inode metadata
+ *   before it can be reallocated by another process or machine.  Since
+ *   this call can happen at any time during the lifetime of the
+ *   glock, don't clear the sync bit (more data might be dirtied
+ *   momentarily).
+ * Else (later):
+ *   Called when demoting (gfs_glock_xmote_th()) or unlocking
+ *   (gfs_glock_drop_th() an EX glock at inter-node scope.  We must flush
+ *   to disk all dirty buffers/pages relating to this glock, and must not
+ *   return to caller to demote/unlock the glock until I/O is complete.
+ *
+ * Syncs go in following order:
+ *   Start data page writes
+ *   Sync metadata to log (wait to complete I/O)
+ *   Sync metadata to in-place location (wait to complete I/O)
+ *   Wait for data page I/O to complete
+ * 
+ */
+
+static void
+inode_go_sync(struct gfs_glock *gl, int flags)
+{
+	int meta = (flags & DIO_METADATA);
+	int data = (flags & DIO_DATA);
+
+	if (test_bit(GLF_DIRTY, &gl->gl_flags)) {
+		if (meta && data) {
+			gfs_sync_page(gl, flags | DIO_START);
+			gfs_log_flush_glock(gl);
+			gfs_sync_buf(gl, flags | DIO_START | DIO_WAIT | DIO_CHECK);
+			gfs_sync_page(gl, flags | DIO_WAIT | DIO_CHECK);
+		} else if (meta) {
+			gfs_log_flush_glock(gl);
+			gfs_sync_buf(gl, flags | DIO_START | DIO_WAIT | DIO_CHECK);
+		} else if (data)
+			gfs_sync_page(gl, flags | DIO_START | DIO_WAIT | DIO_CHECK);
+	}
+
+	/* If we've synced everything, clear the SYNC request.
+	   If we're doing the final (not early) sync, clear DIRTY */
+	if (meta && data) {
+		if (!(flags & DIO_INVISIBLE))
+			clear_bit(GLF_DIRTY, &gl->gl_flags);
+		clear_bit(GLF_SYNC, &gl->gl_flags);
+	}
+}
+
+/**
+ * inode_go_inval - prepare a inode glock to be released
+ * @gl: the glock
+ * @flags: 
+ *
+ */
+
+static void
+inode_go_inval(struct gfs_glock *gl, int flags)
+{
+	int meta = (flags & DIO_METADATA);
+	int data = (flags & DIO_DATA);
+
+	if (meta) {
+		gfs_inval_buf(gl);
+		gl->gl_vn++;
+	}
+	if (data)
+		gfs_inval_page(gl);
+}
+
+/**
+ * inode_go_demote_ok - Check to see if it's ok to unlock an inode glock
+ * @gl: the glock
+ *
+ * See comments for meta_go_demote_ok().
+ *
+ * While other glock types (meta, rgrp) that protect disk data can be retained
+ *   indefinitely, GFS imposes a timeout (overridden when using no_lock lock
+ *   module) for inode glocks, even if there is still data in page cache for
+ *   this inode.
+ *
+ * Returns: TRUE if it's ok
+ */
+
+static int
+inode_go_demote_ok(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	int demote = FALSE;
+
+	if (!gl2ip(gl) && !gl->gl_aspace->i_mapping->nrpages)
+		demote = TRUE;
+	else if (!sdp->sd_args.ar_localcaching &&
+		 time_after_eq(jiffies, gl->gl_stamp + gfs_tune_get(sdp, gt_demote_secs) * HZ))
+		demote = TRUE;
+
+	return demote;
+}
+
+/**
+ * inode_go_lock - operation done after an inode lock is locked by
+ *      a first holder on this node
+ * @gl: the glock
+ * @flags: the flags passed into gfs_glock()
+ *
+ * Returns: errno
+ */
+
+static int
+inode_go_lock(struct gfs_glock *gl, int flags)
+{
+	struct gfs_inode *ip = gl2ip(gl);
+	int error = 0;
+
+	if (ip && ip->i_vn != gl->gl_vn) {
+		error = gfs_copyin_dinode(ip);
+		if (!error)
+			gfs_inode_attr_in(ip);
+	}
+
+	return error;
+}
+
+/**
+ * inode_go_unlock - operation done when an inode lock is unlocked by
+ *     a last holder on this node
+ * @gl: the glock
+ * @flags: the flags passed into gfs_gunlock()
+ *
+ */
+
+static void
+inode_go_unlock(struct gfs_glock *gl, int flags)
+{
+	struct gfs_inode *ip = gl2ip(gl);
+
+	if (ip && test_bit(GLF_DIRTY, &gl->gl_flags))
+		gfs_inode_attr_in(ip);
+
+	if (ip)
+		gfs_flush_meta_cache(ip);
+}
+
+/**
+ * inode_greedy -
+ * @gl: the glock
+ *
+ */
+
+static void
+inode_greedy(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_inode *ip = gl2ip(gl);
+	unsigned int quantum = gfs_tune_get(sdp, gt_greedy_quantum);
+	unsigned int max = gfs_tune_get(sdp, gt_greedy_max);
+	unsigned int new_time;
+
+	spin_lock(&ip->i_spin);
+
+	if (time_after(ip->i_last_pfault + quantum, jiffies)) {
+		new_time = ip->i_greedy + quantum;
+		if (new_time > max)
+			new_time = max;
+	} else {
+		new_time = ip->i_greedy - quantum;
+		if (!new_time || new_time > max)
+			new_time = 1;
+	}
+
+	ip->i_greedy = new_time;
+
+	spin_unlock(&ip->i_spin);
+
+	gfs_inode_put(ip);
+}
+
+/**
+ * rgrp_go_xmote_th - promote/demote (but don't unlock) a resource group glock
+ * @gl: the glock
+ * @state: the requested state
+ * @flags: the flags passed into gfs_glock()
+ *
+ * Acquire a new glock, or change an already-acquired glock to
+ *   more/less restrictive state (other than LM_ST_UNLOCKED).
+ *
+ * We're going to lock the lock in SHARED or EXCLUSIVE state, or
+ *   demote it from EXCLUSIVE to SHARED (because another node needs it SHARED).
+ * When locking, gfs_mhc_zap() and gfs_depend_sync() are basically no-ops;
+ *   meta-header cache and dependency lists should be empty.
+ *
+ */
+
+static void
+rgrp_go_xmote_th(struct gfs_glock *gl, unsigned int state, int flags)
+{
+	struct gfs_rgrpd *rgd = gl2rgd(gl);
+
+	gfs_mhc_zap(rgd);
+	gfs_depend_sync(rgd);
+	gfs_glock_xmote_th(gl, state, flags);
+}
+
+/**
+ * rgrp_go_drop_th - unlock a resource group glock
+ * @gl: the glock
+ *
+ * Invoked from rq_demote().
+ * Another node needs the lock in EXCLUSIVE mode, or lock (unused for too long)
+ *   is being purged from our node's glock cache; we're dropping lock.
+ */
+
+static void
+rgrp_go_drop_th(struct gfs_glock *gl)
+{
+	struct gfs_rgrpd *rgd = gl2rgd(gl);
+
+	gfs_mhc_zap(rgd);
+	gfs_depend_sync(rgd);
+	gfs_glock_drop_th(gl);
+}
+
+/**
+ * rgrp_go_demote_ok - Check to see if it's ok to unlock a RG's glock
+ * @gl: the glock
+ *
+ * See comments for meta_go_demote_ok().
+ *
+ * In addition to Linux page cache, we also check GFS meta-header-cache.
+ *
+ * Returns: TRUE if it's ok
+ */
+
+static int
+rgrp_go_demote_ok(struct gfs_glock *gl)
+{
+	struct gfs_rgrpd *rgd = gl2rgd(gl);
+	int demote = TRUE;
+
+	if (gl->gl_aspace->i_mapping->nrpages)
+		demote = FALSE;
+	else if (rgd && !list_empty(&rgd->rd_mhc)) /* Don't bother with lock here */
+		demote = FALSE;
+
+	return demote;
+}
+
+/**
+ * rgrp_go_lock - operation done after an rgrp lock is locked by
+ *    a first holder on this node.
+ * @gl: the glock
+ * @flags: the flags passed into gfs_glock()
+ *
+ * Returns: errno
+ *
+ * Read rgrp's header and block allocation bitmaps from disk.
+ */
+
+static int
+rgrp_go_lock(struct gfs_glock *gl, int flags)
+{
+	if (flags & GL_SKIP)
+		return 0;
+	return gfs_rgrp_read(gl2rgd(gl));
+}
+
+/**
+ * rgrp_go_unlock - operation done when an rgrp lock is unlocked by
+ *    a last holder on this node.
+ * @gl: the glock
+ * @flags: the flags passed into gfs_gunlock()
+ *
+ * Release rgrp's bitmap buffers (read in when lock was first obtained).
+ * Make sure rgrp's glock's Lock Value Block has up-to-date block usage stats,
+ *   so other nodes can see them.
+ */
+
+static void
+rgrp_go_unlock(struct gfs_glock *gl, int flags)
+{
+	struct gfs_rgrpd *rgd = gl2rgd(gl);
+	if (flags & GL_SKIP)
+		return;
+	gfs_rgrp_relse(rgd);
+	if (test_bit(GLF_DIRTY, &gl->gl_flags))
+		gfs_rgrp_lvb_fill(rgd);
+}
+
+/**
+ * trans_go_xmote_th - promote/demote (but don't unlock) the transaction glock
+ * @gl: the glock
+ * @state: the requested state
+ * @flags: the flags passed into gfs_glock()
+ *
+ * Acquire a new glock, or change an already-acquired glock to
+ *   more/less restrictive state (other than LM_ST_UNLOCKED).
+ */
+
+static void
+trans_go_xmote_th(struct gfs_glock *gl, unsigned int state, int flags)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+
+	if (gl->gl_state != LM_ST_UNLOCKED &&
+	    test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
+		gfs_sync_meta(sdp);
+		gfs_log_shutdown(sdp);
+	}
+
+	gfs_glock_xmote_th(gl, state, flags);
+}
+
+/**
+ * trans_go_xmote_bh - After promoting/demoting (but not unlocking)
+ *       the transaction glock
+ * @gl: the glock
+ *
+ */
+
+static void
+trans_go_xmote_bh(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_glock *j_gl = sdp->sd_journal_gh.gh_gl;
+	struct gfs_log_header head;
+	int error;
+
+	if (gl->gl_state != LM_ST_UNLOCKED &&
+	    test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
+		j_gl->gl_ops->go_inval(j_gl, DIO_METADATA | DIO_DATA);
+
+		error = gfs_find_jhead(sdp, &sdp->sd_jdesc, j_gl, &head);
+		if (error)
+			gfs_consist(sdp);
+		if (!(head.lh_flags & GFS_LOG_HEAD_UNMOUNT))
+			gfs_consist(sdp);
+
+		/*  Initialize some head of the log stuff  */
+      sdp->sd_sequence = head.lh_sequence;
+      sdp->sd_log_head = head.lh_first + 1;
+	}
+}
+
+/**
+ * trans_go_drop_th - unlock the transaction glock
+ * @gl: the glock
+ *
+ * Invoked from rq_demote().
+ * Another node needs the lock in EXCLUSIVE mode to quiesce the filesystem
+ *   (for journal replay, etc.).
+ *
+ * We want to sync the device even with localcaching.  Remember
+ * that localcaching journal replay only marks buffers dirty.
+ */
+
+static void
+trans_go_drop_th(struct gfs_glock *gl)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+
+	if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
+		gfs_sync_meta(sdp);
+		gfs_log_shutdown(sdp);
+	}
+
+	gfs_glock_drop_th(gl);
+}
+
+/**
+ * nondisk_go_demote_ok - Check to see if it's ok to unlock a non-disk glock
+ * @gl: the glock
+ *
+ * See comments for meta_go_demote_ok().
+ *
+ * We never give up a non-disk glock (unless another node needs it).
+ * Non-disk type used for GFS_MOUNT_LOCK, GFS_LIVE_LOCK, GFS_RENAME_LOCK.
+ * GFS_MOUNT_LOCK is always requested GL_NOCACHE, however, so it never uses
+ *   this function.
+ *
+ * Returns: TRUE if it's ok
+ */
+
+static int
+nondisk_go_demote_ok(struct gfs_glock *gl)
+{
+	return FALSE;
+}
+
+/**
+ * quota_go_demote_ok - Check to see if it's ok to unlock a quota glock
+ * @gl: the glock
+ *
+ * See comments for meta_go_demote_ok().
+ *
+ * Returns: TRUE if it's ok
+ */
+
+static int
+quota_go_demote_ok(struct gfs_glock *gl)
+{
+	return !atomic_read(&gl->gl_lvb_count);
+}
+
+struct gfs_glock_operations gfs_meta_glops = {
+      .go_xmote_th = gfs_glock_xmote_th,
+      .go_drop_th = gfs_glock_drop_th,
+      .go_sync = meta_go_sync,
+      .go_inval = meta_go_inval,
+      .go_demote_ok = meta_go_demote_ok,
+      .go_type = LM_TYPE_META
+};
+
+struct gfs_glock_operations gfs_inode_glops = {
+      .go_xmote_th = inode_go_xmote_th,
+      .go_xmote_bh = inode_go_xmote_bh,
+      .go_drop_th = inode_go_drop_th,
+      .go_sync = inode_go_sync,
+      .go_inval = inode_go_inval,
+      .go_demote_ok = inode_go_demote_ok,
+      .go_lock = inode_go_lock,
+      .go_unlock = inode_go_unlock,
+      .go_greedy = inode_greedy,
+      .go_type = LM_TYPE_INODE
+};
+
+struct gfs_glock_operations gfs_rgrp_glops = {
+      .go_xmote_th = rgrp_go_xmote_th,
+      .go_drop_th = rgrp_go_drop_th,
+      .go_sync = meta_go_sync,
+      .go_inval = meta_go_inval,
+      .go_demote_ok = rgrp_go_demote_ok,
+      .go_lock = rgrp_go_lock,
+      .go_unlock = rgrp_go_unlock,
+      .go_type = LM_TYPE_RGRP
+};
+
+struct gfs_glock_operations gfs_trans_glops = {
+      .go_xmote_th = trans_go_xmote_th,
+      .go_xmote_bh = trans_go_xmote_bh,
+      .go_drop_th = trans_go_drop_th,
+      .go_type = LM_TYPE_NONDISK
+};
+
+struct gfs_glock_operations gfs_iopen_glops = {
+      .go_xmote_th = gfs_glock_xmote_th,
+      .go_drop_th = gfs_glock_drop_th,
+      .go_callback = gfs_iopen_go_callback,
+      .go_type = LM_TYPE_IOPEN
+};
+
+struct gfs_glock_operations gfs_flock_glops = {
+      .go_xmote_th = gfs_glock_xmote_th,
+      .go_drop_th = gfs_glock_drop_th,
+      .go_type = LM_TYPE_FLOCK
+};
+
+struct gfs_glock_operations gfs_nondisk_glops = {
+      .go_xmote_th = gfs_glock_xmote_th,
+      .go_drop_th = gfs_glock_drop_th,
+      .go_demote_ok = nondisk_go_demote_ok,
+      .go_type = LM_TYPE_NONDISK
+};
+
+struct gfs_glock_operations gfs_quota_glops = {
+      .go_xmote_th = gfs_glock_xmote_th,
+      .go_drop_th = gfs_glock_drop_th,
+      .go_demote_ok = quota_go_demote_ok,
+      .go_type = LM_TYPE_QUOTA
+};
diff -pruN linux-2.6.9.orig/fs/gfs/glops.h linux-2.6.9.debug/fs/gfs/glops.h
--- linux-2.6.9.orig/fs/gfs/glops.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/glops.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,26 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __GLOPS_DOT_H__
+#define __GLOPS_DOT_H__
+
+extern struct gfs_glock_operations gfs_meta_glops;
+extern struct gfs_glock_operations gfs_inode_glops;
+extern struct gfs_glock_operations gfs_rgrp_glops;
+extern struct gfs_glock_operations gfs_trans_glops;
+extern struct gfs_glock_operations gfs_iopen_glops;
+extern struct gfs_glock_operations gfs_flock_glops;
+extern struct gfs_glock_operations gfs_nondisk_glops;
+extern struct gfs_glock_operations gfs_quota_glops;
+
+#endif /* __GLOPS_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/incore.h linux-2.6.9.debug/fs/gfs/incore.h
--- linux-2.6.9.orig/fs/gfs/incore.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/incore.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1205 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+ *  In-core (memory/RAM) structures.
+ *  These do not appear on-disk.  See gfs_ondisk.h for on-disk structures.
+ */
+
+#ifndef __INCORE_DOT_H__
+#define __INCORE_DOT_H__
+
+/*  flags used in function call parameters  */
+
+#define DIO_NEW           (0x00000001)  /* Newly allocated metadata */
+#define DIO_FORCE         (0x00000002)  /* Force read of block from disk */
+#define DIO_CLEAN         (0x00000004)  /* Don't write to disk */
+#define DIO_DIRTY         (0x00000008)  /* Data changed, must write to disk */
+#define DIO_START         (0x00000010)  /* Start disk read or write */
+#define DIO_WAIT          (0x00000020)  /* Wait for disk r/w to complete */
+
+#define DIO_METADATA      (0x00000040)  /* Process glock's protected metadata */
+#define DIO_DATA          (0x00000080)  /* Process glock's protected filedata */
+#define DIO_INVISIBLE     (0x00000100)  /* Don't monkey with glock's dirty bit */
+#define DIO_CHECK         (0x00000200)  /* Make sure all metadata has been synced */
+#define DIO_ALL           (0x00000400)  /* Flush all AIL transactions to disk */
+
+/*  Structure prototypes  */
+
+struct gfs_log_operations;
+struct gfs_log_element;
+struct gfs_meta_header_cache;
+struct gfs_depend;
+struct gfs_bitmap;
+struct gfs_rgrpd;
+struct gfs_bufdata;
+struct gfs_glock_operations;
+struct gfs_holder;
+struct gfs_glock;
+struct gfs_alloc;
+struct gfs_inode;
+struct gfs_file;
+struct gfs_unlinked;
+struct gfs_quota_le;
+struct gfs_quota_data;
+struct gfs_log_buf;
+struct gfs_trans;
+struct gfs_gl_hash_bucket;
+struct gfs_sbd;
+
+typedef void (*gfs_glop_bh_t) (struct gfs_glock * gl, unsigned int ret);
+
+/*
+ *  Structure of operations that are associated with each
+ *  type of element in the log.
+ */
+struct gfs_log_operations {
+	/*
+	 * Operations specific to a given log element (LE).
+	 * These are typically executed individually via macros such as LO_ADD.
+	 */
+
+	/* Add new LE to transaction */
+	void (*lo_add) (struct gfs_sbd * sdp, struct gfs_log_element * le);
+
+	/* Do any cleanup, etc., needed just before commit to incore log */
+	void (*lo_trans_end) (struct gfs_sbd * sdp,
+			      struct gfs_log_element * le);
+
+	/* Print LE-specific info via printk() */
+	void (*lo_print) (struct gfs_sbd * sdp, struct gfs_log_element * le,
+			  unsigned int where);
+
+	/* Find any incore transactions that overlap through this LE (e.g.
+	 * share glocks), to determine if any transactions can be combined. */
+	struct gfs_trans *(*lo_overlap_trans) (struct gfs_sbd * sdp,
+					       struct gfs_log_element * le);
+
+	/* Change LE from "new" to "incore" status, before write to log */
+	void (*lo_incore_commit) (struct gfs_sbd * sdp, struct gfs_trans * tr,
+				  struct gfs_log_element * le);
+
+	/* Allow writes to in-place locations, after log is on-disk */
+	void (*lo_add_to_ail) (struct gfs_sbd * sdp,
+			       struct gfs_log_element * le);
+
+	/* Clean up LE after log dump */
+	void (*lo_clean_dump) (struct gfs_sbd * sdp,
+			       struct gfs_log_element * le);
+
+	/*
+	 * Operations specific to a class of log elements.
+	 * These are typically executed over a whole transaction by
+	 * macros such as LO_TRANS_SIZE.  Each LE-type-specific operation
+	 * for each LE contributes its part to the overall result.
+	 */
+
+	/* Determine LE-type-specific quantities of blocks of various types
+	 * required for writing the log */
+	void (*lo_trans_size) (struct gfs_sbd * sdp, struct gfs_trans * tr,
+			       unsigned int *mblks, unsigned int *eblks,
+			       unsigned int *blocks, unsigned int *bmem);
+
+	/* Combine LE-type-specific values in new_tr and tr, result is in tr */
+	void (*lo_trans_combine) (struct gfs_sbd * sdp, struct gfs_trans * tr,
+				  struct gfs_trans * new_tr);
+
+	/* Create control and metadata buffers that will make up the log */
+	void (*lo_build_bhlist) (struct gfs_sbd * sdp, struct gfs_trans * tr);
+
+	/* Calculate log space needed for this LE in a log dump */
+	void (*lo_dump_size) (struct gfs_sbd * sdp, unsigned int *elements,
+			      unsigned int *blocks, unsigned int *bmem);
+
+	/* Add LE to log dump */
+	void (*lo_build_dump) (struct gfs_sbd * sdp, struct gfs_trans * tr);
+
+	/*
+	 * Operations that happen at recovery time
+	 */
+
+	/* Reset/init whatever before doing recovery */
+	void (*lo_before_scan) (struct gfs_sbd * sdp, unsigned int jid,
+				struct gfs_log_header * head,
+				unsigned int pass);
+
+	/* LE-specific recovery procedure */
+	int (*lo_scan_elements) (struct gfs_sbd * sdp,
+				 struct gfs_jindex * jdesc,
+				 struct gfs_glock * gl, uint64_t start,
+				 struct gfs_log_descriptor * desc,
+				 unsigned int pass);
+
+	/* Verify and report recovery results/statistics */
+	void (*lo_after_scan) (struct gfs_sbd * sdp, unsigned int jid,
+			       unsigned int pass);
+
+
+	/* 
+	 * Type of element (glock/buf/unlinked/quota)
+	 */
+	char *lo_name;
+};
+
+/*
+ *  Structure that gets added to struct gfs_trans->tr_elements.  They
+ *  make up the "stuff" in each transaction.
+ */
+struct gfs_log_element {
+	struct gfs_log_operations *le_ops; /* Vector of functions */
+
+	struct gfs_trans *le_trans;     /* We're part of this transaction */
+	struct list_head le_list;       /* Link to transaction's element list */
+};
+
+/*
+ * Meta-header cache structure.
+ * One for each metadata block that we've de-allocated.
+ * Used to temporarily store gfs_meta_header structs for meta blocks that
+ *   have been freshly turned into FREEMETA (alloc'd or de-alloc'd).  Storing
+ *   these (small) structures in-core allows us to release the (large) buffers,
+ *   and not need to re-read the header from disk if/when we re-allocate the
+ *   blocks to USEDMETA, as long as this node holds the EXCLUSIVE lock for the
+ *   resource group containing the blocks.  If we release the EX lock, we must
+ *   throw away the rgrp's cached meta headers, since another node could change
+ *   the blocks' contents.
+ * In-core superblock structure hosts the hashed cache, as well as a
+ *   linear list of all cached, in most-recently-added order.
+ * Also, each resource group keeps a list of cached blocks within its scope.
+ */
+struct gfs_meta_header_cache {
+	/* Links to various lists */
+	struct list_head mc_list_hash;   /* Superblock's hashed list */
+	struct list_head mc_list_single; /* Superblock's list, MRU order */
+	struct list_head mc_list_rgd;    /* Resource group's list */
+
+	uint64_t mc_block;               /* Block # (in-place address) */
+	struct gfs_meta_header mc_mh;    /* Payload: the block's meta-header */
+};
+
+/*
+ * Dependency cache structure.
+ * In-core superblock structure hosts the actual cache.
+ * Also, each resource group keeps a list of dependency blocks within its scope.
+ */
+struct gfs_depend {
+	/* Links to various lists */
+	struct list_head gd_list_hash;  /* Superblock's hashed list */
+	struct list_head gd_list_rgd;   /* Resource group's list */
+
+	struct gfs_rgrpd *gd_rgd;       /* Resource group descriptor */
+	uint64_t gd_formal_ino;         /* Inode ID */
+	unsigned long gd_time;          /* Time (jiffies) when put on list */
+};
+
+/*
+ *  Block allocation bitmap descriptor structure.
+ *  One of these for each FS block that contains bitmap data
+ *    (i.e. the resource group header blocks and their following bitmap blocks).
+ *  Each allocatable FS data block is represented by 2 bits (4 alloc states).
+ */
+struct gfs_bitmap {
+	uint32_t bi_offset;  /* Byte offset of bitmap within this bit block
+	                        (non-zero only for an rgrp header block) */
+	uint32_t bi_start;   /* Data block (rgrp scope, 32-bit) represented
+	                        by the first bit-pair in this bit block */
+	uint32_t bi_len;     /* The number of bitmap bytes in this bit block */
+};
+
+/*
+ *  Resource Group (Rgrp) descriptor structure.
+ *  There is one of these for each resource (block) group in the FS.
+ *  The filesystem is divided into a number of resource groups to allow
+ *    simultaneous block alloc operations by a number of nodes.
+ */
+struct gfs_rgrpd {
+	/* Links to superblock lists */
+	struct list_head rd_list;       /* On-disk-order list of all rgrps */
+	struct list_head rd_list_mru;   /* Most Recently Used list of all rgs */
+	struct list_head rd_recent;     /* recently used rgrps */
+	uint32_t rd_try_counter;        /* # of times we fail a try lock */
+
+	struct gfs_glock *rd_gl;        /* Glock for this rgrp */
+
+	struct gfs_rindex rd_ri;        /* Resource Index (on-disk) structure */
+	struct gfs_rgrp rd_rg;          /* Resource Group (on-disk) structure */
+	uint64_t rd_rg_vn;              /* Version #: if != glock's gl_vn,
+	                                   we need to read rgrp fm disk */
+
+	/* Block alloc bitmap cache */
+	struct gfs_bitmap *rd_bits;     /* Array of block bitmap descriptors */
+	struct buffer_head **rd_bh;     /* Array of ptrs to block bitmap bh's */
+
+	/* Block allocation strategy, rgrp scope. Start at these blocks when
+	   searching for next data/meta block to alloc */
+	uint32_t rd_last_alloc_data;    /* Most recent data block allocated */
+	uint32_t rd_last_alloc_meta;    /* Most recent meta block allocated */
+
+	struct list_head rd_mhc;        /* Cached meta-headers for this rgrp */
+	struct list_head rd_depend;     /* Dependent inodes (MRU order) */
+
+	struct gfs_sbd *rd_sbd;		/* FS incore superblock (fs instance) */
+};
+
+/*
+ *  Per-buffer data
+ *  One of these is attached as GFS private data to each FS block's buffer_head.
+ *  These keep track of a buffer's progress through the transaction pipeline,
+ *    using the "new" embedded log element to attach it to a being-built
+ *    transaction, and moving the attachment point to the "incore" LE once
+ *    the transaction completes (at which time the buffer becomes a candidate
+ *    to be written to the on-disk log).
+ *  A buffer may be attached simultaneously to a new and an incore transaction,
+ *    but no more than one of each:  Only one new trans may be built at a time
+ *    for a given buffer, obviously, since the buffer's contents are protected
+ *    by an EXclusive glock when writing.  And, when a transaction is completely
+ *    built, GFS combines incore transactions that share glocks (see
+ *    incore_commit()), i.e. the glock that protects the buffer, so a buffer
+ *    never needs to be attached to more than one (combined) incore trans.
+ *  Note that multiple transactions can modify the buffer since its most
+ *    recent writes to disk.  This principle applies to both in-place and
+ *    journal block locations on-disk, allowing this node to keep modifying the
+ *    cached data without writing it to disk, unless/until another node needs
+ *    to access the data, or the Linux OS tells us to sync to disk.
+ *  If a transaction follows another transaction before the first transaction's
+ *    log completes (indicated by the in-place buffer head still being pinned
+ *    in RAM), GFS copies the first transaction's results to a "frozen"
+ *    image of the buffer, so the first transaction results (an atomic
+ *    snapshot) can be logged properly, while the second transaction is
+ *    modifying the "real" buffer.  This frozen copy lives only until the new
+ *    transaction is complete, at which point one of two things has occurred:
+ *    1).  Buffer was logged successfully; frozen copy's job is done.
+ *    2).  Buffer was not yet logged; frozen copy no longer needed, newer
+ *         buffer becomes the log candidate.
+ *
+ *  gfs_bufdata structs also link into the Active Items Lists (AIL) (buffers
+ *    flushed to on-disk log, but not yet flushed to on-disk in-place locations)
+ *    attached to:
+ *    1).  The latest transaction to modify and log (on-disk) the buffer, and
+ *    2).  The glock that protects the buffer's contents.
+ *  The buffer is attached to only the most recent transaction's AIL
+ *    list for a couple of reasons.  One is that only the most up-to-date
+ *    buffer content needs to be written to the in-place block on-disk.  The
+ *    other is that since there is a more recent copy of the block in
+ *    the log, we don't need to keep the older copies in the log.  We can
+ *    remove them from the AIL and let the log space be reused for new
+ *    transactions (GFS advances the log tail when removing buffers from AIL).
+ */
+struct gfs_bufdata {
+	struct buffer_head *bd_bh;  /* We belong to this Linux buffer_head */
+	struct gfs_glock *bd_gl;    /* This glock protects buffer's payload */
+
+	/* Log elements map us to a particular set of log operations functions,
+	   and to a particular transaction */
+	struct gfs_log_element bd_new_le;     /* New, incomplete transaction */
+	struct gfs_log_element bd_incore_le;  /* Complete (committed) trans */
+
+	char *bd_frozen;            /* "Frozen" copy of buffer's data */
+	struct semaphore bd_lock;   /* Protects access to this structure */
+
+	/* "Pin" means keep buffer in RAM, don't write to disk (yet) */
+	unsigned int bd_pinned;	         /* Recursive pin count */
+
+	/* Links to Active Items Lists */
+	struct list_head bd_ail_tr_list; /* This buf's most recent trans' AIL */
+	struct list_head bd_ail_gl_list; /* This buf's glock's AIL */
+};
+
+/*
+ *  Glock operations
+ *  One set of operations for each glock, the set selected by type of glock.
+ *  These functions get called at various points in a glock's lifetime.
+ *  "xmote" = promote or demote (change lock state) a glock at inter-node scope.
+ *  "th" = top half, "bh" = bottom half
+ *  Some operations/fields are required (GFS assumes they are there):
+ *     go_xmote_th
+ *     go_drop_th
+ *     go_type
+ *  Other operations are optional (GFS checks for presence before calling).
+ */
+struct gfs_glock_operations {
+
+	/* Acquire lock or change lock state at inter-node scope:
+	     Does type-specific preparation (if any)
+	     Uses gfs_glock_xmote_th to call lock module. */
+	void (*go_xmote_th) (struct gfs_glock * gl, unsigned int state,
+			     int flags);
+
+	/* After acquiring or changing a lock at inter-node scope */
+	void (*go_xmote_bh) (struct gfs_glock * gl);
+
+	/* Release (unlock) a lock at inter-node scope:
+	     Does type-specific preparation (if any)
+	     Uses gfs_glock_drop_th to call lock module. */
+	void (*go_drop_th) (struct gfs_glock * gl);
+
+	/* After releasing a lock at inter-node scope */
+	void (*go_drop_bh) (struct gfs_glock * gl);
+
+	/* Sync dirty data to disk (e.g. before demoting an EX inter-node lock)
+	   (another node needs to read the updated data from disk) */
+	void (*go_sync) (struct gfs_glock * gl, int flags);
+
+	/* Invalidate local cached data just after releasing an inter-node lock
+	   (another node may change the on-disk data, so it's no good to us) */
+	void (*go_inval) (struct gfs_glock * gl, int flags);
+
+	/* Lock-type-specific check to see if it's okay to unlock a glock
+	   at inter-node scope (and remove it from our glock cache) */
+	int (*go_demote_ok) (struct gfs_glock * gl);
+
+	/* After getting lock for first holder (within this node) */
+	int (*go_lock) (struct gfs_glock * gl, int flags);
+
+	/* After last holder (within this node) gives up lock (glock may
+	   remain in glock cache, though) */
+	void (*go_unlock) (struct gfs_glock * gl, int flags);
+
+	/* After receiving a callback: another node needs the lock */
+	void (*go_callback) (struct gfs_glock * gl, unsigned int state);
+
+        /* Called when the glock layer marks a lock as being not greedy
+	   anymore */
+	void (*go_greedy) (struct gfs_glock * gl);
+
+	/* Lock type: locks with same lock # (often an FS block #),
+	   but different types, are different locks */
+	int go_type;
+};
+
+/*
+ *  Glock holder structure
+ *  One for each holder of a glock.
+ *  These coordinate the use, within this node, of an acquired inter-node glock.
+ *  Once a node has acquired a glock, it may be shared within that node by
+ *    several processes, or even by several recursive requests from the same
+ *    process.  Each is a separate "holder".  Different holders may co-exist
+ *    having requested different lock states, as long as the node holds the
+ *    glock in a state that is compatible.  A hold requestor may select, via
+ *    flags, the rules by which sharing within the node is granted:
+ *      LM_FLAG_ANY:  Grant if glock state is any other than UNLOCKED.
+ *      GL_EXACT:     Grant only if glock state is exactly the requested state.
+ *      GL_LOCAL_EXCL:  Grant only one holder at a time within this node.
+ *    With no flags, a hold will be granted to a SHARED request even if the
+ *    node holds the glock in EXCLUSIVE mode.  See relaxed_state_ok().
+ *  When a process needs to manipulate a lock, it requests it via one of
+ *    these holder structures.  If the request cannot be satisfied immediately,
+ *    the holder structure gets queued on one of these lists in gfs_glock:
+ *    1) waiters1, for gaining exclusive access to the (local) glock structure.
+ *    2) waiters2, for demoting a lock (unlocking a glock, or changing its state
+ *       to be less restrictive) or relenquishing "greedy" status.
+ *    3) waiters3, for promoting (locking a new glock, or changing a glock state
+ *       to be more restrictive).
+ *  When holding a lock, gfs_holder struct stays on glock's holder list.
+ *  See gfs-kernel/src/harness/lm_interface.h for gh_state (LM_ST_...)
+ *    and gh_flags (LM_FLAG...) fields.
+ *  Also see glock.h for gh_flags field (GL_...) flags.
+ */
+
+/*  Action requests  */
+#define HIF_MUTEX       (0)  /* Exclusive (local) access to glock struct */
+#define HIF_PROMOTE     (1)  /* Change lock to more restrictive state */
+#define HIF_DEMOTE      (2)  /* Change lock to less restrictive state */
+#define HIF_GREEDY      (3)  /* Wait for the glock to be unlocked */
+
+/*  States  */
+#define HIF_ALLOCED     (4)  /* Holder structure is or was in use */
+#define HIF_DEALLOC     (5)  /* Toss holder struct as soon as queued request
+                              *   is satisfied */
+#define HIF_HOLDER      (6)  /* We have been granted a hold on the lock */
+#define HIF_FIRST       (7)  /* We are first holder to get the lock */
+#define HIF_RECURSE     (8)  /* >1 hold requests on same glock by same process*/
+#define HIF_ABORTED     (9) /* Aborted before being submitted */
+
+struct gfs_holder {
+	struct list_head gh_list;      /* Link to one of glock's holder lists */
+
+	struct gfs_glock *gh_gl;       /* Glock that we're holding */
+	struct task_struct *gh_owner;  /* Linux process that is the holder */
+
+	/* request to change lock state */
+	unsigned int gh_state;         /* LM_ST_... requested lock state */
+	int gh_flags;                  /* GL_... or LM_FLAG_... req modifiers */
+
+	int gh_error;                  /* GLR_... CANCELLED/TRYFAILED/-errno */
+	unsigned long gh_iflags;       /* HIF_... holder state, see above */
+	struct completion gh_wait;     /* Wait for completion of ... */
+};
+
+/*
+ *  Glock Structure
+ *  One for each inter-node lock held by this node.
+ *  A glock is a local representation/abstraction of an inter-node lock.
+ *    Inter-node locks are managed by a "lock module" (LM) which plugs in to
+ *    the lock harness / glock interface (see gfs-kernel/harness).  Different
+ *    lock modules support different lock protocols (e.g. GULM, GDLM, no_lock).
+ *  A glock may have one or more holders within a node.  See gfs_holder above.
+ *  Glocks are managed within a hash table hosted by the in-core superblock.
+ *  After all holders have released a glock, it will stay in the hash table
+ *    cache for a time (depending on lock type), during which the inter-node
+ *    lock will not be released unless another node needs the lock (lock
+ *    manager requests this via callback to GFS through LM on this node).  This
+ *    provides better performance in case this node needs the glock again soon.
+ *    See comments for meta_go_demote_ok(), glops.c.
+ *  Each glock has an associated vector of lock-type-specific "glops" functions
+ *    which are called at important times during the life of a glock, and
+ *    which define the type of lock (e.g. dinode, rgrp, non-disk, etc).
+ *    See gfs_glock_operations above.
+ *  A glock, at inter-node scope, is identified by the following dimensions:
+ *    1)  lock number (usually a block # for on-disk protected entities,
+ *           or a fixed assigned number for non-disk locks, e.g. MOUNT).
+ *    2)  lock type (actually, the type of entity protected by the lock).
+ *    3)  lock namespace, to support multiple GFS filesystems simultaneously.
+ *           Namespace (usually cluster:filesystem) is specified when mounting.
+ *           See man page for gfs_mount.
+ *  Glocks require support of Lock Value Blocks (LVBs) by the inter-node lock
+ *    manager.  LVBs are small (32-byte) chunks of data associated with a given
+ *    lock, that can be quickly shared between cluster nodes.  Used for certain
+ *    purposes such as sharing an rgroup's block usage statistics without
+ *    requiring the overhead of:
+ *      -- sync-to-disk by one node, then a
+ *      -- read from disk by another node.
+ *  
+ */
+
+#define GLF_PLUG                (0)  /* Dummy */
+#define GLF_LOCK                (1)  /* Exclusive (local) access to glock
+                                      *   structure */
+#define GLF_STICKY              (2)  /* Don't release this inter-node lock
+                                      *   unless another node explicitly asks */
+#define GLF_PREFETCH            (3)  /* This lock has been (speculatively)
+                                      *   prefetched, demote if not used soon */
+#define GLF_SYNC                (4)  /* Sync lock's protected data as soon as
+                                      *   there are no more holders */
+#define GLF_DIRTY               (5)  /* There is dirty data for this lock,
+                                      *   sync before releasing inter-node */
+#define GLF_SKIP_WAITERS2       (6)  /* Make run_queue() ignore gl_waiters2
+                                      *   (demote/greedy) holders */
+#define GLF_GREEDY              (7)  /* This lock is ignoring callbacks
+                                      *   (requests from other nodes) for now */
+
+struct gfs_glock {
+	struct list_head gl_list;    /* Link to hb_list in one of superblock's
+	                              * sd_gl_hash glock hash table buckets */
+	unsigned long gl_flags;      /* GLF_... see above */
+	struct lm_lockname gl_name;  /* Lock number and lock type */
+	atomic_t gl_count;           /* Usage count */
+
+	spinlock_t gl_spin;          /* Protects some members of this struct */
+
+	/* Lock state reflects inter-node manager's lock state */
+	unsigned int gl_state;       /* LM_ST_... see harness/lm_interface.h */
+
+	/* Lists of gfs_holders */
+	struct list_head gl_holders;  /* all current holders of the glock */
+	struct list_head gl_waiters1; /* HIF_MUTEX */
+	struct list_head gl_waiters2; /* HIF_DEMOTE, HIF_GREEDY */
+	struct list_head gl_waiters3; /* HIF_PROMOTE */
+
+	struct gfs_glock_operations *gl_ops; /* function vector, defines type */
+
+	/* State to remember for async lock requests */
+	struct gfs_holder *gl_req_gh; /* Holder for request being serviced */
+	gfs_glop_bh_t gl_req_bh;  /* The bottom half to execute */
+
+	lm_lock_t *gl_lock;       /* Lock module's private lock data */
+	char *gl_lvb;             /* Lock Value Block */
+	atomic_t gl_lvb_count;    /* LVB recursive usage (hold/unhold) count */
+
+	uint64_t gl_vn;           /* Incremented when protected data changes */
+	unsigned long gl_stamp;   /* Glock cache retention timer */
+	void *gl_object;          /* The protected entity (e.g. a dinode) */
+
+	/* Incore transaction stuff */
+	/* Log elements map us to a particular set of log operations functions,
+	   and to a particular transaction */
+	struct gfs_log_element gl_new_le;     /* New, incomplete transaction */
+	struct gfs_log_element gl_incore_le;  /* Complete (committed) trans */ 
+
+	struct gfs_gl_hash_bucket *gl_bucket; /* Our bucket in sd_gl_hash */
+	struct list_head gl_reclaim;          /* Link to sd_reclaim_list */
+
+	struct gfs_sbd *gl_sbd;               /* Superblock (FS instance) */
+
+	struct inode *gl_aspace;              /* The buffers protected by this lock */
+	struct list_head gl_ail_bufs;         /* AIL buffers protected by us */
+};
+
+/*
+ *  In-Place Reservation structure
+ *  Coordinates allocation of "in-place" (as opposed to journal) FS blocks,
+ *     which contain persistent inode/file/directory data and metadata.
+ *     These blocks are the allocatable blocks within resource groups (i.e.
+ *     not including rgrp header and block alloc bitmap blocks).
+ *  gfs_inplace_reserve() calculates a fulfillment plan for allocating blocks,
+ *     based on block statistics in the resource group headers.
+ *  Then, gfs_blkalloc() or gfs_metaalloc() walks the block alloc bitmaps
+ *     to do the actual allocation.
+ */
+struct gfs_alloc {
+	/* Up to 4 quotas (including an inode's user and group quotas)
+	   can track changes in block allocation */
+
+	unsigned int al_qd_num;          /* # of quotas tracking changes */
+	struct gfs_quota_data *al_qd[4]; /* Ptrs to quota structures */
+	struct gfs_holder al_qd_ghs[4];  /* Holders for quota glocks */
+
+	/* Request, filled in by the caller to gfs_inplace_reserve() */
+
+	uint32_t al_requested_di;     /* Number of dinodes to reserve */
+	uint32_t al_requested_meta;   /* Number of metadata blocks to reserve */
+	uint32_t al_requested_data;   /* Number of data blocks to reserve */
+
+	/* Fulfillment plan, filled in by gfs_inplace_reserve() */
+
+	char *al_file;                /* Debug info, .c file making request */
+	unsigned int al_line;         /* Debug info, line of code making req */
+	struct gfs_holder al_ri_gh;   /* Glock holder for resource grp index */
+	struct gfs_holder al_rgd_gh;  /* Glock holder for al_rgd rgrp */
+	struct gfs_rgrpd *al_rgd;     /* Resource group from which to alloc */
+	uint32_t al_reserved_meta;    /* Alloc up to this # meta blocks from al_rgd */
+	uint32_t al_reserved_data;    /* Alloc up to this # data blocks from al_rgd */
+
+	/* Actual alloc, filled in by gfs_blkalloc()/gfs_metaalloc(), etc. */
+
+	uint32_t al_alloced_di;       /* # dinode blocks allocated */
+	uint32_t al_alloced_meta;     /* # meta blocks allocated */
+	uint32_t al_alloced_data;     /* # data blocks allocated */
+
+	/* Dinode allocation crap */
+
+	struct gfs_unlinked *al_ul;   /* Unlinked dinode log entry */
+};
+
+/*
+ *  Incore inode structure
+ */
+
+#define GIF_QD_LOCKED           (0)
+#define GIF_PAGED               (1)
+#define GIF_SW_PAGED            (2)
+
+struct gfs_inode {
+	struct gfs_inum i_num;   /* Formal inode # and block address */
+
+	atomic_t i_count;        /* Usage count */
+	unsigned long i_flags;   /* GIF_...  see above */
+
+	uint64_t i_vn;           /* Version #: if different from glock's vn,
+	                            we need to read inode from disk */
+	struct gfs_dinode i_di;  /* Dinode (on-disk) structure */
+
+	struct gfs_glock *i_gl;  /* This glock protects this inode */
+	struct gfs_sbd *i_sbd;   /* Superblock (fs instance structure) */
+	struct inode *i_vnode;   /* Linux VFS inode structure */
+
+	struct gfs_holder i_iopen_gh;  /* Glock holder for Inode Open lock */
+
+	/* Block allocation strategy, inode scope */
+	struct gfs_alloc *i_alloc; /* In-place block reservation structure */
+	uint64_t i_last_rg_alloc;  /* Most recent blk alloc was fm this rgrp */
+
+	spinlock_t i_spin;
+	struct rw_semaphore i_rw_mutex;
+
+	/* Cache of most-recently used buffers in indirect addressing chain */
+	struct buffer_head *i_cache[GFS_MAX_META_HEIGHT];
+
+	unsigned int i_greedy; /* The amount of time to be greedy */
+	unsigned long i_last_pfault; /* The time of the last page fault */
+};
+
+/*
+ *  GFS per-fd structure
+ */
+
+#define GFF_DID_DIRECT_ALLOC    (0)
+
+struct gfs_file {
+	unsigned long f_flags; /* GFF_...  see above */
+
+	struct semaphore f_fl_lock; /* Lock to protect flock operations */
+	struct gfs_holder f_fl_gh; /* Holder for this f_vfile's flock */
+
+	struct gfs_inode *f_inode;        /* Incore GFS inode */
+	struct file *f_vfile;             /* Linux file struct */
+};
+
+/*
+ *  Unlinked inode log entry incore structure
+ */
+
+#define ULF_NEW_UL              (0)  /* Part of new (being built) trans */
+#define ULF_INCORE_UL           (1)  /* Part of incore-committed trans */
+#define ULF_IC_LIST             (2)
+#define ULF_OD_LIST             (3)
+#define ULF_LOCK                (4)  /* Protects access to this structure */
+
+struct gfs_unlinked {
+	struct list_head ul_list;    /* Link to superblock's sd_unlinked_list */
+	unsigned int ul_count;       /* Usage count */
+
+	struct gfs_inum ul_inum;     /* Formal inode #, block addr */
+	unsigned long ul_flags;      /* ULF_... */
+
+	/* Log elements map us to a particular set of log operations functions,
+	   and to a particular transaction */
+	struct gfs_log_element ul_new_le;    /* New, not yet committed */
+	struct gfs_log_element ul_incore_le; /* Committed to incore log */
+	struct gfs_log_element ul_ondisk_le; /* Committed to ondisk log */
+};
+
+/*
+ *  Quota log element
+ *  One for each logged change in a block alloc value affecting a given quota.
+ *  Only one of these for a given quota within a given transaction;
+ *    multiple changes, within one transaction, for a given quota will be
+ *    combined into one log element.
+ */
+struct gfs_quota_le {
+	/* Log element maps us to a particular set of log operations functions,
+	   and to a particular transaction */
+	struct gfs_log_element ql_le;    /* Generic log element structure */
+
+	struct gfs_quota_data *ql_data;  /* The quota we're changing */
+	struct list_head ql_data_list;   /* Link to quota's log element list */
+
+	int64_t ql_change;           /* # of blocks alloc'd (+) or freed (-) */
+};
+
+/*
+ *  Quota structure
+ *  One for each user or group quota.
+ *  Summarizes all block allocation activity for a given quota, and supports
+ *    recording updates of current block alloc values in GFS' special quota
+ *    file, including the journaling of these updates, encompassing
+ *    multiple transactions and log dumps.
+ */
+
+#define QDF_USER                (0)   /* User (1) vs. group (0) quota */
+#define QDF_OD_LIST             (1)   /* Waiting for sync to quota file */
+#define QDF_LOCK                (2)   /* Protects access to this structure */
+
+struct gfs_quota_data {
+	struct list_head qd_list;     /* Link to superblock's sd_quota_list */
+	unsigned int qd_count;        /* Usage count */
+
+	uint32_t qd_id;               /* User or group ID number */
+	unsigned long qd_flags;       /* QDF_... */
+
+	/* This list is for non-log-dump transactions */
+	struct list_head qd_le_list;  /* List of gfs_quota_le log elements */
+
+	/* Summary of block alloc changes affecting this quota, in various
+	   stages of logging & syncing changes to the special quota file */
+	int64_t qd_change_new;  /* New, not yet committed to in-core log*/
+	int64_t qd_change_ic;   /* Committed to in-core log */
+	int64_t qd_change_od;   /* Committed to on-disk log */
+	int64_t qd_change_sync; /* Being synced to the in-place quota file */
+
+	struct gfs_quota_le qd_ondisk_ql; /* Log element for log dump */
+	uint64_t qd_sync_gen;         /* Sync-to-quota-file generation # */
+
+	/* Glock provides protection for quota, *and* provides
+	   lock value block (LVB) communication, between nodes, of current
+	   quota values.  Shared lock -> LVB read.  EX lock -> LVB write. */
+	struct gfs_glock *qd_gl;      /* glock for this quota */
+	struct gfs_quota_lvb qd_qb;   /* LVB (limit/warn/value) */
+
+	unsigned long qd_last_warn;   /* Jiffies of last warning to user */
+};
+
+/*
+ * Log Buffer descriptor structure.
+ * One for each block buffer recorded in the log.
+ * When beginning a new transaction, GFS pre-allocates a number of these,
+ *   and puts them on transaction's tr_free_bufs list.
+ * Logged buffers are of two types:
+ *   1).  Exact copies of buffers to be written to in-place location in FS.
+ *   2).  Log-only buffers such as log headers and control blocks (e.g. tags).
+ * A gfs_log_buf is required for both types; the ones for log-only buffers
+ *   contain NULL in lb_unlock, and get cleaned up after the log write.
+ * lb_bh is a "fake" buffer head that directs Linux block I/O to write the buf
+ *   to the on-disk log location, rather than the on-disk in-place location.
+ *   Used for both types.
+ * lb_unlock points to the "real" buffer head that directs Linux to write the
+ *   buf to its regular on-disk in-place filesystem location.  Once the commit
+ *   to the on-disk log is finished, GFS unlocks the "real" buffer so it can be
+ *   written to in-place block, or modified by another transaction.
+ *   Used only for type 1).
+ */
+struct gfs_log_buf {
+	/* Link to one of the transaction structure's lists */
+	struct list_head lb_list;      /* Link to tr_free_bufs or tr_list */
+
+	struct buffer_head lb_bh;      /* "Fake" bh; for the log block */
+	struct buffer_head *lb_unlock; /* "Real" bh; for the in-place block */
+};
+
+/*
+ *  Transaction structure
+ *  One for each transaction
+ *  This coordinates the logging and flushing of written metadata.
+ */
+
+#define TRF_LOG_DUMP            (0x00000001)
+#define TRF_DUMMY               (0x00000002)
+
+struct gfs_trans {
+
+	/* Link to various lists */
+	struct list_head tr_list;      /* Superblk's incore trans or AIL list*/
+
+	/* Initial creation stuff */
+
+	char *tr_file;                 /* Debug info: .c file creating trans */
+	unsigned int tr_line;          /* Debug info: codeline creating trans */
+
+	/* Reservations for on-disk space in journal.
+	   Meta blocks are copies of in-place filesystem blocks.  
+	   Extra blocks are log-only (log header and control blocks) */
+	unsigned int tr_mblks_asked;   /* # of meta log blocks requested */
+	unsigned int tr_eblks_asked;   /* # of extra log blocks requested */
+	unsigned int tr_seg_reserved;  /* # of segments actually reserved */
+
+	struct gfs_holder *tr_t_gh;    /* Glock holder for this transaction */
+
+	/* Stuff filled in during creation */
+
+	unsigned int tr_flags;         /* TRF_... */
+	struct list_head tr_elements;  /* List of this trans' log elements */
+
+	/* Stuff modified during the commit */
+
+	/* When creating a new transaction, GFS pre-allocates as many of
+	   these buffers and descriptor structures as it might need for
+	   all loggable filesystem (meta)data, and log-control (log-only, not
+	   going to filesystem in-place location) data going to on-disk log.
+	   It keeps them on these "free" lists until they get used (and linked
+	   into tr_bufs list, below) or "refunded" if not needed. */
+	unsigned int tr_num_free_bufs; /* List of free gfs_log_buf structs */
+	struct list_head tr_free_bufs; /* .. 1 for each log block */
+	unsigned int tr_num_free_bmem; /* List of free fs-block-size buffers */
+	struct list_head tr_free_bmem; /* .. for log-only (e.g. tag) blocks */
+
+	/* Logged transaction starts with a (first) log header at a segment
+	   boundary, and fills contiguous blocks after that.  Each segment
+	   boundary block gets another log header. */
+	uint64_t tr_log_head;          /* The next log block # to fill */
+	uint64_t tr_first_head;	       /* Trans' first log header's block # */
+
+	/* gfs_log_buf structs move from tr_free_bufs to here when being used */
+	struct list_head tr_bufs;      /* List of buffers going to the log */
+
+	/* Stuff that's part of the Active Items List (AIL) */
+
+	struct list_head tr_ail_bufs;  /* List of buffers on AIL list */
+
+	/* # log elements of various types on tr_elements list */
+
+	unsigned int tr_num_gl;        /* Glocks */
+	unsigned int tr_num_buf;       /* Buffers */
+	unsigned int tr_num_iul;       /* Unlinked inodes */
+	unsigned int tr_num_ida;       /* De-allocated inodes */
+	unsigned int tr_num_q;         /* Quotas */
+};
+
+#define GFS_GLOCKD_DEFAULT (1)
+#define GFS_GLOCKD_MAX (32)
+
+struct gfs_args {
+	char ar_lockproto[GFS_LOCKNAME_LEN]; /* The name of the Lock Protocol */
+	char ar_locktable[GFS_LOCKNAME_LEN]; /* The name of the Lock Table */
+	char ar_hostdata[GFS_LOCKNAME_LEN]; /* The host specific data */
+
+        /*
+	 * GFS can invoke some flock and disk caching optimizations if it is
+	 * not in a cluster, i.e. is a local filesystem.  The chosen lock
+	 * module tells GFS, at mount time, if it supports clustering.
+	 * The nolock module is the only one that does not support clustering;
+	 * it sets to TRUE the local_fs field in the struct lm_lockops.
+	 * GFS can either optimize, or ignore the opportunity.
+	 * The user controls behavior via the following mount options.
+	 */
+	int ar_ignore_local_fs; /* Don't optimize even if local_fs is TRUE */
+	int ar_localflocks; /* Let the VFS do flock|fcntl locks for us */
+	int ar_localcaching; /* Local-style caching (dangerous on multihost) */
+	int ar_oopses_ok; /* Allow oopses */
+
+	int ar_debug; /* Oops on errors instead of trying to be graceful */
+	int ar_upgrade; /* Upgrade ondisk/multihost format */
+
+	unsigned int ar_num_glockd; /* # of glock cleanup daemons to run
+				       (more daemons => faster cleanup)  */
+	int ar_posix_acls; /* Enable posix acls */
+	int ar_suiddir; /* suiddir support */
+};
+
+struct gfs_tune {
+	spinlock_t gt_spin;
+
+	unsigned int gt_ilimit1;
+	unsigned int gt_ilimit1_tries;
+	unsigned int gt_ilimit1_min;
+	unsigned int gt_ilimit2;
+	unsigned int gt_ilimit2_tries;
+	unsigned int gt_ilimit2_min;
+	unsigned int gt_demote_secs; /* Cache retention for unheld glock */
+	unsigned int gt_incore_log_blocks;
+	unsigned int gt_jindex_refresh_secs; /* Check for new journal index */
+	unsigned int gt_depend_secs;
+
+	/* How often various daemons run (seconds) */
+	unsigned int gt_scand_secs; /* Find unused glocks and inodes */
+	unsigned int gt_recoverd_secs; /* Recover journal of crashed node */
+	unsigned int gt_logd_secs; /* Update log tail as AIL flushes */
+	unsigned int gt_quotad_secs; /* Sync changes to quota file, clean*/
+	unsigned int gt_inoded_secs; /* Toss unused inodes */
+
+	unsigned int gt_quota_simul_sync; /* Max # quotavals to sync at once */
+	unsigned int gt_quota_warn_period; /* Secs between quota warn msgs */
+	unsigned int gt_atime_quantum; /* Min secs between atime updates */
+	unsigned int gt_quota_quantum; /* Secs between syncs to quota file */
+	unsigned int gt_quota_scale_num; /* Numerator */
+	unsigned int gt_quota_scale_den; /* Denominator */
+	unsigned int gt_quota_enforce;
+	unsigned int gt_quota_account;
+	unsigned int gt_new_files_jdata;
+	unsigned int gt_new_files_directio;
+	unsigned int gt_max_atomic_write; /* Split large writes into this size*/
+	unsigned int gt_max_readahead; /* Max bytes to read-ahead from disk */
+	unsigned int gt_lockdump_size;
+	unsigned int gt_stall_secs; /* Detects trouble! */
+	unsigned int gt_complain_secs;
+	unsigned int gt_reclaim_limit; /* Max # glocks in reclaim list */
+	unsigned int gt_entries_per_readdir;
+	unsigned int gt_prefetch_secs; /* Usage window for prefetched glocks */
+	unsigned int gt_statfs_slots;
+	unsigned int gt_max_mhc; /* Max # of meta headers in mhc cache */
+	unsigned int gt_greedy_default;
+	unsigned int gt_greedy_quantum;
+	unsigned int gt_greedy_max;
+	unsigned int gt_rgrp_try_threshold;
+};
+
+/*
+ *  One bucket of the filesystem's sd_gl_hash glock hash table.
+ *
+ *  A gfs_glock links into a bucket's list via glock's gl_list member.
+ *
+ */
+struct gfs_gl_hash_bucket {
+	rwlock_t hb_lock;              /* Protects list */
+	struct list_head hb_list;      /* List of glocks in this bucket */
+};
+
+/*
+ *  "Super Block" Data Structure
+ *  One per mounted filesystem.
+ *  This is the big instance structure that ties everything together for
+ *    a given mounted filesystem.  Each GFS mount has its own, supporting
+ *    mounts of multiple GFS filesystems on each node.
+ *  Pointer to this is usually seen as "sdp" throughout code.
+ *  This is a very large structure, as structures go, in part because it
+ *    contains arrays of hash buckets for various in-core caches.
+ */
+
+#define SDF_JOURNAL_LIVE        (0)  /* Journaling is active (journal is writeable)*/
+#define SDF_SHUTDOWN            (1)  /* FS abnormaly shutdown */
+
+/* Run (1) / stop (0) flags for various daemons */
+#define SDF_SCAND_RUN           (2)  /* Put unused glocks on reclaim queue */
+#define SDF_GLOCKD_RUN          (3)  /* Reclaim (dealloc) unused glocks */
+#define SDF_RECOVERD_RUN        (4)  /* Recover journal of a crashed node */
+#define SDF_LOGD_RUN            (5)  /* Update log tail after AIL flushed */
+#define SDF_QUOTAD_RUN          (6)  /* Sync quota changes to file, cleanup */
+#define SDF_INODED_RUN          (7)  /* Deallocate unlinked inodes */
+
+/* (Re)mount options from Linux VFS */
+#define SDF_NOATIME             (8)  /* Don't change access time */
+#define SDF_ROFS                (9)  /* Read-only mode */
+
+/* Journal log dump support */
+#define SDF_NEED_LOG_DUMP       (10) /* Need to rewrite unlink and quota tags */
+#define SDF_FOUND_UL_DUMP       (11) /* Recovery found unlinked tags */
+#define SDF_FOUND_Q_DUMP        (12) /* Recovery found qutoa tags */
+#define SDF_IN_LOG_DUMP         (13) /* Serializes log dumps */
+
+/* Glock cache */
+#define GFS_GL_HASH_SHIFT       (13)    /* # hash buckets = 8K */
+#define GFS_GL_HASH_SIZE        (1 << GFS_GL_HASH_SHIFT)
+#define GFS_GL_HASH_MASK        (GFS_GL_HASH_SIZE - 1)
+
+/* Meta header cache */
+#define GFS_MHC_HASH_SHIFT      (10)    /* # hash buckets = 1K */
+#define GFS_MHC_HASH_SIZE       (1 << GFS_MHC_HASH_SHIFT)
+#define GFS_MHC_HASH_MASK       (GFS_MHC_HASH_SIZE - 1)
+
+/* Dependency cache */
+#define GFS_DEPEND_HASH_SHIFT   (10)    /* # hash buckets = 1K */
+#define GFS_DEPEND_HASH_SIZE    (1 << GFS_DEPEND_HASH_SHIFT)
+#define GFS_DEPEND_HASH_MASK    (GFS_DEPEND_HASH_SIZE - 1)
+
+struct gfs_sbd {
+	struct gfs_sb sd_sb;            /* GFS on-disk Super Block image */
+
+	struct super_block *sd_vfs;     /* Linux VFS device independent sb */
+
+	struct gfs_args sd_args;        /* Mount arguments */
+	unsigned long sd_flags;         /* SDF_... see above */
+
+	struct gfs_tune sd_tune;	/* Filesystem tuning structure */
+
+	/* Resource group stuff */
+
+	struct gfs_inode *sd_riinode;	/* Resource Index (rindex) inode */
+	uint64_t sd_riinode_vn;	        /* Resource Index version # (detects
+	                                   whether new rgrps have been added) */
+
+	struct list_head sd_rglist;	/* List of all resource groups,
+					   on-disk order */
+	struct semaphore sd_rindex_lock;/* Serializes RIndex rereads */
+	struct list_head sd_rg_mru_list;/* List of all resource groups,
+					   most-recently-used (MRU) order */
+	spinlock_t sd_rg_mru_lock;      /* Protect mru list */
+	struct list_head sd_rg_recent;	/* List of rgrps from which blocks
+					   were recently allocated */
+	spinlock_t sd_rg_recent_lock;   /* Protect recent list */
+	struct gfs_rgrpd *sd_rg_forward;/* Next rgrp from which to attempt
+					   a block alloc */
+	spinlock_t sd_rg_forward_lock;  /* Protect forward pointer */
+
+	unsigned int sd_rgcount;	/* Total # of resource groups */
+
+	/*  Constants computed on mount  */
+
+	/* "bb" == "basic block" == 512Byte sector */
+	uint32_t sd_fsb2bb;             /* # 512B basic blocks in a FS block */
+	uint32_t sd_fsb2bb_shift;       /* Shift sector # to the right by 
+	                                   this to get FileSystem block addr */
+	uint32_t sd_diptrs;     /* Max # of block pointers in a dinode */
+	uint32_t sd_inptrs;     /* Max # of block pointers in an indirect blk */
+	uint32_t sd_jbsize;     /* Payload size (bytes) of a journaled metadata
+	                               block (GFS journals all meta blocks) */
+	uint32_t sd_hash_bsize; /* sizeof(exhash hash block) */
+	uint32_t sd_hash_bsize_shift;
+	uint32_t sd_hash_ptrs;  /* Number of points in a hash block */
+	uint32_t sd_max_dirres; /* Max blocks needed to add a directory entry */
+	uint32_t sd_max_height;	/* Max height of a file's tree */
+	uint64_t sd_heightsize[GFS_MAX_META_HEIGHT];
+	uint32_t sd_max_jheight; /* Max height, journaled file's tree */
+	uint64_t sd_jheightsize[GFS_MAX_META_HEIGHT];
+
+	/*  Lock Stuff  */
+
+	/* Glock cache (all glocks currently held by this node for this FS) */
+	struct gfs_gl_hash_bucket sd_gl_hash[GFS_GL_HASH_SIZE];
+
+	/* Glock reclaim support for scand and glockd */
+	struct list_head sd_reclaim_list;   /* List of glocks to reclaim */
+	spinlock_t sd_reclaim_lock;
+	wait_queue_head_t sd_reclaim_wchan;
+	atomic_t sd_reclaim_count;          /* # glocks on reclaim list */
+
+	/* Lock module tells us if we're first-to-mount, 
+	   which journal to use, etc. */
+	struct lm_lockstruct sd_lockstruct; /* Info provided by lock module */
+
+	/*  Other caches */
+
+	/* Meta-header cache (incore copies of on-disk meta headers) */
+	struct list_head sd_mhc[GFS_MHC_HASH_SIZE]; /* hash buckets */
+	struct list_head sd_mhc_single;     /* Non-hashed list of all MHCs */
+	spinlock_t sd_mhc_lock;
+	atomic_t sd_mhc_count;              /* # MHCs in cache */
+
+	/* Dependency cache */
+	struct list_head sd_depend[GFS_DEPEND_HASH_SIZE];  /* Hash buckets */
+	spinlock_t sd_depend_lock;
+	atomic_t sd_depend_count;           /* # dependencies in cache */
+
+	/* LIVE inter-node lock indicates that FS is mounted on at least
+	   one node */
+	struct gfs_holder sd_live_gh;       /* Glock holder for LIVE lock */
+
+	/* For quiescing the filesystem */
+	struct gfs_holder sd_freeze_gh;
+	struct semaphore sd_freeze_lock;
+	unsigned int sd_freeze_count;
+
+	/*  Inode Stuff  */
+
+	struct gfs_inode *sd_rooti;         /* FS's root inode */
+
+	/* Only 1 node at a time may rename (e.g. mv) directory from
+	   one directory to another. */
+	struct gfs_glock *sd_rename_gl;     /* Rename glock */
+
+	/*  Daemon stuff  */
+
+	/* Scan for glocks and inodes to toss from memory */
+	struct task_struct *sd_scand_process; /* Scand places on reclaim list*/
+	unsigned int sd_glockd_num;    /* # of glockd procs to do reclaiming*/
+
+	/* Recover journal of a crashed node */
+	struct task_struct *sd_recoverd_process;
+
+	/* Update log tail as AIL gets flushed to in-place on-disk blocks */
+	struct task_struct *sd_logd_process;
+
+	/* Sync quota updates to disk, and clean up unused quota structs */
+	struct task_struct *sd_quotad_process;
+
+	/* Clean up unused inode structures */
+	struct task_struct *sd_inoded_process;
+
+	/* Support for starting/stopping daemons */
+	struct semaphore sd_thread_lock;
+	struct completion sd_thread_completion;
+
+	/*  Log stuff  */
+
+	/* Transaction lock protects the following from one another:
+	   normal write transaction, journal replay (recovery), fs upgrade,
+	   fs read-only => read/write and read/write => read-only conversions.
+	   Also, acquiring the transaction lock in a state other than shared
+	   causes all other machines in the cluster to sync out their dirty
+	   data, mark their journal as being clean, and prevent any new FS
+	   modifications from occuring (i.e. quiesces the FS). */
+	struct gfs_glock *sd_trans_gl;	/* Transaction glock structure */
+
+	struct gfs_inode *sd_jiinode;	/* Journal index inode */
+	uint64_t sd_jiinode_vn;         /* Journal index version # (detects
+	                                   if new journals have been added) */
+
+	unsigned int sd_journals;	/* Number of journals in the FS */
+	struct gfs_jindex *sd_jindex;	/* Array of journal descriptors */
+	struct semaphore sd_jindex_lock;
+	unsigned long sd_jindex_refresh_time; /* Poll for new journals (secs) */
+
+	struct gfs_jindex sd_jdesc;	 /* This machine's journal descriptor */
+	struct gfs_holder sd_journal_gh; /* This machine's jrnl glock holder */
+
+	uint64_t sd_sequence;	/* Assigned to xactions in order they commit */
+	uint64_t sd_log_head;	/* Block number of next journal write */
+	uint64_t sd_log_wrap;
+
+	spinlock_t sd_log_seg_lock;
+	unsigned int sd_log_seg_free;	/* # of free segments in the log */
+	unsigned int sd_log_seg_ail2; /* # of freeable segments in the log */
+	struct list_head sd_log_seg_list;
+	wait_queue_head_t sd_log_seg_wait;
+
+	/* "Active Items List" of transactions that have been flushed to
+	   on-disk log, and are waiting for flush to in-place on-disk blocks */
+	struct list_head sd_log_ail;	/* "next" is head, "prev" is tail */
+
+	/* Transactions committed incore, but not yet flushed to on-disk log */
+	struct list_head sd_log_incore;	/* "Next" is newest, "prev" is oldest */
+	unsigned int sd_log_buffers;	/* # of buffers in the incore log */
+
+	struct rw_semaphore sd_log_lock;	/* Lock for access to log values */
+
+	uint64_t sd_log_dump_last;
+	uint64_t sd_log_dump_last_wrap;
+
+	/*
+	 * Unlinked inode crap.
+	 * List includes newly created, not-yet-linked inodes,
+	 *   as well as inodes that have been unlinked and are waiting
+         *   to be de-allocated.
+	 */
+	struct list_head sd_unlinked_list; /* List of unlinked inodes */
+	spinlock_t sd_unlinked_lock;       /* Protects list and members */
+
+	atomic_t sd_unlinked_ic_count;
+	atomic_t sd_unlinked_od_count;
+
+	/* Quota crap */
+
+	struct list_head sd_quota_list; /* List of all gfs_quota_data structs */
+	spinlock_t sd_quota_lock;
+
+	atomic_t sd_quota_count;        /* # quotas on sd_quota_list */
+	atomic_t sd_quota_od_count;     /* # quotas waiting for sync to
+	                                   special on-disk quota file */
+
+	struct gfs_inode *sd_qinode;    /* Special on-disk quota file */
+
+	uint64_t sd_quota_sync_gen;     /* Generation, incr when sync to file */
+	unsigned long sd_quota_sync_time; /* Jiffies, last sync to quota file */
+
+	/* License crap */
+
+	struct gfs_inode *sd_linode;    /* Special on-disk license file */
+
+	/* Recovery stuff */
+
+	/* Lock module tells GFS, via callback, when a journal needs recovery.
+	   It stays on this list until recovery daemon performs recovery. */
+	struct list_head sd_dirty_j;    /* List of dirty journals */
+	spinlock_t sd_dirty_j_lock;     /* Protects list */
+
+	/* Statistics for 3 possible recovery actions for each buffer in log,
+	     determined by comparing generation #s of logged block and
+	     in-place block.  Scope of stats is for one journal. */
+	unsigned int sd_recovery_replays; /* newer than in-place; copy it */
+	unsigned int sd_recovery_skips;   /* older than in-place; ignore it */
+	unsigned int sd_recovery_sames;   /* same as in-place; ignore it */
+
+	/* Counters */
+
+	/* current quantities of various things */
+	atomic_t sd_glock_count;      /* # of gfs_glock structs alloc'd */
+	atomic_t sd_glock_held_count; /* # of glocks locked by this node */
+	atomic_t sd_inode_count;      /* # of gfs_inode structs alloc'd */
+	atomic_t sd_bufdata_count;    /* # of gfs_bufdata structs alloc'd */
+
+	atomic_t sd_fh2dentry_misses; /* total # get_dentry misses */
+	atomic_t sd_reclaimed;        /* total # glocks reclaimed since mount */
+
+	/* total lock-related calls handled since mount */
+	atomic_t sd_glock_nq_calls;
+	atomic_t sd_glock_dq_calls;
+	atomic_t sd_glock_prefetch_calls;
+	atomic_t sd_lm_lock_calls;
+	atomic_t sd_lm_unlock_calls;
+	atomic_t sd_lm_callbacks;
+
+	atomic_t sd_lm_outstanding;
+	atomic_t sd_bio_reads;
+	atomic_t sd_bio_writes;
+	atomic_t sd_bio_outstanding;
+
+	/* total calls from Linux VFS handled since mount */
+	atomic_t sd_ops_address;
+	atomic_t sd_ops_dentry;
+	atomic_t sd_ops_export;
+	atomic_t sd_ops_file;
+	atomic_t sd_ops_inode;
+	atomic_t sd_ops_super;
+	atomic_t sd_ops_vm;
+
+	char sd_fsname[256];
+
+	/* Debugging crud */
+
+	unsigned long sd_last_warning;
+
+	spinlock_t sd_ail_lock;
+	struct list_head sd_recovery_bufs;
+
+	struct list_head sd_list;
+};
+
+#endif /* __INCORE_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/inode.c linux-2.6.9.debug/fs/gfs/inode.c
--- linux-2.6.9.orig/fs/gfs/inode.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/inode.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,2216 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/posix_acl.h>
+
+#include "gfs.h"
+#include "acl.h"
+#include "bmap.h"
+#include "dio.h"
+#include "dir.h"
+#include "eattr.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "log.h"
+#include "ops_address.h"
+#include "ops_file.h"
+#include "ops_inode.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "trans.h"
+#include "unlinked.h"
+
+/**
+ * inode_attr_in - Copy attributes from the dinode into the VFS inode
+ * @ip: The GFS inode (with embedded disk inode data)
+ * @inode:  The Linux VFS inode
+ *
+ */
+
+static void
+inode_attr_in(struct gfs_inode *ip, struct inode *inode)
+{
+	unsigned int mode;
+
+	inode->i_ino = ip->i_num.no_formal_ino;
+
+	switch (ip->i_di.di_type) {
+	case GFS_FILE_REG:
+		mode = S_IFREG;
+		inode->i_rdev = 0;
+		break;
+	case GFS_FILE_DIR:
+		mode = S_IFDIR;
+		inode->i_rdev = 0;
+		break;
+	case GFS_FILE_LNK:
+		mode = S_IFLNK;
+		inode->i_rdev = 0;
+		break;
+	case GFS_FILE_BLK:
+		mode = S_IFBLK;
+		inode->i_rdev = MKDEV(ip->i_di.di_major, ip->i_di.di_minor);
+		break;
+	case GFS_FILE_CHR:
+		mode = S_IFCHR;
+		inode->i_rdev = MKDEV(ip->i_di.di_major, ip->i_di.di_minor);
+		break;
+	case GFS_FILE_FIFO:
+		mode = S_IFIFO;
+		inode->i_rdev = 0;
+		break;
+	case GFS_FILE_SOCK:
+		mode = S_IFSOCK;
+		inode->i_rdev = 0;
+		break;
+	default:
+		if (gfs_consist_inode(ip))
+			printk("GFS: fsid=%s: type = %u\n",
+			       ip->i_sbd->sd_fsname, ip->i_di.di_type);
+		return;
+	};
+
+	inode->i_mode = mode | (ip->i_di.di_mode & S_IALLUGO);
+	inode->i_nlink = ip->i_di.di_nlink;
+	inode->i_uid = ip->i_di.di_uid;
+	inode->i_gid = ip->i_di.di_gid;
+	i_size_write(inode, ip->i_di.di_size);
+	inode->i_atime.tv_sec = ip->i_di.di_atime;
+	inode->i_mtime.tv_sec = ip->i_di.di_mtime;
+	inode->i_ctime.tv_sec = ip->i_di.di_ctime;
+	inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0;
+	inode->i_blksize = PAGE_SIZE;
+	inode->i_blocks = ip->i_di.di_blocks <<
+		(ip->i_sbd->sd_sb.sb_bsize_shift - GFS_BASIC_BLOCK_SHIFT);
+	inode->i_generation = ip->i_di.di_header.mh_incarn;
+
+	if (ip->i_di.di_flags & GFS_DIF_IMMUTABLE)
+		inode->i_flags |= S_IMMUTABLE;
+	else
+		inode->i_flags &= ~S_IMMUTABLE;
+
+	if (ip->i_di.di_flags & GFS_DIF_APPENDONLY)
+		inode->i_flags |= S_APPEND;
+	else
+		inode->i_flags &= ~S_APPEND;
+}
+
+/**
+ * gfs_inode_attr_in - Copy attributes from the dinode into the VFS inode
+ * @ip: The GFS inode (with embedded disk inode data)
+ *
+ */
+
+void
+gfs_inode_attr_in(struct gfs_inode *ip)
+{
+	struct inode *inode;
+
+	inode = gfs_iget(ip, NO_CREATE);
+	if (inode) {
+		inode_attr_in(ip, inode);
+		iput(inode);
+	}
+
+}
+
+/**
+ * gfs_inode_attr_out - Copy attributes from VFS inode into the dinode
+ * @ip: The GFS inode
+ *
+ * Only copy out the attributes that we want the VFS layer
+ * to be able to modify.
+ */
+
+void
+gfs_inode_attr_out(struct gfs_inode *ip)
+{
+	struct inode *inode = ip->i_vnode;
+
+	ip->i_di.di_mode = inode->i_mode & S_IALLUGO;
+	ip->i_di.di_uid = inode->i_uid;
+	ip->i_di.di_gid = inode->i_gid;
+	ip->i_di.di_atime = inode->i_atime.tv_sec;
+	ip->i_di.di_mtime = inode->i_mtime.tv_sec;
+	ip->i_di.di_ctime = inode->i_ctime.tv_sec;
+}
+
+/**
+ * gfs_iget - Get/Create a struct inode for a struct gfs_inode
+ * @ip: the struct gfs_inode to get the struct inode for
+ * @create: CREATE -- create a new struct inode if one does not already exist
+ *          NO_CREATE -- return NULL if inode doesn't exist
+ *
+ * Returns: A VFS inode, or NULL if NO_CREATE and none in existance
+ *
+ * If this function creates a new inode, it:
+ *   Copies fields from the GFS on-disk (d)inode to the VFS inode
+ *   Attaches the appropriate ops vectors to the VFS inode and address_space
+ *   Attaches the VFS inode to the gfs_inode
+ *   Inserts the new inode in the VFS inode hash, while avoiding races
+ */
+
+struct inode *
+gfs_iget(struct gfs_inode *ip, int create)
+{
+	struct inode *inode = NULL, *tmp;
+
+	spin_lock(&ip->i_spin);
+	if (ip->i_vnode)
+		inode = igrab(ip->i_vnode);
+	spin_unlock(&ip->i_spin);
+
+	if (inode || !create)
+		return inode;
+
+	tmp = new_inode(ip->i_sbd->sd_vfs);
+	if (!tmp)
+		return NULL;
+
+	inode_attr_in(ip, tmp);
+
+	/* Attach GFS-specific ops vectors */
+	if (ip->i_di.di_type == GFS_FILE_REG) {
+		tmp->i_op = &gfs_file_iops;
+		tmp->i_fop = &gfs_file_fops;
+		tmp->i_mapping->a_ops = &gfs_file_aops;
+	} else if (ip->i_di.di_type == GFS_FILE_DIR) {
+		tmp->i_op = &gfs_dir_iops;
+		tmp->i_fop = &gfs_dir_fops;
+	} else if (ip->i_di.di_type == GFS_FILE_LNK) {
+		tmp->i_op = &gfs_symlink_iops;
+	} else {
+		tmp->i_op = &gfs_dev_iops;
+		init_special_inode(tmp, tmp->i_mode, tmp->i_rdev);
+	}
+
+	vn2ip(tmp) = NULL;
+
+	/* Did another process successfully create an inode while we were
+	   preparing this (tmp) one?  If so, we can use that other one, and
+	   trash the one we were preparing. 
+	   The other process might not be done inserting the inode in the
+	   VFS hash table.  If so, we need to wait until it is done, then
+	   we can use it.  */
+	for (;;) {
+		spin_lock(&ip->i_spin);
+		if (!ip->i_vnode)
+			break;
+		inode = igrab(ip->i_vnode);
+		spin_unlock(&ip->i_spin);
+
+		if (inode) {
+			iput(tmp);
+			return inode;
+		}
+		yield();
+	}
+
+	inode = tmp;
+
+	gfs_inode_hold(ip);
+	ip->i_vnode = inode;
+	vn2ip(inode) = ip;
+
+	spin_unlock(&ip->i_spin);
+
+	insert_inode_hash(inode);
+
+	return inode;
+}
+
+/**
+ * gfs_copyin_dinode - Refresh the incore copy of the dinode
+ * @ip: The GFS inode
+ *
+ * Returns: errno
+ */
+
+int
+gfs_copyin_dinode(struct gfs_inode *ip)
+{
+	struct buffer_head *dibh;
+	int error;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		return error;
+
+	if (gfs_metatype_check(ip->i_sbd, dibh, GFS_METATYPE_DI)) {
+		brelse(dibh);
+		return -EIO;
+	}
+
+	gfs_dinode_in(&ip->i_di, dibh->b_data);
+	brelse(dibh);
+
+	if (ip->i_num.no_formal_ino != ip->i_di.di_num.no_formal_ino) {
+		if (gfs_consist_inode(ip))
+			gfs_dinode_print(&ip->i_di);
+		return -EIO;
+	}
+
+	/* Handle a moved inode (not implemented yet) */
+	if (ip->i_num.no_addr != ip->i_di.di_num.no_addr) {
+		if (gfs_consist_inode(ip))
+			gfs_dinode_print(&ip->i_di);
+		return -EIO;
+	}
+
+	ip->i_vn = ip->i_gl->gl_vn;
+
+	return 0;
+}
+
+/**
+ * inode_create - create a struct gfs_inode, acquire Inode-Open (iopen) glock,
+ *      read dinode from disk
+ * @i_gl: The (already held) glock covering the inode
+ * @inum: The inode number
+ * @io_gl: the iopen glock to acquire/hold (using holder in new gfs_inode)
+ * @io_state: the state the iopen glock should be acquired in
+ * @ipp: pointer to put the returned inode in
+ *
+ * Returns: errno
+ */
+
+static int
+inode_create(struct gfs_glock *i_gl, struct gfs_inum *inum,
+	     struct gfs_glock *io_gl, unsigned int io_state,
+	     struct gfs_inode **ipp)
+{
+	struct gfs_sbd *sdp = i_gl->gl_sbd;
+	struct gfs_inode *ip;
+	int error = 0;
+
+	RETRY_MALLOC(ip = kmem_cache_alloc(gfs_inode_cachep, GFP_KERNEL), ip);
+	memset(ip, 0, sizeof(struct gfs_inode));
+
+	ip->i_num = *inum;
+
+	atomic_set(&ip->i_count, 1);
+
+	ip->i_gl = i_gl;
+	ip->i_sbd = sdp;
+
+	spin_lock_init(&ip->i_spin);
+	init_rwsem(&ip->i_rw_mutex);
+
+	ip->i_greedy = gfs_tune_get(sdp, gt_greedy_default);
+
+	/* Lock the iopen glock (may be recursive) */
+	error = gfs_glock_nq_init(io_gl,
+				  io_state, GL_LOCAL_EXCL | GL_EXACT,
+				  &ip->i_iopen_gh);
+	if (error)
+		goto fail;
+
+	ip->i_iopen_gh.gh_owner = NULL;
+
+	/* Assign the inode's glock as this iopen glock's protected object */
+	spin_lock(&io_gl->gl_spin);
+	gfs_glock_hold(i_gl);
+	gl2gl(io_gl) = i_gl;
+	spin_unlock(&io_gl->gl_spin);
+
+	/* Read dinode from disk */
+	error = gfs_copyin_dinode(ip);
+	if (error)
+		goto fail_iopen;
+
+	gfs_glock_hold(i_gl);
+	gl2ip(i_gl) = ip;
+
+	atomic_inc(&sdp->sd_inode_count);
+
+	*ipp = ip;
+
+	return 0;
+
+ fail_iopen:
+	spin_lock(&io_gl->gl_spin);
+	gl2gl(io_gl) = NULL;
+	gfs_glock_put(i_gl);
+	spin_unlock(&io_gl->gl_spin);
+
+	gfs_glock_dq_uninit(&ip->i_iopen_gh);
+
+ fail:
+	gfs_flush_meta_cache(ip);
+	kmem_cache_free(gfs_inode_cachep, ip);
+	*ipp = NULL;
+
+	return error;
+}
+
+/**
+ * gfs_inode_get - Get an inode given its number
+ * @i_gl: The glock covering the inode
+ * @inum: The inode number
+ * @create: Flag to say if we are allowed to create a new struct gfs_inode
+ * @ipp: pointer to put the returned inode in
+ *
+ * Returns: errno
+ *
+ * If creating a new gfs_inode structure, reads dinode from disk.
+ */
+
+int
+gfs_inode_get(struct gfs_glock *i_gl, struct gfs_inum *inum, int create,
+		struct gfs_inode **ipp)
+{
+	struct gfs_glock *io_gl;
+	int error = 0;
+
+	*ipp = gl2ip(i_gl);
+	if (*ipp) {
+		atomic_inc(&(*ipp)->i_count);
+		gfs_assert_warn(i_gl->gl_sbd, 
+				(*ipp)->i_num.no_formal_ino ==
+				inum->no_formal_ino);
+	} else if (create) {
+		error = gfs_glock_get(i_gl->gl_sbd,
+				      inum->no_addr, &gfs_iopen_glops,
+				      CREATE, &io_gl);
+		if (!error) {
+			error = inode_create(i_gl, inum, io_gl,
+					     LM_ST_SHARED, ipp);
+			gfs_glock_put(io_gl);
+		}
+	}
+
+	return error;
+}
+
+/**
+ * gfs_inode_hold - hold a struct gfs_inode structure
+ * @ip: The GFS inode
+ *
+ */
+
+void
+gfs_inode_hold(struct gfs_inode *ip)
+{
+	gfs_assert(ip->i_sbd, atomic_read(&ip->i_count) > 0,);
+	atomic_inc(&ip->i_count);
+}
+
+/**
+ * gfs_inode_put - put a struct gfs_inode structure
+ * @ip: The GFS inode
+ *
+ */
+
+void
+gfs_inode_put(struct gfs_inode *ip)
+{
+	gfs_assert(ip->i_sbd, atomic_read(&ip->i_count) > 0,);
+	atomic_dec(&ip->i_count);
+}
+
+/**
+ * gfs_inode_destroy - Destroy a GFS inode structure with no references on it
+ * @ip: The GFS inode
+ *
+ * Also, unhold the iopen glock and release indirect addressing buffers.
+ * This function must be called with a glocks held on the inode and 
+ *   the associated iopen.
+ *
+ */
+
+void
+gfs_inode_destroy(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_glock *io_gl = ip->i_iopen_gh.gh_gl;
+	struct gfs_glock *i_gl = ip->i_gl;
+
+	gfs_assert_warn(sdp, !atomic_read(&ip->i_count));
+	gfs_assert(sdp, gl2gl(io_gl) == i_gl,);
+
+	/* Unhold the iopen glock */
+	spin_lock(&io_gl->gl_spin);
+	gl2gl(io_gl) = NULL;
+	gfs_glock_put(i_gl);
+	spin_unlock(&io_gl->gl_spin);
+
+	gfs_glock_dq_uninit(&ip->i_iopen_gh);
+
+	/* Release indirect addressing buffers, destroy the GFS inode struct */
+	gfs_flush_meta_cache(ip);
+	kmem_cache_free(gfs_inode_cachep, ip);
+
+	gl2ip(i_gl) = NULL;
+	gfs_glock_put(i_gl);
+
+	atomic_dec(&sdp->sd_inode_count);
+}
+
+/**
+ * dinode_mark_unused - Set UNUSED flag in on-disk dinode
+ * @ip:
+ *
+ * Also:
+ * --  Increment incarnation number, to indicate that it no longer
+ *       represents the old inode.
+ * --  Update change time (ctime)
+ *
+ * Returns: errno
+ */
+
+static int
+dinode_mark_unused(struct gfs_inode *ip)
+{
+	struct buffer_head *dibh;
+	struct gfs_dinode *di;
+	uint32_t incarn;
+	uint64_t ctime;
+	uint32_t flags;
+	int error;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		return error;
+
+	di = (struct gfs_dinode *)dibh->b_data;
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+
+	incarn = gfs32_to_cpu(di->di_header.mh_incarn) + 1;
+	di->di_header.mh_incarn = cpu_to_gfs32(incarn);
+
+	ctime = get_seconds();
+	di->di_ctime = cpu_to_gfs64(ctime);
+
+	flags = (gfs32_to_cpu(di->di_flags)) | GFS_DIF_UNUSED;
+	di->di_flags = cpu_to_gfs32(flags);
+
+	brelse(dibh);
+
+	return 0;
+}
+
+/**
+ * dinode_dealloc - Put deallocate a dinode
+ * @ip: The GFS inode
+ *
+ * Returns: errno
+ */
+
+static int
+dinode_dealloc(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al;
+	struct gfs_rgrpd *rgd;
+	int error;
+
+	if (ip->i_di.di_blocks != 1) {
+		if (gfs_consist_inode(ip))
+			gfs_dinode_print(&ip->i_di);
+		return -EIO;
+	}
+
+	al = gfs_alloc_get(ip);
+
+	error = gfs_quota_hold_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto out;
+
+	error = gfs_rindex_hold(sdp, &al->al_ri_gh);
+	if (error)
+		goto out_qs;
+
+	rgd = gfs_blk2rgrpd(sdp, ip->i_num.no_addr);
+	if (!rgd) {
+		gfs_consist_inode(ip);
+		error = -EIO;
+		goto out_rindex_relse;
+	}
+
+	error = gfs_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &al->al_rgd_gh);
+	if (error)
+		goto out_rindex_relse;
+
+	/* Trans may require:
+	   One block for the RG header.
+	   One block for the dinode bit.
+	   One block for the dinode.
+	   We also need a block for the unlinked change.
+	   One block for the quota change. */
+
+	error = gfs_trans_begin(sdp, 3, 2);
+	if (error)
+		goto out_rg_gunlock;
+
+	/* Set the UNUSED flag in the on-disk dinode block, increment incarn */
+	error = dinode_mark_unused(ip);
+	if (error)
+		goto out_end_trans;
+
+	/* De-allocate on-disk dinode block to FREEMETA */
+	gfs_difree(rgd, ip);
+
+	gfs_trans_add_unlinked(sdp, GFS_LOG_DESC_IDA, &ip->i_num);
+	clear_bit(GLF_STICKY, &ip->i_gl->gl_flags);
+
+ out_end_trans:
+	gfs_trans_end(sdp);
+
+ out_rg_gunlock:
+	gfs_glock_dq_uninit(&al->al_rgd_gh);
+
+ out_rindex_relse:
+	gfs_glock_dq_uninit(&al->al_ri_gh);
+
+ out_qs:
+	gfs_quota_unhold_m(ip);
+
+ out:
+	gfs_alloc_put(ip);
+
+	return error;
+}
+
+/**
+ * inode_dealloc - Deallocate all on-disk blocks for an inode (dinode)
+ * @sdp: the filesystem
+ * @inum: the inode number to deallocate
+ * @io_gh: a holder for the iopen glock for this inode
+ *
+ * De-allocates all on-disk blocks, data and metadata, associated with an inode.
+ * All metadata blocks become GFS_BLKST_FREEMETA.
+ * All data blocks become GFS_BLKST_FREE.
+ * Also de-allocates incore gfs_inode structure.
+ *
+ * Returns: errno
+ */
+
+static int
+inode_dealloc(struct gfs_sbd *sdp, struct gfs_inum *inum,
+		struct gfs_holder *io_gh)
+{
+	struct gfs_inode *ip;
+	struct gfs_holder i_gh;
+	int error;
+
+	/* Lock the inode as we blow it away */
+	error = gfs_glock_nq_num(sdp,
+				 inum->no_formal_ino, &gfs_inode_glops,
+				 LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	/* We reacquire the iopen lock here to avoid a race with the NFS server
+	   calling gfs_read_inode() with the inode number of a inode we're in
+	   the process of deallocating.  And we can't keep our hold on the lock
+	   from inode_dealloc_init() for deadlock reasons.  We do, however,
+	   overlap this iopen lock with the one to be acquired EX within
+	   inode_create(), below (recursive EX locks will be granted to same
+	   holder process, i.e. this process). */
+
+	gfs_holder_reinit(LM_ST_EXCLUSIVE, LM_FLAG_TRY, io_gh);
+	error = gfs_glock_nq(io_gh);
+	switch (error) {
+	case 0:
+		break;
+	case GLR_TRYFAILED:
+		error = 0;
+		goto fail;
+	default:
+		goto fail;
+	}
+
+	gfs_assert_warn(sdp, !gl2ip(i_gh.gh_gl));
+	error = inode_create(i_gh.gh_gl, inum, io_gh->gh_gl, LM_ST_EXCLUSIVE,
+			     &ip);
+
+	gfs_glock_dq(io_gh);
+
+	if (error)
+		goto fail;
+
+	/* Verify disk (d)inode, gfs inode, and VFS (v)inode are unused */
+	if (ip->i_di.di_nlink) {
+		if (gfs_consist_inode(ip))
+			gfs_dinode_print(&ip->i_di);
+		error = -EIO;
+		goto fail_iput;
+	}
+	gfs_assert_warn(sdp, atomic_read(&ip->i_count) == 1);
+	gfs_assert_warn(sdp, !ip->i_vnode);
+
+	/* Free all on-disk directory leaves (if any) to FREEMETA state */
+	if (ip->i_di.di_type == GFS_FILE_DIR &&
+	    (ip->i_di.di_flags & GFS_DIF_EXHASH)) {
+		error = gfs_dir_exhash_free(ip);
+		if (error)
+			goto fail_iput;
+	}
+
+	/* Free all on-disk extended attribute blocks to FREEMETA state */
+	if (ip->i_di.di_eattr) {
+		error = gfs_ea_dealloc(ip);
+		if (error)
+			goto fail_iput;
+	}
+
+	/* Free all data blocks to FREE state, and meta blocks to FREEMETA */
+	error = gfs_shrink(ip, 0, NULL);
+	if (error)
+		goto fail_iput;
+
+	/* Set UNUSED flag and increment incarn # in on-disk dinode block,
+	   and de-alloc the block to FREEMETA */
+	error = dinode_dealloc(ip);
+	if (error)
+		goto fail_iput;
+
+	/* Free the GFS inode structure, unhold iopen and inode glocks */
+	gfs_inode_put(ip);
+	gfs_inode_destroy(ip);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return 0;
+
+ fail_iput:
+	gfs_inode_put(ip);
+	gfs_inode_destroy(ip);
+
+ fail:
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * inode_dealloc_init - Try to deallocate an initialized on-disk inode (dinode)
+ *      and all of its associated data and meta blocks
+ * @sdp: the filesystem
+ *
+ * Returns: 0 on success, -errno on error, 1 on busy (inode open)
+ */
+
+static int
+inode_dealloc_init(struct gfs_sbd *sdp, struct gfs_inum *inum)
+{
+	struct gfs_holder io_gh;
+	int error = 0;
+
+	/* If not busy (on this node), de-alloc GFS incore inode, releasing
+	   any indirect addressing buffers, and unholding iopen glock */
+	gfs_try_toss_inode(sdp, inum);
+
+	/* Does another process (cluster-wide) have this inode open? */
+	error = gfs_glock_nq_num(sdp,
+				 inum->no_addr, &gfs_iopen_glops,
+				 LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB, &io_gh);
+	switch (error) {
+	case 0:
+		break;
+	case GLR_TRYFAILED:
+		return 1;
+	default:
+		return error;
+	}
+
+	/* Unlock here to prevent deadlock */
+	gfs_glock_dq(&io_gh);
+
+	/* No other process in the entire cluster has this inode open;
+	   we can remove it and all of its associated blocks from disk */
+	error = inode_dealloc(sdp, inum, &io_gh);
+	gfs_holder_uninit(&io_gh);
+
+	return error;
+}
+
+/**
+ * inode_dealloc_uninit - dealloc an uninitialized on-disk inode (dinode) block
+ * @sdp: the filesystem
+ *
+ * Create a transaction to change dinode block's alloc state to FREEMETA
+ *
+ * Returns: 0 on success, -errno on error, 1 on busy
+ */
+
+static int
+inode_dealloc_uninit(struct gfs_sbd *sdp, struct gfs_inum *inum)
+{
+	struct gfs_rgrpd *rgd;
+	struct gfs_holder ri_gh, rgd_gh;
+	int error;
+
+	error = gfs_rindex_hold(sdp, &ri_gh);
+	if (error)
+		return error;
+
+	rgd = gfs_blk2rgrpd(sdp, inum->no_addr);
+	if (!rgd) {
+		gfs_consist(sdp);
+		error = -EIO;
+		goto fail;
+	}
+
+	error = gfs_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &rgd_gh);
+	if (error)
+		goto fail;
+
+	/* Trans may require:
+	   One block for the RG header.
+	   One block for the dinode bit.
+	   We also need a block for the unlinked change. */
+
+	error = gfs_trans_begin(sdp, 2, 1);
+	if (error)
+		goto fail_gunlock;
+
+	gfs_difree_uninit(rgd, inum->no_addr);
+	gfs_trans_add_unlinked(sdp, GFS_LOG_DESC_IDA, inum);
+
+	gfs_trans_end(sdp);
+
+	gfs_glock_dq_uninit(&rgd_gh);
+	gfs_glock_dq_uninit(&ri_gh);
+
+	return 0;
+
+ fail_gunlock:
+	gfs_glock_dq_uninit(&rgd_gh);
+
+ fail:
+	gfs_glock_dq_uninit(&ri_gh);
+
+	return error;	
+}
+
+/**
+ * gfs_inode_dealloc - Grab an unlinked inode off the list and try to free it.
+ * @sdp: the filesystem
+ *
+ * Returns: 0 on success, -errno on error, 1 on busy
+ */
+
+int
+gfs_inode_dealloc(struct gfs_sbd *sdp, struct gfs_inum *inum)
+{
+	if (inum->no_formal_ino)
+		return inode_dealloc_init(sdp, inum);
+	else
+		return inode_dealloc_uninit(sdp, inum);
+}
+
+/**
+ * gfs_change_nlink - Change nlink count on inode
+ * @ip: The GFS inode
+ * @diff: The change in the nlink count required
+ *
+ * Returns: errno
+ */
+
+int
+gfs_change_nlink(struct gfs_inode *ip, int diff)
+{
+	struct buffer_head *dibh;
+	uint32_t nlink;
+	int error;
+
+	nlink = ip->i_di.di_nlink + diff;
+
+	/* Tricky.  If we are reducing the nlink count,
+	   but the new value ends up being bigger than the
+	   old one, we must have underflowed. */
+	if (diff < 0 && nlink > ip->i_di.di_nlink) {
+		if (gfs_consist_inode(ip))
+			gfs_dinode_print(&ip->i_di);
+		return -EIO;
+	}
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		return error;
+
+	ip->i_di.di_nlink = nlink;
+	ip->i_di.di_ctime = get_seconds();
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+	brelse(dibh);
+
+	return 0;
+}
+
+/**
+ * gfs_lookupi - Look up a filename in a directory and return its inode
+ * @d_gh: An initialized holder for the directory glock
+ * @name: The name of the inode to look for
+ * @is_root: If TRUE, ignore the caller's permissions
+ * @i_gh: An uninitialized holder for the new inode glock
+ *
+ * There will always be a vnode (Linux VFS inode) for the d_gh inode unless
+ *   @is_root is true.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_lookupi(struct gfs_holder *d_gh, struct qstr *name,
+	    int is_root, struct gfs_holder *i_gh)
+{
+	struct gfs_inode *dip = gl2ip(d_gh->gh_gl);
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_glock *gl;
+	struct gfs_inode *ip;
+	struct gfs_inum inum, inum2;
+	unsigned int type;
+	int error;
+
+	i_gh->gh_gl = NULL;
+
+	if (!name->len || name->len > GFS_FNAMESIZE)
+		return -ENAMETOOLONG;
+
+	if (gfs_filecmp(name, ".", 1) ||
+	    (gfs_filecmp(name, "..", 2) && dip == sdp->sd_rooti)) {
+		gfs_holder_reinit(LM_ST_SHARED, 0, d_gh);
+		error = gfs_glock_nq(d_gh);
+		if (!error) {
+			error = gfs_glock_nq_init(dip->i_gl,
+						  LM_ST_SHARED, 0,
+						  i_gh);
+			if (error) {
+				gfs_glock_dq(d_gh);
+				return error;
+			}
+			gfs_inode_hold(dip);
+		}
+		return error;
+	}
+
+	if (gfs_assert_warn(sdp, !gfs_glock_is_locked_by_me(d_gh->gh_gl)))
+		return -EINVAL;
+
+	gfs_holder_reinit(LM_ST_SHARED, 0, d_gh);
+	error = gfs_glock_nq(d_gh);
+	if (error)
+		return error;
+
+	if (!is_root) {
+		error = permission(dip->i_vnode, MAY_EXEC, NULL);
+		if (error) {
+			gfs_glock_dq(d_gh);
+			return error;
+		}
+	}
+
+	error = gfs_dir_search(dip, name, &inum, &type);
+	if (error) {
+		gfs_glock_dq(d_gh);
+		if (error == -ENOENT)
+			error = 0;
+		return error;
+	}
+
+ restart:
+	error = gfs_glock_get(sdp, inum.no_formal_ino, &gfs_inode_glops,
+			      CREATE, &gl);
+	if (error) {
+		gfs_glock_dq(d_gh);
+		return error;
+	}
+
+	/*  Acquire the second lock  */
+
+	if (gl->gl_name.ln_number < dip->i_gl->gl_name.ln_number) {
+		gfs_glock_dq(d_gh);
+
+		error = gfs_glock_nq_init(gl, LM_ST_SHARED,
+					  LM_FLAG_ANY | GL_LOCAL_EXCL,
+					  i_gh);
+		if (error)
+			goto out;
+
+		gfs_holder_reinit(LM_ST_SHARED, 0, d_gh);
+		error = gfs_glock_nq(d_gh);
+		if (error) {
+			gfs_glock_dq_uninit(i_gh);
+			goto out;
+		}
+
+		if (!is_root) {
+			error = permission(dip->i_vnode, MAY_EXEC, NULL);
+			if (error) {
+				gfs_glock_dq(d_gh);
+				gfs_glock_dq_uninit(i_gh);
+				goto out;
+			}
+		}
+
+		error = gfs_dir_search(dip, name, &inum2, &type);
+		if (error) {
+			gfs_glock_dq(d_gh);
+			gfs_glock_dq_uninit(i_gh);
+			if (error == -ENOENT)
+				error = 0;
+			goto out;
+		}
+
+		if (!gfs_inum_equal(&inum, &inum2)) {
+			gfs_glock_dq_uninit(i_gh);
+			gfs_glock_put(gl);
+			inum = inum2;
+			goto restart;
+		}
+	} else {
+		error = gfs_glock_nq_init(gl, LM_ST_SHARED,
+					  LM_FLAG_ANY | GL_LOCAL_EXCL,
+					  i_gh);
+		if (error) {
+			gfs_glock_dq(d_gh);
+			goto out;
+		}
+	}
+
+	error = gfs_inode_get(gl, &inum, CREATE, &ip);
+	if (error) {
+		gfs_glock_dq(d_gh);
+		gfs_glock_dq_uninit(i_gh);
+	} else if (ip->i_di.di_type != type) {
+		gfs_consist_inode(dip);
+		gfs_inode_put(ip);
+		gfs_glock_dq(d_gh);
+		gfs_glock_dq_uninit(i_gh);
+		error = -EIO;
+	}
+
+ out:
+	gfs_glock_put(gl);
+
+	return error;
+}
+
+/**
+ * create_ok - OK to create a new on-disk inode here?
+ * @dip:  Directory in which dinode is to be created
+ * @name:  Name of new dinode
+ * @type:  GFS_FILE_XXX (regular file, dir, etc.)
+ *
+ * Returns: errno
+ */
+
+static int
+create_ok(struct gfs_inode *dip, struct qstr *name, unsigned int type)
+{
+	int error;
+
+	error = permission(dip->i_vnode, MAY_WRITE | MAY_EXEC, NULL);
+	if (error)
+		return error;
+
+	/*  Don't create entries in an unlinked directory  */
+
+	if (!dip->i_di.di_nlink)
+		return -EPERM;
+
+	error = gfs_dir_search(dip, name, NULL, NULL);
+	switch (error) {
+	case -ENOENT:
+		error = 0;
+		break;
+	case 0:
+		return -EEXIST;
+	default:
+		return error;
+	}
+
+	if (dip->i_di.di_entries == (uint32_t)-1)
+		return -EFBIG;
+	if (type == GFS_FILE_DIR && dip->i_di.di_nlink == (uint32_t)-1)
+		return -EMLINK;
+
+	return 0;
+}
+
+/**
+ * dinode_alloc - Create an on-disk inode
+ * @dip:  Directory in which to create the dinode
+ * @ul:
+ *
+ * Since this dinode is not yet linked, we also create an unlinked inode
+ *   descriptor.
+ *
+ * Returns: errno
+ */
+
+static int
+dinode_alloc(struct gfs_inode *dip, struct gfs_unlinked **ul)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_alloc *al;
+	struct gfs_inum inum;
+	int error;
+
+	/* Create in-place allocation structure, reserve 1 dinode */
+	al = gfs_alloc_get(dip);
+	al->al_requested_di = 1;
+	error = gfs_inplace_reserve(dip);
+	if (error)
+		goto out;
+
+	error = gfs_trans_begin(sdp, al->al_rgd->rd_ri.ri_length, 1);
+	if (error)
+		goto out_inplace;
+
+	inum.no_formal_ino = 0;
+	error = gfs_dialloc(dip, &inum.no_addr);
+	if (error)
+		goto out_end_trans;
+
+	*ul = gfs_trans_add_unlinked(sdp, GFS_LOG_DESC_IUL, &inum);
+	gfs_unlinked_lock(sdp, *ul);
+
+	gfs_trans_add_gl(dip->i_gl);
+
+ out_end_trans:
+	gfs_trans_end(sdp);
+
+ out_inplace:
+	gfs_inplace_release(dip);
+
+ out:
+	gfs_alloc_put(dip);
+
+	return error;
+}
+
+/**
+ * pick_formal_ino - Pick a formal inode number for a given inode
+ * @sdp: the filesystem
+ * @inum: the inode number structure
+ *
+ */
+
+static void
+pick_formal_ino(struct gfs_sbd *sdp, struct gfs_inum *inum)
+{
+	/*  This won't always be true  */
+	inum->no_formal_ino = inum->no_addr;
+}
+
+/**
+ * make_dinode - Fill in a new dinode structure
+ * @dip: the directory this inode is being created in
+ * @gl: The glock covering the new inode
+ * @inum: the inode number
+ * @type: the file type
+ * @mode: the file permissions
+ * @uid:
+ * @gid:
+ *
+ */
+
+static int
+make_dinode(struct gfs_inode *dip,
+	    struct gfs_glock *gl, struct gfs_inum *inum,
+	    unsigned int type, unsigned int mode,
+	    unsigned int uid, unsigned int gid)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_dinode di;
+	struct buffer_head *dibh;
+	struct gfs_rgrpd *rgd;
+	int error;
+
+	error = gfs_dread(gl, inum->no_addr,
+			  DIO_NEW | DIO_START | DIO_WAIT,
+			  &dibh);
+	if (error)
+		return error;
+
+	gfs_trans_add_bh(gl, dibh);
+	gfs_metatype_set(dibh, GFS_METATYPE_DI, GFS_FORMAT_DI);
+	gfs_buffer_clear_tail(dibh, sizeof(struct gfs_dinode));
+
+	memset(&di, 0, sizeof(struct gfs_dinode));
+
+	gfs_meta_header_in(&di.di_header, dibh->b_data);
+
+	di.di_num = *inum;
+
+	di.di_mode = mode & S_IALLUGO;
+	di.di_uid = uid;
+	di.di_gid = gid;
+	di.di_nlink = 1;
+	di.di_blocks = 1;
+	di.di_atime = di.di_mtime = di.di_ctime = get_seconds();
+
+	rgd = gfs_blk2rgrpd(sdp, inum->no_addr);
+	if (!rgd) {
+		if (gfs_consist(sdp))
+			printk("GFS: fsid=%s: block = %"PRIu64"\n",
+			       sdp->sd_fsname, inum->no_addr);
+		brelse(dibh);
+		return -EIO;
+	}
+
+	di.di_rgrp = rgd->rd_ri.ri_addr;
+	di.di_goal_rgrp = di.di_rgrp;
+	di.di_goal_dblk = di.di_goal_mblk = inum->no_addr - rgd->rd_ri.ri_data1;
+
+	if (type == GFS_FILE_REG) {
+		if ((dip->i_di.di_flags & GFS_DIF_INHERIT_JDATA) ||
+		    gfs_tune_get(sdp, gt_new_files_jdata))
+			di.di_flags |= GFS_DIF_JDATA;
+		if ((dip->i_di.di_flags & GFS_DIF_INHERIT_DIRECTIO) ||
+		    gfs_tune_get(sdp, gt_new_files_directio))
+			di.di_flags |= GFS_DIF_DIRECTIO;
+	} else if (type == GFS_FILE_DIR) {
+		di.di_flags |= (dip->i_di.di_flags & GFS_DIF_INHERIT_DIRECTIO);
+		di.di_flags |= (dip->i_di.di_flags & GFS_DIF_INHERIT_JDATA);
+	}
+
+	di.di_type = type;
+
+	gfs_dinode_out(&di, dibh->b_data);
+	brelse(dibh);
+
+	return 0;
+}
+
+/**
+ * inode_init_and_link -
+ * @dip:
+ * @name:
+ * @inum:
+ * @gl:
+ * @type:
+ * @mode:
+ *
+ * Returns: errno
+ */
+
+static int
+inode_init_and_link(struct gfs_inode *dip, struct qstr *name,
+		    struct gfs_inum *inum, struct gfs_glock *gl,
+		    unsigned int type, mode_t mode)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_alloc *al;
+	struct gfs_inode *ip;
+	unsigned int uid, gid;
+	int alloc_required;
+	void *acl_a_data = NULL, *acl_d_data = NULL;
+	unsigned int acl_size = 0, acl_blocks = 0;
+	int error;
+
+	if (sdp->sd_args.ar_suiddir &&
+	    (dip->i_di.di_mode & S_ISUID) &&
+	    dip->i_di.di_uid) {
+		if (type == GFS_FILE_DIR)
+			mode |= S_ISUID;
+		else if (dip->i_di.di_uid != current->fsuid)
+			mode &= ~07111;
+		uid = dip->i_di.di_uid;
+	} else
+		uid = current->fsuid;
+
+	if (dip->i_di.di_mode & S_ISGID) {
+		if (type == GFS_FILE_DIR)
+			mode |= S_ISGID;
+		gid = dip->i_di.di_gid;
+	} else
+		gid = current->fsgid;
+
+	error = gfs_acl_new_prep(dip, type, &mode,
+				 &acl_a_data, &acl_d_data,
+				 &acl_size, &acl_blocks);
+	if (error)
+		return error;
+
+	al = gfs_alloc_get(dip);
+
+	error = gfs_quota_lock_m(dip, uid, gid);
+	if (error)
+		goto fail;
+
+	error = gfs_quota_check(dip, uid, gid);
+	if (error)
+		goto fail_gunlock_q;
+
+	if (acl_blocks)
+		alloc_required = TRUE;
+	else {
+		error = gfs_diradd_alloc_required(dip, name, &alloc_required);
+		if (error)
+			goto fail_gunlock_q;
+	}
+
+	if (alloc_required) {
+		error = gfs_quota_check(dip, dip->i_di.di_uid, dip->i_di.di_gid);
+		if (error)
+			goto fail_gunlock_q;
+
+		al->al_requested_meta = sdp->sd_max_dirres + acl_blocks;
+
+		error = gfs_inplace_reserve(dip);
+		if (error)
+			goto fail_gunlock_q;
+
+		/* Trans may require:
+		   blocks for two dinodes, the directory blocks necessary for
+		   a new entry, RG bitmap blocks for an allocation,
+		   and one block for a quota change and
+		   one block for an unlinked tag. */
+
+		error = gfs_trans_begin(sdp,
+					2 + sdp->sd_max_dirres + acl_blocks +
+					al->al_rgd->rd_ri.ri_length, 2);
+		if (error)
+			goto fail_inplace;
+	} else {
+		error = gfs_rindex_hold(sdp, &al->al_ri_gh);
+		if (error)
+			goto fail_gunlock_q;
+
+		/* Trans may require:
+		   blocks for two dinodes, a leaf block,
+		   and one block for a quota change and
+		   one block for an unlinked tag. */
+
+		error = gfs_trans_begin(sdp, 3, 2);
+		if (error)
+			goto fail_inplace;
+	}
+
+	error = gfs_dir_add(dip, name, inum, type);
+	if (error)
+		goto fail_end_trans;
+
+	error = make_dinode(dip, gl, inum, type, mode, uid, gid);
+	if (error)
+		goto fail_end_trans;
+
+	al->al_ul = gfs_trans_add_unlinked(sdp, GFS_LOG_DESC_IDA,
+					   &(struct gfs_inum){0, inum->no_addr});
+	gfs_trans_add_quota(sdp, +1, uid, gid);
+
+	error = gfs_inode_get(gl, inum, CREATE, &ip);
+
+	/* This should only fail if we are already shutdown. */
+	if (gfs_assert_withdraw(sdp, !error))
+		goto fail_end_trans;
+
+	if (acl_blocks)
+		error = gfs_acl_new_init(dip, ip,
+					 acl_a_data, acl_d_data,
+					 acl_size);
+
+	if (!alloc_required)
+		gfs_glock_dq_uninit(&al->al_ri_gh);
+
+	return error;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_inplace:
+	if (alloc_required)
+		gfs_inplace_release(dip);
+	else
+		gfs_glock_dq_uninit(&al->al_ri_gh);
+
+ fail_gunlock_q:
+	gfs_quota_unlock_m(dip);
+
+ fail:
+	gfs_alloc_put(dip);
+	if (acl_a_data)
+		kfree(acl_a_data);
+	else if (acl_d_data)
+		kfree(acl_d_data);
+
+	return error;
+}
+
+/**
+ * gfs_createi - Create a new inode
+ * @d_gh: An initialized holder for the directory glock
+ * @name: The name of the new file
+ * @type: The type of dinode (GFS_FILE_REG, GFS_FILE_DIR, GFS_FILE_LNK, ...)
+ * @mode: the permissions on the new inode
+ * @i_gh: An uninitialized holder for the new inode glock
+ *
+ * If the return value is 0, the glocks on both the directory and the new
+ * file are held.  A transaction has been started and an inplace reservation
+ * is held, as well.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_createi(struct gfs_holder *d_gh, struct qstr *name,
+	    unsigned int type, unsigned int mode,
+	    struct gfs_holder *i_gh)
+{
+	struct gfs_inode *dip = gl2ip(d_gh->gh_gl);
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_unlinked *ul;
+	struct gfs_inum inum;
+	struct gfs_holder io_gh;
+	int error;
+
+	if (!name->len || name->len > GFS_FNAMESIZE)
+		return -ENAMETOOLONG;
+
+	gfs_holder_reinit(LM_ST_EXCLUSIVE, 0, d_gh);
+	error = gfs_glock_nq(d_gh);
+	if (error)
+		return error;
+
+	error = create_ok(dip, name, type);
+	if (error)
+		goto fail;
+
+	error = dinode_alloc(dip, &ul);
+	if (error)
+		goto fail;
+
+	inum.no_addr = ul->ul_inum.no_addr;
+	pick_formal_ino(sdp, &inum);
+
+	if (inum.no_formal_ino < dip->i_num.no_formal_ino) {
+		gfs_glock_dq(d_gh);
+
+		error = gfs_glock_nq_num(sdp,
+					 inum.no_formal_ino, &gfs_inode_glops,
+					 LM_ST_EXCLUSIVE, GL_SKIP, i_gh);
+		if (error) {
+			gfs_unlinked_unlock(sdp, ul);
+			return error;
+		}
+
+		gfs_holder_reinit(LM_ST_EXCLUSIVE, 0, d_gh);
+		error = gfs_glock_nq(d_gh);
+		if (error) {
+			gfs_glock_dq_uninit(i_gh);
+			gfs_unlinked_unlock(sdp, ul);
+			return error;
+		}
+
+		error = create_ok(dip, name, type);
+		if (error)
+			goto fail_gunlock_i;
+	} else {
+		error = gfs_glock_nq_num(sdp,
+					 inum.no_formal_ino, &gfs_inode_glops,
+					 LM_ST_EXCLUSIVE, GL_SKIP, i_gh);
+		if (error)
+			goto fail_ul;
+	}
+
+	error = gfs_glock_nq_num(sdp,
+				 inum.no_addr, &gfs_iopen_glops,
+				 LM_ST_SHARED, GL_LOCAL_EXCL | GL_EXACT,
+				 &io_gh);
+	if (error)
+		goto fail_gunlock_i;
+
+	error = inode_init_and_link(dip, name, &inum, i_gh->gh_gl, type, mode);
+	if (error)
+		goto fail_gunlock_io;
+
+	gfs_glock_dq_uninit(&io_gh);
+
+	return 0;
+
+ fail_gunlock_io:
+	gfs_glock_dq_uninit(&io_gh);
+
+ fail_gunlock_i:
+	gfs_glock_dq_uninit(i_gh);
+
+ fail_ul:
+	gfs_unlinked_unlock(sdp, ul);
+
+ fail:
+	gfs_glock_dq(d_gh);
+
+	return error;
+}
+
+/**
+ * gfs_unlinki - Unlink a file
+ * @dip: The inode of the directory
+ * @name: The name of the file to be unlinked
+ * @ip: The inode of the file to be removed
+ *
+ * Assumes Glocks on both dip and ip are held.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_unlinki(struct gfs_inode *dip, struct qstr *name, struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	int error;
+
+	error = gfs_dir_del(dip, name);
+	if (error)
+		return error;
+
+	error = gfs_change_nlink(ip, -1);
+	if (error)
+		return error;
+
+	/* If this inode is being unlinked from the directory structure,
+	   we need to mark that in the log so that it isn't lost during
+	   a crash. */
+
+	if (!ip->i_di.di_nlink) {
+		gfs_trans_add_unlinked(sdp, GFS_LOG_DESC_IUL, &ip->i_num);
+		set_bit(GLF_STICKY, &ip->i_gl->gl_flags);
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_rmdiri - Remove a directory
+ * @dip: The parent directory of the directory to be removed
+ * @name: The name of the directory to be removed
+ * @ip: The GFS inode of the directory to be removed
+ *
+ * Assumes Glocks on dip and ip are held
+ *
+ * Returns: errno
+ */
+
+int
+gfs_rmdiri(struct gfs_inode *dip, struct qstr *name, struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct qstr dotname;
+	int error;
+
+	if (ip->i_di.di_entries != 2) {
+		if (gfs_consist_inode(ip))
+			gfs_dinode_print(&ip->i_di);
+		return -EIO;
+	}
+
+	error = gfs_dir_del(dip, name);
+	if (error)
+		return error;
+
+	error = gfs_change_nlink(dip, -1);
+	if (error)
+		return error;
+
+	dotname.len = 1;
+	dotname.name = ".";
+	error = gfs_dir_del(ip, &dotname);
+	if (error)
+		return error;
+
+	dotname.len = 2;
+	dotname.name = "..";
+	error = gfs_dir_del(ip, &dotname);
+	if (error)
+		return error;
+
+	error = gfs_change_nlink(ip, -2);
+	if (error)
+		return error;
+
+	/* This inode is being unlinked from the directory structure and
+	   we need to mark that in the log so that it isn't lost during
+	   a crash. */
+
+	gfs_trans_add_unlinked(sdp, GFS_LOG_DESC_IUL, &ip->i_num);
+	set_bit(GLF_STICKY, &ip->i_gl->gl_flags);
+
+	return 0;
+}
+
+/*
+ * gfs_unlink_ok - check to see that a inode is still in a directory
+ * @dip: the directory
+ * @name: the name of the file
+ * @ip: the inode
+ *
+ * Assumes that the lock on (at least) @dip is held.
+ *
+ * Returns: 0 if the parent/child relationship is correct, errno if it isn't
+ */
+
+int
+gfs_unlink_ok(struct gfs_inode *dip, struct qstr *name, struct gfs_inode *ip)
+{
+	struct gfs_inum inum;
+	unsigned int type;
+	int error;
+
+	if (IS_IMMUTABLE(ip->i_vnode) || IS_APPEND(ip->i_vnode))
+		return -EPERM;
+
+	if ((dip->i_di.di_mode & S_ISVTX) &&
+	    dip->i_di.di_uid != current->fsuid &&
+	    ip->i_di.di_uid != current->fsuid &&
+	    !capable(CAP_FOWNER))
+		return -EPERM;
+
+	if (IS_APPEND(dip->i_vnode))
+		return -EPERM;
+
+	error = permission(dip->i_vnode, MAY_WRITE | MAY_EXEC, NULL);
+	if (error)
+		return error;
+
+	error = gfs_dir_search(dip, name, &inum, &type);
+	if (error)
+		return error;
+
+	if (inum.no_formal_ino != ip->i_num.no_formal_ino)
+		return -ENOENT;
+
+	if (ip->i_di.di_type != type) {
+		gfs_consist_inode(dip);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * gfs_ok_to_move - check if it's ok to move a directory to another directory
+ * @this: move this
+ * @to: to here
+ *
+ * Follow @to back to the root and make sure we don't encounter @this
+ * Assumes we already hold the rename lock.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_ok_to_move(struct gfs_inode *this, struct gfs_inode *to)
+{
+	struct gfs_sbd *sdp = this->i_sbd;
+	struct gfs_inode *tmp;
+	struct gfs_holder to_gh, tmp_gh;
+	struct qstr dotdot;
+	int error = 0;
+
+	memset(&dotdot, 0, sizeof (struct qstr));
+	dotdot.name = "..";
+	dotdot.len = 2;
+
+	gfs_inode_hold(to);
+
+	for (;;) {
+		if (to == this) {
+			error = -EINVAL;
+			break;
+		}
+		if (to == sdp->sd_rooti) {
+			error = 0;
+			break;
+		}
+
+		gfs_holder_init(to->i_gl, 0, 0, &to_gh);
+
+		error = gfs_lookupi(&to_gh, &dotdot, TRUE, &tmp_gh);
+		if (error) {
+			gfs_holder_uninit(&to_gh);
+			break;
+		}
+		if (!tmp_gh.gh_gl) {
+			gfs_holder_uninit(&to_gh);
+			error = -ENOENT;
+			break;
+		}
+
+		tmp = gl2ip(tmp_gh.gh_gl);
+
+		gfs_glock_dq_uninit(&to_gh);
+		gfs_glock_dq_uninit(&tmp_gh);
+
+		gfs_inode_put(to);
+		to = tmp;
+	}
+
+	gfs_inode_put(to);
+
+	return error;
+}
+
+/**
+ * gfs_readlinki - return the contents of a symlink
+ * @ip: the symlink's inode
+ * @buf: a pointer to the buffer to be filled
+ * @len: a pointer to the length of @buf
+ *
+ * If @buf is too small, a piece of memory is kmalloc()ed and needs
+ * to be freed by the caller.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_readlinki(struct gfs_inode *ip, char **buf, unsigned int *len)
+{
+	struct gfs_holder i_gh;
+	struct buffer_head *dibh;
+	unsigned int x;
+	int error;
+
+	gfs_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &i_gh);
+	error = gfs_glock_nq_atime(&i_gh);
+	if (error) {
+		gfs_holder_uninit(&i_gh);
+		return error;
+	}
+
+	if (!ip->i_di.di_size) {
+		gfs_consist_inode(ip);
+		error = -EIO;
+		goto out;
+	}
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		goto out;
+
+	x = ip->i_di.di_size + 1;
+	if (x > *len) {
+		*buf = kmalloc(x, GFP_KERNEL);
+		if (!*buf) {
+			error = -ENOMEM;
+			goto out_brelse;
+		}
+	}
+
+	memcpy(*buf, dibh->b_data + sizeof(struct gfs_dinode), x);
+	*len = x;
+
+ out_brelse:
+	brelse(dibh);
+
+ out:
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_glock_nq_atime - Acquire a hold on an inode's glock, and
+ *       conditionally update the inode's atime
+ * @gh: the holder to acquire
+ *
+ * Tests atime (access time) for gfs_read, gfs_readdir and gfs_mmap
+ * Update if the difference between the current time and the inode's current
+ * atime is greater than an interval specified at mount (or default).
+ *
+ * Will not update if GFS mounted NOATIME (this is *the* place where NOATIME
+ *   has an effect) or Read-Only.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_glock_nq_atime(struct gfs_holder *gh)
+{
+	struct gfs_glock *gl = gh->gh_gl;
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_inode *ip = gl2ip(gl);
+	int64_t curtime, quantum = gfs_tune_get(sdp, gt_atime_quantum);
+	unsigned int state;
+	int flags;
+	int error;
+
+	if (gfs_assert_warn(sdp, gh->gh_flags & GL_ATIME) ||
+	    gfs_assert_warn(sdp, !(gh->gh_flags & GL_ASYNC)) ||
+	    gfs_assert_warn(sdp, gl->gl_ops == &gfs_inode_glops))
+		return -EINVAL;
+
+	/* Save original request state of lock holder */
+	state = gh->gh_state;
+	flags = gh->gh_flags;
+
+	error = gfs_glock_nq(gh);
+	if (error)
+		return error;
+
+	if (test_bit(SDF_NOATIME, &sdp->sd_flags) ||
+	    test_bit(SDF_ROFS, &sdp->sd_flags))
+		return 0;
+
+	curtime = get_seconds();
+	if (curtime - ip->i_di.di_atime >= quantum) {
+		/* Get EX hold (force EX glock via !ANY) to write the dinode */
+		gfs_glock_dq(gh);
+		gfs_holder_reinit(LM_ST_EXCLUSIVE,
+				  gh->gh_flags & ~LM_FLAG_ANY,
+				  gh);
+		error = gfs_glock_nq(gh);
+		if (error)
+			return error;
+
+		/* Verify that atime hasn't been updated while we were
+		   trying to get exclusive lock. */
+
+		curtime = get_seconds();
+		if (curtime - ip->i_di.di_atime >= quantum) {
+			struct buffer_head *dibh;
+
+			error = gfs_trans_begin(sdp, 1, 0);
+			if (error == -EROFS)
+				return 0;
+			if (error)
+				goto fail;
+
+			error = gfs_get_inode_buffer(ip, &dibh);
+			if (error)
+				goto fail_end_trans;
+
+			ip->i_di.di_atime = curtime;
+
+			gfs_trans_add_bh(ip->i_gl, dibh);
+			gfs_dinode_out(&ip->i_di, dibh->b_data);
+			brelse(dibh);
+
+			gfs_trans_end(sdp);
+		}
+
+		/* If someone else has asked for the glock,
+		   unlock and let them have it. Then reacquire
+		   in the original state. */
+		if (gfs_glock_is_blocking(gl)) {
+			gfs_glock_dq(gh);
+			gfs_holder_reinit(state, flags, gh);
+			return gfs_glock_nq(gh);
+		}
+	}
+
+	return 0;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail:
+	gfs_glock_dq(gh);
+
+	return error;
+}
+
+/**
+ * glock_compare_atime - Compare two struct gfs_glock structures for gfs_sort()
+ * @arg_a: the first structure
+ * @arg_b: the second structure
+ *
+ * Sort order determined by (in order of priority):
+ * -- lock number
+ * -- lock state (SHARED > EXCLUSIVE or GL_ATIME, which can demand EXCLUSIVE)
+ *
+ * Returns: 1 if A > B
+ *         -1 if A < B
+ *          0 if A = B
+ */
+
+static int
+glock_compare_atime(const void *arg_a, const void *arg_b)
+{
+	struct gfs_holder *gh_a = *(struct gfs_holder **)arg_a;
+	struct gfs_holder *gh_b = *(struct gfs_holder **)arg_b;
+	struct lm_lockname *a = &gh_a->gh_gl->gl_name;
+	struct lm_lockname *b = &gh_b->gh_gl->gl_name;
+	int ret = 0;
+
+	if (a->ln_number > b->ln_number)
+		ret = 1;
+	else if (a->ln_number < b->ln_number)
+		ret = -1;
+	else {
+		if (gh_a->gh_state == LM_ST_SHARED &&
+		    gh_b->gh_state == LM_ST_EXCLUSIVE)
+			ret = 1;
+		else if (gh_a->gh_state == LM_ST_SHARED &&
+			 (gh_b->gh_flags & GL_ATIME))
+			ret = 1;
+	}
+
+	return ret;
+}
+
+/**
+ * gfs_glock_nq_m_atime - acquire multiple glocks where one may need an
+ *      atime update
+ * @num_gh: the number of structures
+ * @ghs: an array of struct gfs_holder structures
+ *
+ * Returns: 0 on success (all glocks acquired),
+ *          errno on failure (no glocks acquired)
+ */
+
+int
+gfs_glock_nq_m_atime(unsigned int num_gh, struct gfs_holder *ghs)
+{
+	struct gfs_holder **p;
+	unsigned int x;
+	int error = 0;
+
+	if (!num_gh)
+		return 0;
+
+	if (num_gh == 1) {
+		ghs->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC);
+		if (ghs->gh_flags & GL_ATIME)
+			error = gfs_glock_nq_atime(ghs);
+		else
+			error = gfs_glock_nq(ghs);
+		return error;
+	}
+
+	p = kmalloc(num_gh * sizeof(struct gfs_holder *), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+
+	for (x = 0; x < num_gh; x++)
+		p[x] = &ghs[x];
+
+	gfs_sort(p, num_gh, sizeof(struct gfs_holder *), glock_compare_atime);
+
+	for (x = 0; x < num_gh; x++) {
+		p[x]->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC);
+
+		if (p[x]->gh_flags & GL_ATIME)
+			error = gfs_glock_nq_atime(p[x]);
+		else
+			error = gfs_glock_nq(p[x]);
+
+		if (error) {
+			while (x--)
+				gfs_glock_dq(p[x]);
+			break;
+		}
+	}
+
+	kfree(p);
+	return error;
+}
+
+/**
+ * gfs_try_toss_vnode - See if we can toss a vnode from memory
+ * @ip: the inode
+ *
+ * Returns:  TRUE if the vnode was tossed
+ */
+
+void
+gfs_try_toss_vnode(struct gfs_inode *ip)
+{
+	struct inode *inode;
+
+	inode = gfs_iget(ip, NO_CREATE);
+	if (!inode)
+		return;
+
+	d_prune_aliases(inode);
+
+	if (ip->i_di.di_type == GFS_FILE_DIR) {
+		struct list_head *head = &inode->i_dentry;
+		struct dentry *d = NULL;
+
+		spin_lock(&dcache_lock);
+		if (list_empty(head))
+			spin_unlock(&dcache_lock);
+		else {
+			d = list_entry(head->next, struct dentry, d_alias);
+			dget_locked(d);
+			spin_unlock(&dcache_lock);
+
+			if (have_submounts(d))
+				dput(d);
+			else {
+				shrink_dcache_parent(d);
+				dput(d);
+				d_prune_aliases(inode);
+			}
+		}
+	}
+
+	inode->i_nlink = 0;
+	iput(inode);
+}
+
+
+static int
+__gfs_setattr_simple(struct gfs_inode *ip, struct iattr *attr)
+{
+	struct buffer_head *dibh;
+	int error;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (!error) {
+		inode_setattr(ip->i_vnode, attr);
+		gfs_inode_attr_out(ip);
+
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	return error;
+}
+
+/**
+ * gfs_setattr_simple -
+ * @ip:
+ * @attr:
+ *
+ * Called with a reference on the vnode.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_setattr_simple(struct gfs_inode *ip, struct iattr *attr)
+{
+	int error;
+
+	if (current_transaction)
+		return __gfs_setattr_simple(ip, attr);
+
+	/* Trans may require:
+	   one dinode block. */
+
+	error = gfs_trans_begin(ip->i_sbd, 1, 0);
+	if (error)
+		return error;
+
+	error = __gfs_setattr_simple(ip, attr);
+
+	gfs_trans_end(ip->i_sbd);
+
+	return error;
+}
+
+/**
+ * iah_make_jdata -
+ * @gl:
+ * @inum:
+ *
+ */
+
+static void
+iah_make_jdata(struct gfs_glock *gl, struct gfs_inum *inum)
+{
+	struct buffer_head *bh;
+	struct gfs_dinode *di;
+	uint32_t flags;
+	int error;
+
+	error = gfs_dread(gl, inum->no_addr, DIO_START | DIO_WAIT, &bh);
+
+	/* This should only fail if we are already shutdown. */
+	if (gfs_assert_withdraw(gl->gl_sbd, !error))
+		return;
+
+	di = (struct gfs_dinode *)bh->b_data;
+
+	flags = di->di_flags;
+	flags = gfs32_to_cpu(flags) | GFS_DIF_JDATA;
+	di->di_flags = cpu_to_gfs32(flags);
+
+	brelse(bh);
+}
+
+/**
+ * iah_super_update - Write superblock to disk
+ * @sdp:  filesystem instance structure
+ *
+ * Returns: errno
+ *
+ * Update on-disk superblock, using (modified) data in sdp->sd_sb
+ */
+
+static int
+iah_super_update(struct gfs_sbd *sdp)
+{
+	struct gfs_glock *gl;
+	struct buffer_head *bh;
+	int error;
+
+	error = gfs_glock_get(sdp,
+			      GFS_SB_LOCK, &gfs_meta_glops,
+			      NO_CREATE, &gl);
+	if (gfs_assert_withdraw(sdp, !error && gl)) /* This should already be held. */
+		return -EINVAL;
+
+	error = gfs_dread(gl, GFS_SB_ADDR >> sdp->sd_fsb2bb_shift,
+			  DIO_START | DIO_WAIT, &bh);
+	if (!error) {
+		gfs_trans_add_bh(gl, bh);
+		gfs_sb_out(&sdp->sd_sb, bh->b_data);
+		brelse(bh);
+	}
+
+	gfs_glock_put(gl);
+
+	return error;
+}
+
+/**
+ * inode_alloc_hidden - allocate on-disk inode for a special (hidden) file
+ * @sdp:  the filesystem instance structure
+ * @inum:  new dinode's block # and formal inode #, to be filled
+ *         in by this function.
+ *
+ * Returns: errno
+ *
+ * This function is called only very rarely, when the first-to-mount
+ * node can't find a pre-existing special file (e.g. license or quota file) that
+ * it expects to find.  This should happen only when upgrading from an older
+ * version of the filesystem.
+ *
+ * The @inum must be a member of sdp->sd_sb in order to get updated to on-disk
+ * superblock properly.
+ */
+
+static int
+inode_alloc_hidden(struct gfs_sbd *sdp, struct gfs_inum *inum)
+{
+	struct gfs_inode *dip = sdp->sd_rooti;
+	struct gfs_holder d_gh, i_gh;
+	struct gfs_unlinked *ul;
+	int error;
+
+	error = gfs_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, &d_gh);
+	if (error)
+		return error;
+
+	error = dinode_alloc(dip, &ul);
+	if (error)
+		goto fail;
+
+	inum->no_addr = ul->ul_inum.no_addr;
+	pick_formal_ino(sdp, inum);
+
+	/* Don't worry about deadlock ordering here.  We're the first
+	   mounter and still under the mount lock (i.e. there is no
+	   contention). */
+
+	error = gfs_glock_nq_num(sdp,
+				 inum->no_formal_ino, &gfs_inode_glops,
+				 LM_ST_EXCLUSIVE, GL_SKIP, &i_gh);
+	if (error)
+		goto fail_ul;
+
+	gfs_alloc_get(dip);
+
+	error = gfs_quota_hold_m(dip, 0, 0);
+	if (error)
+		goto fail_al;
+
+	/* Trans may require:
+	   The new inode, the superblock,
+	   and one block for a quota change and
+	   one block for an unlinked tag. */
+      
+	error = gfs_trans_begin(sdp, 2, 2);
+	if (error)
+		goto fail_unhold;
+	
+	error = make_dinode(dip, i_gh.gh_gl, inum, GFS_FILE_REG, 0600, 0, 0);
+	if (error)
+		goto fail_end_trans;
+
+	/* Hidden files get all of their data (not just metadata) journaled */
+	iah_make_jdata(i_gh.gh_gl, inum);
+
+	error = iah_super_update(sdp);
+	if (error)
+		goto fail_end_trans;
+
+	gfs_trans_add_unlinked(sdp, GFS_LOG_DESC_IDA,
+			       &(struct gfs_inum){0, inum->no_addr});
+	gfs_trans_add_quota(sdp, +1, 0, 0);
+	gfs_trans_add_gl(dip->i_gl);
+
+	gfs_trans_end(sdp);
+	gfs_quota_unhold_m(dip);
+	gfs_alloc_put(dip);
+
+	gfs_glock_dq_uninit(&i_gh);
+	gfs_glock_dq_uninit(&d_gh);
+
+	gfs_unlinked_unlock(sdp, ul);
+
+	gfs_log_flush(sdp);
+
+	return 0;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_unhold:
+	gfs_quota_unhold_m(dip);
+
+ fail_al:
+	gfs_alloc_put(dip);
+	gfs_glock_dq_uninit(&i_gh);
+
+ fail_ul:
+	gfs_unlinked_unlock(sdp, ul);
+
+ fail:
+	gfs_glock_dq_uninit(&d_gh);
+
+	return error;
+}
+
+/**
+ * gfs_alloc_qinode - allocate a quota inode
+ * @sdp: The GFS superblock
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+int
+gfs_alloc_qinode(struct gfs_sbd *sdp)
+{
+	return inode_alloc_hidden(sdp, &sdp->sd_sb.sb_quota_di);
+}
+
+/**
+ * gfs_alloc_linode - allocate a license inode
+ * @sdp: The GFS superblock
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+int
+gfs_alloc_linode(struct gfs_sbd *sdp)
+{
+	return inode_alloc_hidden(sdp, &sdp->sd_sb.sb_license_di);
+}
diff -pruN linux-2.6.9.orig/fs/gfs/inode.h linux-2.6.9.debug/fs/gfs/inode.h
--- linux-2.6.9.orig/fs/gfs/inode.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/inode.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,70 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __INODE_DOT_H__
+#define __INODE_DOT_H__
+
+void gfs_inode_attr_in(struct gfs_inode *ip);
+void gfs_inode_attr_out(struct gfs_inode *ip);
+struct inode *gfs_iget(struct gfs_inode *ip, int create);
+
+int gfs_copyin_dinode(struct gfs_inode *ip);
+
+int gfs_inode_get(struct gfs_glock *i_gl, struct gfs_inum *inum, int create,
+		    struct gfs_inode **ipp);
+void gfs_inode_hold(struct gfs_inode *ip);
+void gfs_inode_put(struct gfs_inode *ip);
+void gfs_inode_destroy(struct gfs_inode *ip);
+
+int gfs_inode_dealloc(struct gfs_sbd *sdp, struct gfs_inum *inum);
+
+int gfs_change_nlink(struct gfs_inode *ip, int diff);
+int gfs_lookupi(struct gfs_holder *d_gh, struct qstr *name,
+		int is_root, struct gfs_holder *i_gh);
+int gfs_createi(struct gfs_holder *d_gh, struct qstr *name,
+		unsigned int type, unsigned int mode,
+		struct gfs_holder *i_gh);
+int gfs_unlinki(struct gfs_inode *dip, struct qstr *name, struct gfs_inode *ip);
+int gfs_rmdiri(struct gfs_inode *dip, struct qstr *name, struct gfs_inode *ip);
+int gfs_unlink_ok(struct gfs_inode *dip, struct qstr *name,
+		  struct gfs_inode *ip);
+int gfs_ok_to_move(struct gfs_inode *this, struct gfs_inode *to);
+int gfs_readlinki(struct gfs_inode *ip, char **buf, unsigned int *len);
+
+int gfs_glock_nq_atime(struct gfs_holder *gh);
+int gfs_glock_nq_m_atime(unsigned int num_gh, struct gfs_holder *ghs);
+
+void gfs_try_toss_vnode(struct gfs_inode *ip);
+
+int gfs_setattr_simple(struct gfs_inode *ip, struct iattr *attr);
+
+/*  Backwards compatibility functions  */
+
+int gfs_alloc_qinode(struct gfs_sbd *sdp);
+int gfs_alloc_linode(struct gfs_sbd *sdp);
+
+/*  Inlines  */
+
+static __inline__ int
+gfs_is_stuffed(struct gfs_inode *ip)
+{
+	return !ip->i_di.di_height;
+}
+
+static __inline__ int
+gfs_is_jdata(struct gfs_inode *ip)
+{
+	return ip->i_di.di_flags & GFS_DIF_JDATA;
+}
+
+#endif /* __INODE_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ioctl.c linux-2.6.9.debug/fs/gfs/ioctl.c
--- linux-2.6.9.orig/fs/gfs/ioctl.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ioctl.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1490 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/uaccess.h>
+#include <linux/gfs_ioctl.h>
+
+#include "gfs.h"
+#include "bmap.h"
+#include "dio.h"
+#include "dir.h"
+#include "eattr.h"
+#include "file.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "ioctl.h"
+#include "log.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "super.h"
+#include "trans.h"
+
+typedef int (*gi_filler_t) (struct gfs_inode *ip,
+			    struct gfs_ioctl *gi,
+			    char *buf,
+			    unsigned int size,
+			    unsigned int *count);
+
+#define ARG_SIZE (32)
+
+/**
+ * gi_skeleton - Setup a buffer that functions can print into
+ * @ip:
+ * @gi:
+ * @filler:
+ *
+ * Returns: -errno or count of bytes copied to userspace
+ */
+
+static int
+gi_skeleton(struct gfs_inode *ip, struct gfs_ioctl *gi,
+	    gi_filler_t filler)
+{
+	unsigned int size = gfs_tune_get(ip->i_sbd, gt_lockdump_size);
+        char *buf;
+	unsigned int count = 0;
+        int error;
+
+	if (size > gi->gi_size)
+		size = gi->gi_size;
+
+        buf = kmalloc(size, GFP_KERNEL);
+        if (!buf)
+                return -ENOMEM;
+
+        error = filler(ip, gi, buf, size, &count);
+	if (error)
+		goto out;
+
+	if (copy_to_user(gi->gi_data, buf, count + 1))
+		error = -EFAULT;
+	else
+		error = count + 1;
+
+ out:
+	kfree(buf);
+
+	return error;
+}
+
+/**
+ * gi_get_cookie - Return the "cookie" (identifying string) for a
+ *                 filesystem mount
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */ 
+
+static int
+gi_get_cookie(struct gfs_inode *ip,
+	      struct gfs_ioctl *gi,
+	      char *buf,
+	      unsigned int size,
+	      unsigned int *count)
+{
+        int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	gfs_printf("version 0\n");
+        gfs_printf("%lu", (unsigned long)ip->i_sbd);
+
+        error = 0;
+
+ out:
+        return error;
+}
+
+/**
+ * gi_get_super - Return the "struct gfs_sb" for a filesystem
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int
+gi_get_super(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	struct gfs_holder sb_gh;
+	struct buffer_head *bh;
+	struct gfs_sb *sb;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs_sb))
+		return -EINVAL;
+
+	sb = kmalloc(sizeof(struct gfs_sb), GFP_KERNEL);
+	if (!sb)
+		return -ENOMEM;
+
+	error = gfs_glock_nq_num(sdp,
+				 GFS_SB_LOCK, &gfs_meta_glops,
+				 LM_ST_SHARED, 0, &sb_gh);
+	if (error)
+		goto out;
+
+	error = gfs_dread(sb_gh.gh_gl, GFS_SB_ADDR >> sdp->sd_fsb2bb_shift,
+			  DIO_START | DIO_WAIT, &bh);
+	if (error) {
+		gfs_glock_dq_uninit(&sb_gh);
+		goto out;
+	}
+	gfs_sb_in(sb, bh->b_data);
+	brelse(bh);
+
+	gfs_glock_dq_uninit(&sb_gh);
+
+	if (copy_to_user(gi->gi_data, sb,
+			 sizeof(struct gfs_sb)))
+	        error = -EFAULT;
+	else
+		error = sizeof(struct gfs_sb);
+
+ out:
+	kfree(sb);
+
+	return error;
+}
+
+/**
+ * gi_get_args - Return the mount arguments
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */ 
+
+static int
+gi_get_args(struct gfs_inode *ip,
+	    struct gfs_ioctl *gi,
+	    char *buf,
+	    unsigned int size,
+	    unsigned int *count)
+{
+	struct gfs_args *args = &ip->i_sbd->sd_args;
+	int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	gfs_printf("version 0\n");
+	gfs_printf("lockproto %s\n", args->ar_lockproto);
+	gfs_printf("locktable %s\n", args->ar_locktable);
+	gfs_printf("hostdata %s\n", args->ar_hostdata);
+	gfs_printf("ignore_local_fs %d\n", args->ar_ignore_local_fs);
+	gfs_printf("localcaching %d\n", args->ar_localcaching);
+        gfs_printf("localflocks %d\n", args->ar_localflocks);
+        gfs_printf("oopses_ok %d\n", args->ar_oopses_ok);
+        gfs_printf("upgrade %d\n", args->ar_upgrade);
+	gfs_printf("num_glockd %u\n", args->ar_num_glockd);
+        gfs_printf("posix_acls %d\n", args->ar_posix_acls);
+        gfs_printf("suiddir %d\n", args->ar_suiddir);
+
+	error = 0;
+	
+ out:
+	return error;
+}
+
+/**
+ * gi_get_lockstruct - Return the information in the FS' lockstruct
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */ 
+
+static int
+gi_get_lockstruct(struct gfs_inode *ip,
+		  struct gfs_ioctl *gi,
+		  char *buf,
+		  unsigned int size,
+		  unsigned int *count)
+{
+	struct lm_lockstruct *ls = &ip->i_sbd->sd_lockstruct;
+        int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	gfs_printf("version 0\n");
+        gfs_printf("jid %u\n", ls->ls_jid);
+        gfs_printf("first %u\n", ls->ls_first);
+        gfs_printf("lvb_size %u\n", ls->ls_lvb_size);
+        gfs_printf("flags %d\n", ls->ls_flags);
+
+	error = 0;
+
+ out:
+        return error;
+}
+
+/**
+ * gi_get_stat_gfs - Return a filesystem's space usage information
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */ 
+
+static int
+gi_get_stat_gfs(struct gfs_inode *ip,
+		struct gfs_ioctl *gi,
+		char *buf,
+		unsigned int size,
+		unsigned int *count)
+{
+	struct gfs_stat_gfs sg;
+        int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	error = gfs_stat_gfs(ip->i_sbd, &sg, TRUE);
+	if (error)
+		return error;
+
+	error = -ENOBUFS;
+
+	gfs_printf("version 0\n");
+	gfs_printf("bsize %u\n", ip->i_sbd->sd_sb.sb_bsize);
+        gfs_printf("total_blocks %"PRIu64"\n", sg.sg_total_blocks);
+        gfs_printf("free %"PRIu64"\n", sg.sg_free);
+        gfs_printf("used_dinode %"PRIu64"\n", sg.sg_used_dinode);
+        gfs_printf("free_dinode %"PRIu64"\n", sg.sg_free_dinode);
+        gfs_printf("used_meta %"PRIu64"\n", sg.sg_used_meta);
+        gfs_printf("free_meta %"PRIu64"\n", sg.sg_free_meta);
+
+	error = 0;
+
+ out:
+        return error;
+}
+
+/**
+ * handle_roll - Read a atomic_t as an unsigned int
+ * @a: a counter
+ *
+ * if @a is negative, reset it to zero
+ *
+ * Returns: the value of the counter
+ */
+
+static unsigned int
+handle_roll(atomic_t *a)
+{
+	int x = atomic_read(a);
+	if (x < 0) {
+		atomic_set(a, 0);
+		return 0;
+	}
+	return (unsigned int)x;
+}
+
+/**
+ * gi_get_counters - Return usage counters
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */ 
+
+static int
+gi_get_counters(struct gfs_inode *ip,
+		struct gfs_ioctl *gi,
+		char *buf,
+		unsigned int size,
+		unsigned int *count)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+        int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	gfs_printf("version 0\n");
+	gfs_printf("sd_glock_count:locks::%d\n",
+		   atomic_read(&sdp->sd_glock_count));
+	gfs_printf("sd_glock_held_count:locks held::%d\n",
+		   atomic_read(&sdp->sd_glock_held_count));
+	gfs_printf("sd_inode_count:incore inodes::%d\n",
+		   atomic_read(&sdp->sd_inode_count));
+	gfs_printf("sd_bufdata_count:metadata buffers::%d\n",
+		   atomic_read(&sdp->sd_bufdata_count));
+	gfs_printf("sd_unlinked_ic_count:unlinked inodes::%d\n",
+		   atomic_read(&sdp->sd_unlinked_ic_count));
+	gfs_printf("sd_quota_count:quota IDs::%d\n",
+		   atomic_read(&sdp->sd_quota_count));
+	gfs_printf("sd_log_buffers:incore log buffers::%u\n",
+		   sdp->sd_log_buffers);
+	gfs_printf("sd_log_seg_free:log segments free::%u\n",
+		   sdp->sd_log_seg_free);
+	gfs_printf("ji_nsegment:log segments total::%u\n",
+		   sdp->sd_jdesc.ji_nsegment);
+	gfs_printf("sd_mhc_count:meta header cache entries::%d\n",
+		   atomic_read(&sdp->sd_mhc_count));
+	gfs_printf("sd_depend_count:glock dependencies::%d\n",
+		   atomic_read(&sdp->sd_depend_count));
+	gfs_printf("sd_reclaim_count:glocks on reclaim list::%d\n",
+		   atomic_read(&sdp->sd_reclaim_count));
+	gfs_printf("sd_log_wrap:log wraps::%"PRIu64"\n",
+		   sdp->sd_log_wrap);
+	gfs_printf("sd_lm_outstanding:outstanding LM calls::%d\n",
+		   atomic_read(&sdp->sd_lm_outstanding));
+	gfs_printf("sd_bio_outstanding:outstanding BIO calls::%u\n",
+		   atomic_read(&sdp->sd_bio_outstanding));
+	gfs_printf("sd_fh2dentry_misses:fh2dentry misses:diff:%u\n",
+		   handle_roll(&sdp->sd_fh2dentry_misses));
+	gfs_printf("sd_reclaimed:glocks reclaimed:diff:%u\n",
+		   handle_roll(&sdp->sd_reclaimed));
+	gfs_printf("sd_glock_nq_calls:glock nq calls:diff:%u\n",
+		   handle_roll(&sdp->sd_glock_nq_calls));
+	gfs_printf("sd_glock_dq_calls:glock dq calls:diff:%u\n",
+		   handle_roll(&sdp->sd_glock_dq_calls));
+	gfs_printf("sd_glock_prefetch_calls:glock prefetch calls:diff:%u\n",
+		   handle_roll(&sdp->sd_glock_prefetch_calls));
+	gfs_printf("sd_lm_lock_calls:lm_lock calls:diff:%u\n",
+		   handle_roll(&sdp->sd_lm_lock_calls));
+	gfs_printf("sd_lm_unlock_calls:lm_unlock calls:diff:%u\n",
+		   handle_roll(&sdp->sd_lm_unlock_calls));
+	gfs_printf("sd_lm_callbacks:lm callbacks:diff:%u\n",
+		   handle_roll(&sdp->sd_lm_callbacks));
+	gfs_printf("sd_ops_address:address operations:diff:%u\n",
+		   handle_roll(&sdp->sd_ops_address));
+	gfs_printf("sd_ops_dentry:dentry operations:diff:%u\n",
+		   handle_roll(&sdp->sd_ops_dentry));
+	gfs_printf("sd_ops_export:export operations:diff:%u\n",
+		   handle_roll(&sdp->sd_ops_export));
+	gfs_printf("sd_ops_file:file operations:diff:%u\n",
+		   handle_roll(&sdp->sd_ops_file));
+	gfs_printf("sd_ops_inode:inode operations:diff:%u\n",
+		   handle_roll(&sdp->sd_ops_inode));
+	gfs_printf("sd_ops_super:super operations:diff:%u\n",
+		   handle_roll(&sdp->sd_ops_super));
+	gfs_printf("sd_ops_vm:vm operations:diff:%u\n",
+		   handle_roll(&sdp->sd_ops_vm));
+	gfs_printf("sd_bio_reads:block I/O reads:diff:%u\n",
+		   handle_roll(&sdp->sd_bio_reads) >>
+		   (sdp->sd_sb.sb_bsize_shift - 9));
+	gfs_printf("sd_bio_writes:block I/O writes:diff:%u\n",
+		   handle_roll(&sdp->sd_bio_writes) >>
+		   (sdp->sd_sb.sb_bsize_shift - 9));
+
+        error = 0;
+
+ out:
+        return error;
+}
+
+/**
+ * gi_get_tune - Return current values of the tuneable parameters
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */ 
+
+static int
+gi_get_tune(struct gfs_inode *ip,
+	    struct gfs_ioctl *gi,
+	    char *buf,
+	    unsigned int size,
+	    unsigned int *count)
+{
+        struct gfs_tune *gt = &ip->i_sbd->sd_tune;
+        int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	spin_lock(&gt->gt_spin);
+
+	gfs_printf("version 0\n");
+        gfs_printf("ilimit1 %u\n", gt->gt_ilimit1);
+        gfs_printf("ilimit1_tries %u\n", gt->gt_ilimit1_tries);
+        gfs_printf("ilimit1_min %u\n", gt->gt_ilimit1_min);
+        gfs_printf("ilimit2 %u\n", gt->gt_ilimit2);
+        gfs_printf("ilimit2_tries %u\n", gt->gt_ilimit2_tries);
+        gfs_printf("ilimit2_min %u\n", gt->gt_ilimit2_min);
+        gfs_printf("demote_secs %u\n", gt->gt_demote_secs);
+        gfs_printf("incore_log_blocks %u\n", gt->gt_incore_log_blocks);
+        gfs_printf("jindex_refresh_secs %u\n", gt->gt_jindex_refresh_secs);
+        gfs_printf("depend_secs %u\n", gt->gt_depend_secs);
+        gfs_printf("scand_secs %u\n", gt->gt_scand_secs);
+        gfs_printf("recoverd_secs %u\n", gt->gt_recoverd_secs);
+        gfs_printf("logd_secs %u\n", gt->gt_logd_secs);
+        gfs_printf("quotad_secs %u\n", gt->gt_quotad_secs);
+        gfs_printf("inoded_secs %u\n", gt->gt_inoded_secs);
+        gfs_printf("quota_simul_sync %u\n", gt->gt_quota_simul_sync);
+        gfs_printf("quota_warn_period %u\n", gt->gt_quota_warn_period);
+        gfs_printf("atime_quantum %u\n", gt->gt_atime_quantum);
+        gfs_printf("quota_quantum %u\n", gt->gt_quota_quantum);
+        gfs_printf("quota_scale_num %u\n", gt->gt_quota_scale_num);
+        gfs_printf("quota_scale_den %u\n", gt->gt_quota_scale_den);
+        gfs_printf("quota_enforce %u\n", gt->gt_quota_enforce);
+        gfs_printf("quota_account %u\n", gt->gt_quota_account);
+        gfs_printf("new_files_jdata %u\n", gt->gt_new_files_jdata);
+        gfs_printf("new_files_directio %u\n", gt->gt_new_files_directio);
+        gfs_printf("max_atomic_write %u\n", gt->gt_max_atomic_write);
+        gfs_printf("max_readahead %u\n", gt->gt_max_readahead);
+        gfs_printf("lockdump_size %u\n", gt->gt_lockdump_size);
+        gfs_printf("stall_secs %u\n", gt->gt_stall_secs);
+        gfs_printf("complain_secs %u\n", gt->gt_complain_secs);
+        gfs_printf("reclaim_limit %u\n", gt->gt_reclaim_limit);
+        gfs_printf("entries_per_readdir %u\n", gt->gt_entries_per_readdir);
+        gfs_printf("prefetch_secs %u\n", gt->gt_prefetch_secs);
+        gfs_printf("statfs_slots %u\n", gt->gt_statfs_slots);
+        gfs_printf("max_mhc %u\n", gt->gt_max_mhc);
+        gfs_printf("greedy_default %u\n", gt->gt_greedy_default);
+        gfs_printf("greedy_quantum %u\n", gt->gt_greedy_quantum);
+        gfs_printf("greedy_max %u\n", gt->gt_greedy_max);
+        gfs_printf("rgrp_try_threshold %u\n", gt->gt_rgrp_try_threshold);
+
+        error = 0;
+
+ out:
+	spin_unlock(&gt->gt_spin);
+
+        return error;
+}
+
+#define tune_set(f, v) \
+do { \
+	spin_lock(&gt->gt_spin); \
+	gt->f = (v); \
+	spin_unlock(&gt->gt_spin); \
+} while (0)
+
+/**
+ * gi_set_tune - Set a tuneable parameter
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int
+gi_set_tune(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	struct gfs_tune *gt = &sdp->sd_tune;
+ 	char param[ARG_SIZE], value[ARG_SIZE];
+	unsigned int x;
+
+	if (!capable(CAP_SYS_ADMIN))
+                return -EACCES;
+	if (gi->gi_argc != 3)
+		return -EINVAL;
+
+	if (strncpy_from_user(param, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	param[ARG_SIZE - 1] = 0;
+
+	if (strncpy_from_user(value, gi->gi_argv[2], ARG_SIZE) < 0)
+		return -EFAULT;
+	value[ARG_SIZE - 1] = 0;
+
+	if (strcmp(param, "ilimit1") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit1, x);
+
+	} else if (strcmp(param, "ilimit1_tries") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit1_tries, x);
+
+	} else if (strcmp(param, "ilimit1_min") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit1_min, x);
+
+	} else if (strcmp(param, "ilimit2") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit2, x);
+
+	} else if (strcmp(param, "ilimit2_tries") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit2_tries, x);
+
+	} else if (strcmp(param, "ilimit2_min") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit2_min, x);
+
+	} else if (strcmp(param, "demote_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_demote_secs, x);
+
+	} else if (strcmp(param, "incore_log_blocks") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_incore_log_blocks, x);
+
+	} else if (strcmp(param, "jindex_refresh_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_jindex_refresh_secs, x);
+
+	} else if (strcmp(param, "depend_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_depend_secs, x);
+
+	} else if (strcmp(param, "scand_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_scand_secs, x);
+		wake_up_process(sdp->sd_scand_process);
+
+	} else if (strcmp(param, "recoverd_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_recoverd_secs, x);
+		wake_up_process(sdp->sd_recoverd_process);
+
+	} else if (strcmp(param, "logd_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_logd_secs, x);
+		wake_up_process(sdp->sd_logd_process);
+
+	} else if (strcmp(param, "quotad_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_quotad_secs, x);
+		wake_up_process(sdp->sd_quotad_process);
+
+	} else if (strcmp(param, "inoded_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_inoded_secs, x);
+		wake_up_process(sdp->sd_inoded_process);
+
+	} else if (strcmp(param, "quota_simul_sync") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_quota_simul_sync, x);
+
+	} else if (strcmp(param, "quota_warn_period") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_quota_warn_period, x);
+
+	} else if (strcmp(param, "atime_quantum") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_atime_quantum, x);
+
+	} else if (strcmp(param, "quota_quantum") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_quota_quantum, x);
+
+	} else if (strcmp(param, "quota_scale") == 0) {
+		unsigned int y;
+		if (sscanf(value, "%u %u", &x, &y) != 2 || !y)
+			return -EINVAL;
+		spin_lock(&gt->gt_spin);
+		gt->gt_quota_scale_num = x;
+		gt->gt_quota_scale_den = y;
+		spin_unlock(&gt->gt_spin);
+
+	} else if (strcmp(param, "quota_enforce") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		x = !!x;
+		spin_lock(&gt->gt_spin);
+		gt->gt_quota_enforce = x;
+		if (x)
+			gt->gt_quota_account = 1;
+		spin_unlock(&gt->gt_spin);
+
+	} else if (strcmp(param, "quota_account") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		x = !!x;
+		spin_lock(&gt->gt_spin);
+		gt->gt_quota_account = x;
+		if (x)
+			spin_unlock(&gt->gt_spin);
+		else {
+			unsigned int y;
+			gt->gt_quota_enforce = 0;
+			spin_unlock(&gt->gt_spin);
+			for (y = 0; y < 2; y++) {
+				gfs_log_flush(sdp);
+				gfs_sync_meta(sdp);
+				gfs_quota_sync(sdp);
+			}
+		}
+
+	} else if (strcmp(param, "new_files_jdata") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		x = !!x;
+		tune_set(gt_new_files_jdata, x);
+
+	} else if (strcmp(param, "new_files_directio") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		x = !!x;
+		tune_set(gt_new_files_directio, x);
+
+	} else if (strcmp(param, "max_atomic_write") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_max_atomic_write, x);
+
+	} else if (strcmp(param, "max_readahead") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_max_readahead, x);
+
+	} else if (strcmp(param, "lockdump_size") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_lockdump_size, x);
+
+	} else if (strcmp(param, "stall_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_stall_secs, x);
+
+	} else if (strcmp(param, "complain_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_complain_secs, x);
+
+	} else if (strcmp(param, "reclaim_limit") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_reclaim_limit, x);
+
+	} else if (strcmp(param, "entries_per_readdir") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_entries_per_readdir, x);
+
+	} else if (strcmp(param, "prefetch_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_prefetch_secs, x);
+
+	} else if (strcmp(param, "statfs_slots") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_statfs_slots, x);
+
+	} else if (strcmp(param, "max_mhc") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_max_mhc, x);
+
+	} else if (strcmp(param, "greedy_default") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_greedy_default, x);
+
+	} else if (strcmp(param, "greedy_quantum") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_greedy_quantum, x);
+
+	} else if (strcmp(param, "greedy_max") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_greedy_max, x);
+
+	} else if (strcmp(param, "rgrp_try_threshold") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_rgrp_try_threshold, x);
+
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * gi_do_reclaim - Reclaim unused metadata
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */ 
+
+static int
+gi_do_reclaim(struct gfs_inode *ip,
+	      struct gfs_ioctl *gi,
+	      char *buf,
+	      unsigned int size,
+	      unsigned int *count)
+{
+	uint64_t inodes, metadata;
+        int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	error = gfs_reclaim_metadata(ip->i_sbd,
+				     &inodes,
+				     &metadata);
+	if (error)
+		return error;
+
+	error = -ENOBUFS;
+
+	gfs_printf("version 0\n");
+        gfs_printf("inodes %"PRIu64"\n", inodes);
+        gfs_printf("metadata %"PRIu64"\n", metadata);
+
+        error = 0;
+
+ out:
+        return error;
+}
+
+/**
+ * gi_do_shrink - throw out unused glocks
+ * @sdp:
+ * @gi:
+ *
+ * Returns: 0
+ */
+
+static int
+gi_do_shrink(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	gfs_gl_hash_clear(sdp, FALSE);
+	return 0;
+}
+
+/**
+ * gi_get_file_stat -
+ * @ip:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int
+gi_get_file_stat(struct gfs_inode *ip, struct gfs_ioctl *gi)
+{
+	struct gfs_holder i_gh;
+	struct gfs_dinode *di;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs_dinode))
+		return -EINVAL;
+
+	di = kmalloc(sizeof(struct gfs_dinode), GFP_KERNEL);
+	if (!di)
+		return -ENOMEM;
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		goto out;
+	memcpy(di, &ip->i_di, sizeof(struct gfs_dinode));
+	gfs_glock_dq_uninit(&i_gh);
+
+	if (copy_to_user(gi->gi_data, di,
+			 sizeof(struct gfs_dinode)))
+		error = -EFAULT;
+	else
+		error = sizeof(struct gfs_dinode);
+
+ out:
+	kfree(di);
+
+	return error;
+}
+
+/**
+ * gi_set_file_flag - set or clear a flag on a file
+ * @ip:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int
+gi_set_file_flag(struct gfs_inode *ip, struct gfs_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int set;
+	uint32_t flag;
+	struct gfs_holder i_gh;
+	struct buffer_head *dibh;
+	int error;
+
+	if (gi->gi_argc != 3)
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	if (strcmp(buf, "set") == 0)
+		set = TRUE;
+	else if (strcmp(buf, "clear") == 0)
+		set = FALSE;
+	else
+		return -EINVAL;
+
+        if (strncpy_from_user(buf, gi->gi_argv[2], ARG_SIZE) < 0)
+                return -EFAULT;
+        buf[ARG_SIZE - 1] = 0;
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	error = -EACCES;
+	if (ip->i_di.di_uid != current->fsuid && !capable(CAP_FOWNER))
+		goto out;
+
+	error = -EINVAL;
+
+	if (strcmp(buf, "jdata") == 0) {
+		if (ip->i_di.di_type != GFS_FILE_REG ||
+		    ip->i_di.di_size)
+			goto out;
+		flag = GFS_DIF_JDATA;
+	} else if (strcmp(buf, "directio") == 0) {
+		if (ip->i_di.di_type != GFS_FILE_REG)
+			goto out;
+		flag = GFS_DIF_DIRECTIO;
+	} else if (strcmp(buf, "immutable") == 0) {
+		/* The IMMUTABLE flag can only be changed by
+		   the relevant capability. */
+		error = -EPERM;
+		if (!capable(CAP_LINUX_IMMUTABLE))
+			goto out;
+		flag = GFS_DIF_IMMUTABLE;
+	} else if (strcmp(buf, "appendonly") == 0) {
+                /* The APPENDONLY flag can only be changed by
+                   the relevant capability. */
+                error = -EPERM;
+                if (!capable(CAP_LINUX_IMMUTABLE))
+                        goto out;
+		flag = GFS_DIF_APPENDONLY;
+	} else if (strcmp(buf, "inherit_jdata") == 0) {
+		if (ip->i_di.di_type != GFS_FILE_DIR) 
+			goto out;
+		flag = GFS_DIF_INHERIT_JDATA;
+	} else if (strcmp(buf, "inherit_directio") == 0) {
+		if (ip->i_di.di_type != GFS_FILE_DIR)
+			goto out;
+                flag = GFS_DIF_INHERIT_DIRECTIO;
+	} else
+		goto out;
+
+	error = gfs_trans_begin(ip->i_sbd, 1, 0);
+	if (error)
+		goto out;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		goto out_trans_end;
+
+	if (set)
+		ip->i_di.di_flags |= flag;
+	else
+		ip->i_di.di_flags &= ~flag;
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+
+	brelse(dibh);
+
+ out_trans_end:
+	gfs_trans_end(ip->i_sbd);
+
+ out:
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+
+}
+
+/**
+ * gi_get_file_meta - Return all the metadata for a file
+ * @ip:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int
+gi_get_file_meta(struct gfs_inode *ip, struct gfs_ioctl *gi)
+{
+	struct gfs_holder i_gh;
+	struct gfs_user_buffer ub;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	ub.ub_data = gi->gi_data;
+	ub.ub_size = gi->gi_size;
+	ub.ub_count = 0;
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		return error;
+
+        error = -EACCES;
+        if (ip->i_di.di_uid != current->fsuid && !capable(CAP_FOWNER))
+                goto out;
+
+	error = gfs_get_file_meta(ip, &ub);
+	if (error)
+		goto out;
+
+	if (ip->i_di.di_type == GFS_FILE_DIR &&
+	    (ip->i_di.di_flags & GFS_DIF_EXHASH)) {
+		error = gfs_get_dir_meta(ip, &ub);
+		if (error)
+			goto out;
+	}
+
+	if (ip->i_di.di_eattr) {
+		error = gfs_get_eattr_meta(ip, &ub);
+		if (error)
+			goto out;
+	}
+
+	error = ub.ub_count;
+
+ out:
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_file_flush - sync out all dirty data and
+ *                    drop the cache (and lock) for a file.
+ * @ip:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int
+gi_do_file_flush(struct gfs_inode *ip, struct gfs_ioctl *gi)
+{
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	gfs_glock_force_drop(ip->i_gl);
+	return 0;
+}
+
+/**
+ * gi2hip - return the "struct gfs_inode" for a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the "struct gfs_inode"
+ */
+
+static struct gfs_inode *
+gi2hip(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+
+	if (gi->gi_argc != 2)
+		return ERR_PTR(-EINVAL);
+
+        if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+                return ERR_PTR(-EFAULT);
+        buf[ARG_SIZE - 1] = 0;
+
+	if (strcmp(buf, "jindex") == 0)
+		return sdp->sd_jiinode;
+	else if (strcmp(buf, "rindex") == 0)
+		return sdp->sd_riinode;
+	else if (strcmp(buf, "quota") == 0)
+		return sdp->sd_qinode;
+	else if (strcmp(buf, "license") == 0)
+		return sdp->sd_linode;
+	else
+		return ERR_PTR(-EINVAL);
+}
+
+/**
+ * gi_get_hfile_stat - get stat info on a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int
+gi_get_hfile_stat(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	struct gfs_inode *ip;
+	struct gfs_dinode *di;
+	struct gfs_holder i_gh;
+	int error;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (gi->gi_size != sizeof(struct gfs_dinode))
+		return -EINVAL;
+
+        di = kmalloc(sizeof(struct gfs_dinode), GFP_KERNEL);
+        if (!di)
+                return -ENOMEM;
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		goto out;
+	memcpy(di, &ip->i_di, sizeof(struct gfs_dinode));
+	gfs_glock_dq_uninit(&i_gh);
+
+        if (copy_to_user(gi->gi_data, di,
+                         sizeof(struct gfs_dinode)))
+                error = -EFAULT;
+        else
+                error = sizeof(struct gfs_dinode);
+
+ out:
+	kfree(di);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_read - Read data from a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes read, or -errno
+ */
+
+static int
+gi_do_hfile_read(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	struct gfs_inode *ip;
+	struct gfs_holder i_gh;
+	int error;
+
+        if (!capable(CAP_SYS_ADMIN))
+                return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (!access_ok(VERIFY_WRITE, gi->gi_data, gi->gi_size))
+		return -EFAULT;
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
+	if (error)
+		return error;
+
+	error = gfs_readi(ip, gi->gi_data, gi->gi_offset, gi->gi_size,
+			  gfs_copy2user);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_write - Write data to a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes written, or -errno
+ */
+
+static int
+gi_do_hfile_write(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	struct gfs_inode *ip;
+	struct gfs_alloc *al = NULL;
+	struct gfs_holder i_gh;
+	unsigned int data_blocks, ind_blocks;
+	int alloc_required;
+	int error;
+
+        if (!capable(CAP_SYS_ADMIN))
+                return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (!access_ok(VERIFY_READ, gi->gi_data, gi->gi_size))
+		return -EFAULT;
+
+	gfs_write_calc_reserv(ip, gi->gi_size, &data_blocks, &ind_blocks);
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE,
+				  LM_FLAG_PRIORITY | GL_SYNC, &i_gh);
+	if (error)
+		return error;
+
+        if (!gfs_is_jdata(ip)) {
+                gfs_consist_inode(ip);
+		error = -EIO;
+		goto out;
+        }
+
+	error = gfs_write_alloc_required(ip, gi->gi_offset, gi->gi_size,
+					 &alloc_required);
+	if (error)
+		goto out;
+
+	if (alloc_required) {
+		al = gfs_alloc_get(ip);
+
+		error = gfs_quota_hold_m(ip, NO_QUOTA_CHANGE,
+					 NO_QUOTA_CHANGE);
+		if (error)
+			goto out_alloc;
+
+		al->al_requested_meta = ind_blocks + data_blocks;
+
+		error = gfs_inplace_reserve(ip);
+		if (error)
+			goto out_qs;
+
+		/* Trans may require:
+		   All blocks for a RG bitmap, all the "data" blocks, whatever
+		   indirect blocks we need, a modified dinode, and a quota change */
+
+		error = gfs_trans_begin(sdp,
+					1 + al->al_rgd->rd_ri.ri_length +
+					ind_blocks + data_blocks, 1);
+		if (error)
+			goto out_relse;
+	} else {
+		/* Trans may require:
+		   All the "data" blocks and a modified dinode. */
+
+		error = gfs_trans_begin(sdp, 1 + data_blocks, 0);
+		if (error)
+			goto out_relse;
+	}
+
+	error = gfs_writei(ip, gi->gi_data, gi->gi_offset, gi->gi_size,
+			   gfs_copy_from_user, NULL);
+
+	gfs_trans_end(sdp);
+
+ out_relse:
+	if (alloc_required) {
+		gfs_assert_warn(sdp, error || al->al_alloced_meta);
+		gfs_inplace_release(ip);
+	}
+
+ out_qs:
+	if (alloc_required)
+		gfs_quota_unhold_m(ip);
+
+ out_alloc:
+	if (alloc_required)
+		gfs_alloc_put(ip);
+
+ out:
+	ip->i_gl->gl_vn++;
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_trunc - truncate a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int
+gi_do_hfile_trunc(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	struct gfs_inode *ip;
+	struct gfs_holder i_gh;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_SYNC, &i_gh);
+	if (error)
+		return error;
+
+	error = gfs_truncatei(ip, gi->gi_offset, NULL);
+
+	ip->i_gl->gl_vn++;
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_quota_sync - sync the outstanding quota changes for a FS
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int
+gi_do_quota_sync(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	return gfs_quota_sync(sdp);
+}
+
+/**
+ * gi_do_quota_refresh - Refresh the a quota LVB from the quota file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int
+gi_do_quota_refresh(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int user;
+	uint32_t id;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 2)
+		return -EINVAL;
+
+        if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+                return -EFAULT;
+        buf[ARG_SIZE - 1] = 0;
+
+	switch (buf[0]) {
+	case 'u':
+		user = TRUE;
+		break;
+	case 'g':
+		user = FALSE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (buf[1] != ':')
+		return -EINVAL;
+
+	if (sscanf(buf + 2, "%u", &id) != 1)
+		return -EINVAL;
+
+	return gfs_quota_refresh(sdp, user, id);
+}
+
+/**
+ * gi_do_quota_read - read quota values from the quota file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int
+gi_do_quota_read(struct gfs_sbd *sdp, struct gfs_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int user;
+	uint32_t id;
+	struct gfs_quota q;
+	int error;
+
+	if (gi->gi_argc != 2)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs_quota))
+		return -EINVAL;
+
+        if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+                return -EFAULT;
+        buf[ARG_SIZE - 1] = 0;
+
+	switch (buf[0]) {
+	case 'u':
+		user = TRUE;
+		break;
+	case 'g':
+		user = FALSE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (buf[1] != ':')
+		return -EINVAL;
+
+	if (sscanf(buf + 2, "%u", &id) != 1)
+		return -EINVAL;
+
+	error = gfs_quota_read(sdp, user, id, &q);
+	if (error)
+		return error;
+
+	if (copy_to_user(gi->gi_data, &q, sizeof(struct gfs_quota)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * gfs_ioctl_i -
+ * @ip:
+ * @arg:
+ *
+ * Returns: -errno or positive byte count
+ */
+
+int
+gfs_ioctl_i(struct gfs_inode *ip, void *arg)
+{
+	struct gfs_ioctl *gi_user = (struct gfs_ioctl *)arg;
+	struct gfs_ioctl gi;
+	char **argv;
+	char arg0[ARG_SIZE];
+	int error = -EFAULT;
+
+	if (copy_from_user(&gi, gi_user, sizeof(struct gfs_ioctl)))
+		return -EFAULT;
+	if (!gi.gi_argc)
+		return -EINVAL;
+	argv = kmalloc(gi.gi_argc * sizeof(char *), GFP_KERNEL);
+	if (!argv)
+		return -ENOMEM;
+	if (copy_from_user(argv, gi.gi_argv,
+			   gi.gi_argc * sizeof(char *)))
+		goto out;
+	gi.gi_argv = argv;
+
+	if (strncpy_from_user(arg0, argv[0], ARG_SIZE) < 0)
+		goto out;
+	arg0[ARG_SIZE - 1] = 0;
+
+	if (strcmp(arg0, "get_cookie") == 0)
+                error = gi_skeleton(ip, &gi, gi_get_cookie);
+	else if (strcmp(arg0, "get_super") == 0)
+		error = gi_get_super(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "get_args") == 0)
+		error = gi_skeleton(ip, &gi, gi_get_args);
+	else if (strcmp(arg0, "get_lockstruct") == 0)
+		error = gi_skeleton(ip, &gi, gi_get_lockstruct);
+        else if (strcmp(arg0, "get_stat_gfs") == 0)
+                error = gi_skeleton(ip, &gi, gi_get_stat_gfs);
+        else if (strcmp(arg0, "get_counters") == 0)
+                error = gi_skeleton(ip, &gi, gi_get_counters);
+        else if (strcmp(arg0, "get_tune") == 0)
+                error = gi_skeleton(ip, &gi, gi_get_tune);
+	else if (strcmp(arg0, "set_tune") == 0)
+		error = gi_set_tune(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_reclaim") == 0)
+		error = gi_skeleton(ip, &gi, gi_do_reclaim);
+	else if (strcmp(arg0, "do_shrink") == 0)
+		error = gi_do_shrink(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "get_file_stat") == 0)
+		error = gi_get_file_stat(ip, &gi);
+	else if (strcmp(arg0, "set_file_flag") == 0)
+		error = gi_set_file_flag(ip, &gi);
+	else if (strcmp(arg0, "get_file_meta") == 0)
+		error = gi_get_file_meta(ip, &gi);
+	else if (strcmp(arg0, "do_file_flush") == 0)
+		error = gi_do_file_flush(ip, &gi);
+	else if (strcmp(arg0, "get_hfile_stat") == 0)
+		error = gi_get_hfile_stat(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_read") == 0)
+		error = gi_do_hfile_read(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_write") == 0)
+		error = gi_do_hfile_write(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_trunc") == 0)
+		error = gi_do_hfile_trunc(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_sync") == 0)
+		error = gi_do_quota_sync(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_refresh") == 0)
+		error = gi_do_quota_refresh(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_read") == 0)
+		error = gi_do_quota_read(ip->i_sbd, &gi);
+	else
+		error = -ENOTTY;
+
+ out:
+	kfree(argv);
+
+	return error;
+}
+
+
diff -pruN linux-2.6.9.orig/fs/gfs/ioctl.h linux-2.6.9.debug/fs/gfs/ioctl.h
--- linux-2.6.9.orig/fs/gfs/ioctl.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ioctl.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,19 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __IOCTL_DOT_H__
+#define __IOCTL_DOT_H__
+
+int gfs_ioctl_i(struct gfs_inode *ip, void *arg);
+
+#endif /* __IOCTL_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/lm.c linux-2.6.9.debug/fs/gfs/lm.c
--- linux-2.6.9.orig/fs/gfs/lm.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/lm.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,500 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "glock.h"
+#include "lm.h"
+#include "super.h"
+
+/**
+ * lm_cb -
+ * @fsdata:
+ * @type:
+ * @data:
+ *
+ */
+
+static void
+lm_cb(lm_fsdata_t *fsdata, unsigned int type, void *data)
+{
+	if (type == LM_CB_ASYNC)
+		atomic_dec(&((struct gfs_sbd *)fsdata)->sd_lm_outstanding);
+	gfs_glock_cb(fsdata, type, data);
+}
+
+/**
+ * gfs_lm_mount - mount a locking protocol
+ * @sdp: the filesystem
+ * @args: mount arguements
+ * @silent: if TRUE, don't complain if the FS isn't a GFS fs
+ *
+ * Returns: errno
+ */
+
+int
+gfs_lm_mount(struct gfs_sbd *sdp, int silent)
+{
+	struct gfs_sb *sb = NULL;
+	char *proto, *table;
+	int error;
+
+	proto = sdp->sd_args.ar_lockproto;
+	table = sdp->sd_args.ar_locktable;
+
+	/*  Try to autodetect  */
+
+	if (!proto[0] || !table[0]) {
+		struct buffer_head *bh = sb_getblk(sdp->sd_vfs,
+						   GFS_SB_ADDR >> sdp->sd_fsb2bb_shift);
+		lock_buffer(bh);
+		clear_buffer_dirty(bh);
+		clear_buffer_uptodate(bh);
+		unlock_buffer(bh);
+		ll_rw_block(READ, 1, &bh);
+		wait_on_buffer(bh);
+
+		if (!buffer_uptodate(bh)) {
+			brelse(bh);
+			return -EIO;
+		}
+
+		sb = kmalloc(sizeof(struct gfs_sb), GFP_KERNEL);
+		if (!sb) {
+			brelse(bh);
+			return -ENOMEM;
+		}
+		gfs_sb_in(sb, bh->b_data);
+		brelse(bh);
+
+		error = gfs_check_sb(sdp, sb, silent);
+		if (error)
+			goto out;
+
+		if (!proto[0])
+			proto = sb->sb_lockproto;
+
+		if (!table[0])
+			table = sb->sb_locktable;
+	}
+
+	printk("GFS: Trying to join cluster \"%s\", \"%s\"\n",
+	       proto, table);
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	error = lm_mount(proto, table, sdp->sd_args.ar_hostdata,
+			 lm_cb, sdp,
+			 GFS_MIN_LVB_SIZE, &sdp->sd_lockstruct);
+	atomic_dec(&sdp->sd_lm_outstanding);
+	if (error) {
+		printk("GFS: can't mount proto = %s, table = %s, hostdata = %s\n",
+		     proto, table, sdp->sd_args.ar_hostdata);
+		goto out;
+	}
+
+	if (gfs_assert_warn(sdp, sdp->sd_lockstruct.ls_lockspace) ||
+	    gfs_assert_warn(sdp, sdp->sd_lockstruct.ls_ops) ||
+	    gfs_assert_warn(sdp, sdp->sd_lockstruct.ls_lvb_size >= GFS_MIN_LVB_SIZE)) {
+		lm_unmount(&sdp->sd_lockstruct);
+		goto out;
+	}
+
+	snprintf(sdp->sd_fsname, 256, "%s.%u",
+		 (*table) ? table : sdp->sd_vfs->s_id,
+		 sdp->sd_lockstruct.ls_jid);
+
+	printk("GFS: fsid=%s: Joined cluster. Now mounting FS...\n",
+	       sdp->sd_fsname);
+
+ out:
+	if (sb)
+		kfree(sb);
+
+	return error;
+}
+
+/**
+ * gfs_lm_others_may_mount -
+ * @sdp:
+ *
+ */
+
+void
+gfs_lm_others_may_mount(struct gfs_sbd *sdp)
+{
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		sdp->sd_lockstruct.ls_ops->lm_others_may_mount(sdp->sd_lockstruct.ls_lockspace);
+	atomic_dec(&sdp->sd_lm_outstanding);
+}
+
+/**
+ * gfs_lm_unmount - Unmount lock protocol
+ * @sdp: The GFS superblock
+ *
+ */
+
+void
+gfs_lm_unmount(struct gfs_sbd *sdp)
+{
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		lm_unmount(&sdp->sd_lockstruct);
+	atomic_dec(&sdp->sd_lm_outstanding);
+}
+
+/**
+ * gfs_lm_withdraw -
+ * @sdp:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int
+gfs_lm_withdraw(struct gfs_sbd *sdp, char *fmt, ...)
+{
+	va_list args;
+
+	if (test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags))
+		return 0;
+
+	va_start(args, fmt);
+	vprintk(fmt, args);
+	va_end(args);
+
+	printk("GFS: fsid=%s: about to withdraw from the cluster\n",
+	       sdp->sd_fsname);
+	printk("GFS: fsid=%s: waiting for outstanding I/O\n",
+	       sdp->sd_fsname);
+
+	if (sdp->sd_args.ar_debug)
+		BUG();
+
+	while (atomic_read(&sdp->sd_bio_outstanding)) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ / 10);
+	}
+/*
+ *  I'm still not sure if we want to do this.  If we do, we need to
+ *  add code to cancel outstanding requests.
+ *
+	while (atomic_read(&sdp->sd_lm_outstanding)) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ / 10);
+	}
+*/
+
+	printk("GFS: fsid=%s: telling LM to withdraw\n",
+	       sdp->sd_fsname);
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	lm_withdraw(&sdp->sd_lockstruct);
+	atomic_dec(&sdp->sd_lm_outstanding);
+
+	printk("GFS: fsid=%s: withdrawn\n",
+	       sdp->sd_fsname);
+
+	return -1;
+}
+
+/**
+ * gfs_lm_get_lock -
+ * @sdp:
+ * @name:
+ * @lockp:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_lm_get_lock(struct gfs_sbd *sdp,
+		struct lm_lockname *name, lm_lock_t **lockp)
+{
+	int error;
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		error = -EIO;
+	else
+		error = sdp->sd_lockstruct.ls_ops->lm_get_lock(sdp->sd_lockstruct.ls_lockspace,
+							       name, lockp);
+	atomic_dec(&sdp->sd_lm_outstanding);
+
+	return error;
+}
+
+/**
+ * gfs_lm_put_lock -
+ * @sdp:
+ * @lock:
+ *
+ */
+
+void
+gfs_lm_put_lock(struct gfs_sbd *sdp, lm_lock_t *lock)
+{
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		sdp->sd_lockstruct.ls_ops->lm_put_lock(lock);
+	atomic_dec(&sdp->sd_lm_outstanding);
+}
+
+/**
+ * gfs_lm_lock -
+ * @sdp:
+ * @lock:
+ * @cur_state:
+ * @req_state:
+ * @flags:
+ *
+ * Returns:
+ */
+
+unsigned int
+gfs_lm_lock(struct gfs_sbd *sdp, lm_lock_t *lock,
+	    unsigned int cur_state, unsigned int req_state,
+	    unsigned int flags)
+{
+	int ret;
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		ret = 0;
+	else
+		ret = sdp->sd_lockstruct.ls_ops->lm_lock(lock,
+							 cur_state,
+							 req_state, flags);
+	if (ret != LM_OUT_ASYNC)
+		atomic_dec(&sdp->sd_lm_outstanding);
+
+	return ret;
+}
+
+/**
+ * gfs_lm_lock -
+ * @sdp:
+ * @lock:
+ * @cur_state:
+ *
+ * Returns:
+ */
+
+unsigned int
+gfs_lm_unlock(struct gfs_sbd *sdp, lm_lock_t *lock,
+	      unsigned int cur_state)
+{
+	int ret;
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		ret = 0;
+	else
+		ret =  sdp->sd_lockstruct.ls_ops->lm_unlock(lock, cur_state);
+	if (ret != LM_OUT_ASYNC)
+		atomic_dec(&sdp->sd_lm_outstanding);
+
+	return ret;
+}
+
+/**
+ * gfs_lm_cancel -
+ * @sdp:
+ * @lock:
+ *
+ */
+
+void
+gfs_lm_cancel(struct gfs_sbd *sdp, lm_lock_t *lock)
+{
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		sdp->sd_lockstruct.ls_ops->lm_cancel(lock);
+	atomic_dec(&sdp->sd_lm_outstanding);
+}
+
+/**
+ * gfs_lm_hold_lvb -
+ * @sdp:
+ * @lock:
+ * @lvbp:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_lm_hold_lvb(struct gfs_sbd *sdp, lm_lock_t *lock, char **lvbp)
+{
+	int error;
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		error = -EIO;
+	else
+		error = sdp->sd_lockstruct.ls_ops->lm_hold_lvb(lock, lvbp);
+	atomic_dec(&sdp->sd_lm_outstanding);
+
+	return error;
+}
+
+/**
+ * gfs_lm_unhold_lvb -
+ * @sdp:
+ * @lock:
+ * @lvb:
+ *
+ */
+
+void
+gfs_lm_unhold_lvb(struct gfs_sbd *sdp, lm_lock_t *lock, char *lvb)
+{
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		sdp->sd_lockstruct.ls_ops->lm_unhold_lvb(lock, lvb);
+	atomic_dec(&sdp->sd_lm_outstanding);
+}
+
+/**
+ * gfs_lm_sync_lvb -
+ * @sdp:
+ * @lock:
+ * @lvb:
+ *
+ */
+
+void
+gfs_lm_sync_lvb(struct gfs_sbd *sdp, lm_lock_t *lock, char *lvb)
+{
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		sdp->sd_lockstruct.ls_ops->lm_sync_lvb(lock, lvb);
+	atomic_dec(&sdp->sd_lm_outstanding);
+}
+
+/**
+ * gfs_lm_plock_get -
+ * @sdp:
+ * @name:
+ * @file:
+ * @fl:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_lm_plock_get(struct gfs_sbd *sdp,
+		 struct lm_lockname *name,
+		 struct file *file, struct file_lock *fl)
+{
+	int error;
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		error = -EIO;
+	else
+		error = sdp->sd_lockstruct.ls_ops->lm_plock_get(
+			sdp->sd_lockstruct.ls_lockspace,
+			name, file, fl);
+	atomic_dec(&sdp->sd_lm_outstanding);
+
+	return error;
+}
+
+/**
+ * gfs_lm_plock -
+ * @sdp:
+ * @name:
+ * @file:
+ * @cmd:
+ * @fl:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_lm_plock(struct gfs_sbd *sdp,
+	     struct lm_lockname *name,
+	     struct file *file, int cmd, struct file_lock *fl)
+{
+	int error;
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		error = -EIO;
+	else
+		error = sdp->sd_lockstruct.ls_ops->lm_plock(
+			sdp->sd_lockstruct.ls_lockspace,
+			name, file, cmd, fl);
+	atomic_dec(&sdp->sd_lm_outstanding);
+
+	return error;
+}
+
+/**
+ * gfs_lm_punlock -
+ * @sdp:
+ * @name:
+ * @file:
+ * @fl:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_lm_punlock(struct gfs_sbd *sdp,
+	       struct lm_lockname *name,
+	       struct file *file, struct file_lock *fl)
+{
+	int error;
+
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		error = -EIO;
+	else
+		error = sdp->sd_lockstruct.ls_ops->lm_punlock(
+			sdp->sd_lockstruct.ls_lockspace,
+			name, file, fl);
+	atomic_dec(&sdp->sd_lm_outstanding);
+
+	return error;
+}
+
+/**
+ * gfs_lm_recovery_done -
+ * @sdp:
+ * @jid:
+ * @message:
+ *
+ */
+
+void
+gfs_lm_recovery_done(struct gfs_sbd *sdp,
+		     unsigned int jid, unsigned int message)
+{
+	atomic_inc(&sdp->sd_lm_outstanding);
+	if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		sdp->sd_lockstruct.ls_ops->lm_recovery_done(sdp->sd_lockstruct.ls_lockspace,
+							    jid,
+							    message);
+	atomic_dec(&sdp->sd_lm_outstanding);
+}
+
+
diff -pruN linux-2.6.9.orig/fs/gfs/lm.h linux-2.6.9.debug/fs/gfs/lm.h
--- linux-2.6.9.orig/fs/gfs/lm.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/lm.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,46 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __LM_DOT_H__
+#define __LM_DOT_H__
+
+int gfs_lm_mount(struct gfs_sbd *sdp, int silent);
+void gfs_lm_others_may_mount(struct gfs_sbd *sdp);
+void gfs_lm_unmount(struct gfs_sbd *sdp);
+int gfs_lm_withdraw(struct gfs_sbd *sdp, char *fmt, ...)
+__attribute__ ((format(printf, 2, 3)));
+int gfs_lm_get_lock(struct gfs_sbd *sdp,
+		    struct lm_lockname *name, lm_lock_t **lockp);
+void gfs_lm_put_lock(struct gfs_sbd *sdp, lm_lock_t *lock);
+unsigned int gfs_lm_lock(struct gfs_sbd *sdp, lm_lock_t *lock,
+			 unsigned int cur_state, unsigned int req_state,
+			 unsigned int flags);
+unsigned int gfs_lm_unlock(struct gfs_sbd *sdp, lm_lock_t *lock,
+			   unsigned int cur_state);
+void gfs_lm_cancel(struct gfs_sbd *sdp, lm_lock_t *lock);
+int gfs_lm_hold_lvb(struct gfs_sbd *sdp, lm_lock_t *lock, char **lvbp);
+void gfs_lm_unhold_lvb(struct gfs_sbd *sdp, lm_lock_t *lock, char *lvb);
+void gfs_lm_sync_lvb(struct gfs_sbd *sdp, lm_lock_t *lock, char *lvb);
+int gfs_lm_plock_get(struct gfs_sbd *sdp,
+		     struct lm_lockname *name,
+		     struct file *file, struct file_lock *fl);
+int gfs_lm_plock(struct gfs_sbd *sdp,
+		 struct lm_lockname *name,
+		 struct file *file, int cmd, struct file_lock *fl);
+int gfs_lm_punlock(struct gfs_sbd *sdp,
+		   struct lm_lockname *name,
+		   struct file *file, struct file_lock *fl);
+void gfs_lm_recovery_done(struct gfs_sbd *sdp,
+			  unsigned int jid, unsigned int message);
+
+#endif /* __LM_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/log.c linux-2.6.9.debug/fs/gfs/log.c
--- linux-2.6.9.orig/fs/gfs/log.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/log.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1442 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+          What rolls down stairs
+             Alone or in pairs
+      Rolls over your neighbor's dog.
+         What's great for a snack
+           And fits on your back
+             It's log, log, log!
+             It's lo-og, lo-og,
+       It's big, it's heavy, it's wood.
+             It's lo-og, lo-og,
+       It's better than bad, it's good.
+           Everyone wants a log,
+         You're gonna love it, log
+         Come on and get your log,
+           Everyone needs a log...
+            LOG... FROM BLAMMO!
+
+                     -- The Ren and Stimpy Show
+*/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "log.h"
+#include "lops.h"
+
+/**
+ * gfs_struct2blk - compute stuff
+ * @sdp: the filesystem
+ * @nstruct: the number of structures
+ * @ssize: the size of the structures
+ *
+ * Compute the number of log descriptor blocks needed to hold a certain number
+ * of structures of a certain size.
+ *
+ * Returns: the number of blocks needed (minimum is always 1)
+ */
+
+unsigned int
+gfs_struct2blk(struct gfs_sbd *sdp, unsigned int nstruct, unsigned int ssize)
+{
+	unsigned int blks;
+	unsigned int first, second;
+
+	blks = 1;
+	first = (sdp->sd_sb.sb_bsize - sizeof(struct gfs_log_descriptor)) / ssize;
+
+	if (nstruct > first) {
+		second = sdp->sd_sb.sb_bsize / ssize;
+		blks += DIV_RU(nstruct - first, second);
+	}
+
+	return blks;
+}
+
+/**
+ * gfs_blk2seg - Convert number of blocks into number of segments
+ * @sdp: The GFS superblock
+ * @blocks: The number of blocks
+ *
+ * Returns: The number of journal segments
+ */
+
+unsigned int
+gfs_blk2seg(struct gfs_sbd *sdp, unsigned int blocks)
+{
+	return DIV_RU(blocks, sdp->sd_sb.sb_seg_size - 1);
+}
+
+/**
+ * log_distance - Compute distance between two journal blocks
+ * @sdp: The GFS superblock
+ * @newer: The most recent journal block of the pair
+ * @older: The older journal block of the pair
+ *
+ *   Compute the distance (in the journal direction) between two
+ *   blocks in the journal
+ *
+ * Returns: the distance in blocks
+ */
+
+static __inline__ unsigned int
+log_distance(struct gfs_sbd *sdp, uint64_t newer, uint64_t older)
+{
+	int64_t dist;
+
+	dist = newer - older;
+	if (dist < 0)
+		dist += sdp->sd_jdesc.ji_nsegment * sdp->sd_sb.sb_seg_size;
+
+	return dist;
+}
+
+/**
+ * log_incr_head - Increment journal head (next block to fill in journal)
+ * @sdp: The GFS superblock
+ * @head: the variable holding the head of the journal
+ *
+ * Increment journal head by one. 
+ * At the end of the journal, wrap head back to the start.
+ * Don't confuse journal/log head with a gfs_log_header!
+ */
+
+static __inline__ void
+log_incr_head(struct gfs_sbd *sdp, uint64_t * head)
+{
+	struct gfs_jindex *jdesc = &sdp->sd_jdesc;
+
+	if (++*head ==
+	    jdesc->ji_addr + jdesc->ji_nsegment * sdp->sd_sb.sb_seg_size)
+		*head = jdesc->ji_addr;
+}
+
+/**
+ * gfs_ail_start - Start I/O on the AIL
+ * @sdp: the filesystem
+ * @flags:  DIO_ALL -- flush *all* AIL transactions to disk
+ *          default -- flush first-on-list AIL transaction to disk
+ *
+ */
+
+void
+gfs_ail_start(struct gfs_sbd *sdp, int flags)
+{
+	struct list_head *head = &sdp->sd_log_ail;
+	struct list_head *first, *tmp;
+	struct gfs_trans *first_tr, *tr;
+
+	gfs_log_lock(sdp);
+
+	if (list_empty(head)) {
+		gfs_log_unlock(sdp);
+		return;
+	}
+
+	first = head->prev;
+	first_tr = list_entry(first, struct gfs_trans, tr_list);
+	gfs_ail_start_trans(sdp, first_tr);
+
+	if (flags & DIO_ALL)
+		first_tr = NULL;
+
+	for (tmp = first->prev; tmp != head; tmp = tmp->prev) {
+		if (first_tr && gfs_ail_empty_trans(sdp, first_tr))
+			break;
+
+		tr = list_entry(tmp, struct gfs_trans, tr_list);
+		gfs_ail_start_trans(sdp, tr);
+	}
+
+	gfs_log_unlock(sdp);
+}
+
+/**
+ * current_tail - Find block number of current log tail
+ * @sdp: The GFS superblock
+ *
+ * Find the block number of the current tail of the log.
+ * Assumes that the log lock is held.
+ *
+ * Returns: The tail's block number (must be on a log segment boundary)
+ */
+
+static uint64_t
+current_tail(struct gfs_sbd *sdp)
+{
+	struct gfs_trans *tr;
+	uint64_t tail;
+
+	if (list_empty(&sdp->sd_log_ail)) {
+		tail = sdp->sd_log_head;
+
+		if (!gfs_log_is_header(sdp, tail)) {
+			tail--;
+			gfs_assert(sdp, gfs_log_is_header(sdp, tail), );
+		}
+	} else {
+		tr = list_entry(sdp->sd_log_ail.prev,
+				struct gfs_trans, tr_list);
+		tail = tr->tr_first_head;
+	}
+
+	return tail;
+}
+
+/**
+ * gfs_ail_empty - move the tail of the log forward (if possible)
+ * @sdp: the filesystem
+ *
+ * Returns: TRUE if the AIL is empty
+ *
+ * Checks each transaction on sd_log_ail, to see if it has been successfully
+ *   flushed to in-place blocks on disk.  If so, removes trans from sd_log_ail,
+ *   effectively advancing the tail of the log (freeing log segments so they
+ *   can be overwritten).
+ * Adds # freed log segments to sd_log_seg_free.
+ */
+
+int
+gfs_ail_empty(struct gfs_sbd *sdp)
+{
+	struct list_head *head, *tmp, *prev;
+	struct gfs_trans *tr;
+	uint64_t oldtail, newtail;
+	unsigned int dist;
+	unsigned int segments;
+	int ret;
+
+	gfs_log_lock(sdp);
+
+	oldtail = current_tail(sdp);
+
+	for (head = &sdp->sd_log_ail, tmp = head->prev, prev = tmp->prev;
+	     tmp != head;
+	     tmp = prev, prev = tmp->prev) {
+		tr = list_entry(tmp, struct gfs_trans, tr_list);
+		if (gfs_ail_empty_trans(sdp, tr)) {
+			list_del(&tr->tr_list);
+			kfree(tr);
+		}
+	}
+
+	newtail = current_tail(sdp);
+
+	if (oldtail != newtail) {
+		dist = log_distance(sdp, newtail, oldtail);
+
+		segments = dist / sdp->sd_sb.sb_seg_size;
+		gfs_assert(sdp, segments * sdp->sd_sb.sb_seg_size == dist,);
+
+		sdp->sd_log_seg_ail2 += segments;
+		gfs_assert(sdp, sdp->sd_log_seg_free + sdp->sd_log_seg_ail2 <=
+			   sdp->sd_jdesc.ji_nsegment,); 
+	}
+
+	ret = list_empty(head);
+
+	gfs_log_unlock(sdp);
+
+	return ret;
+}
+
+/**
+ * gfs_log_reserve - Make a log reservation
+ * @sdp: The GFS superblock
+ * @segments: The number of segments to reserve
+ * @jump_queue: if TRUE, don't care about fairness ordering
+ *
+ * Returns: errno
+ */
+
+int
+gfs_log_reserve(struct gfs_sbd *sdp, unsigned int segments, int jump_queue)
+{
+	struct list_head list;
+	unsigned int try = 0;
+
+	if (gfs_assert_warn(sdp, segments))
+		return -EINVAL;
+	if (gfs_assert_warn(sdp, segments < sdp->sd_jdesc.ji_nsegment))
+		return -EINVAL;
+
+	INIT_LIST_HEAD(&list);
+
+	for (;;) {
+		spin_lock(&sdp->sd_log_seg_lock);
+
+		if (list_empty(&list)) {
+			if (jump_queue)
+				list_add(&list, &sdp->sd_log_seg_list);
+			else {
+				list_add_tail(&list, &sdp->sd_log_seg_list);
+				while (sdp->sd_log_seg_list.next != &list) {
+					DECLARE_WAITQUEUE(__wait_chan, current);
+					set_current_state(TASK_UNINTERRUPTIBLE);
+					add_wait_queue(&sdp->sd_log_seg_wait,
+						       &__wait_chan);
+					spin_unlock(&sdp->sd_log_seg_lock);
+					schedule();
+					spin_lock(&sdp->sd_log_seg_lock);
+					remove_wait_queue(&sdp->sd_log_seg_wait,
+							  &__wait_chan);
+					set_current_state(TASK_RUNNING);
+				}
+			}
+		}
+
+		if (sdp->sd_log_seg_free > segments) {
+			sdp->sd_log_seg_free -= segments;
+			list_del(&list);
+			spin_unlock(&sdp->sd_log_seg_lock);
+			wake_up(&sdp->sd_log_seg_wait);
+			break;
+		}
+
+		spin_unlock(&sdp->sd_log_seg_lock);
+
+		if (try) {
+			gfs_log_flush(sdp);
+			gfs_ail_start(sdp, 0);
+		}
+
+		gfs_ail_empty(sdp);
+
+		try++;
+		yield();
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_log_release - Release a given number of log segments
+ * @sdp: The GFS superblock
+ * @segments: The number of segments
+ *
+ */
+
+void
+gfs_log_release(struct gfs_sbd *sdp, unsigned int segments)
+{
+	spin_lock(&sdp->sd_log_seg_lock);
+	sdp->sd_log_seg_free += segments;
+	gfs_assert(sdp, sdp->sd_log_seg_free + sdp->sd_log_seg_ail2 <=
+		   sdp->sd_jdesc.ji_nsegment,);
+	spin_unlock(&sdp->sd_log_seg_lock);
+}
+
+/**
+ * log_get_header - Get and initialize a journal header buffer
+ * @sdp: The GFS superblock
+ * @tr: The transaction that needs a log header
+ * @next: FALSE if this log header appears in midst of current transaction
+ *        TRUE if this starts next transaction (and commits current trans)
+ *
+ * Returns: the initialized log buffer descriptor
+ *
+ * Initialize one of the transaction's pre-allocated buffers (and associated
+ *   log buffer descriptor) to be a log header for this transaction.
+ * A log header gets written to *each* log segment boundary block, so journal
+ *   recovery will quickly be able to get its bearings.  A single transaction
+ *   may span several log segments, which means that log headers will appear
+ *   in the midst of that transaction (@next == FALSE).  These headers get
+ *   added to trans' list of buffers to write to log.
+ * Log commit is accomplished by writing the log header for the next
+ *   transaction (@next == TRUE), with pre-incremented sequence number,
+ *   and updated first-in-transaction block number.  These headers do *not* get
+ *   added to trans' buffer list, since they are written separately to disk
+ *   *after* the trans gets completely flushed to on-disk log.
+ * NOTE:  This buffer will *not* get written to an in-place location in the
+ *        filesystem; it is for use only within the log.
+ */
+
+static struct gfs_log_buf *
+log_get_header(struct gfs_sbd *sdp, struct gfs_trans *tr, int next)
+{
+	struct gfs_log_buf *lb;
+	struct list_head *bmem;
+	struct gfs_log_header header;
+
+	/* Make sure we're on a log segment boundary block */
+	gfs_assert(sdp, gfs_log_is_header(sdp, tr->tr_log_head),);
+
+	/* Grab a free log buffer descriptor (attached to trans) */
+	gfs_assert(sdp, tr->tr_num_free_bufs &&
+		   !list_empty(&tr->tr_free_bufs),);
+	lb = list_entry(tr->tr_free_bufs.next, struct gfs_log_buf, lb_list);
+	list_del(&lb->lb_list);
+	tr->tr_num_free_bufs--;
+
+	/* Grab a free log buffer (attached to trans) */
+	gfs_assert(sdp, tr->tr_num_free_bmem &&
+		   !list_empty(&tr->tr_free_bmem),);
+	bmem = tr->tr_free_bmem.next;
+	list_del(bmem);
+	tr->tr_num_free_bmem--;
+
+	/* Create "fake" bh to write bmem to log header block */
+	gfs_logbh_init(sdp, &lb->lb_bh, tr->tr_log_head, (char *)bmem);
+	memset(bmem, 0, sdp->sd_sb.sb_bsize);
+
+	memset(&header, 0, sizeof (header));
+
+	if (next) {
+		/* Fill in header for next transaction, committing previous */
+		header.lh_header.mh_magic = GFS_MAGIC;
+		header.lh_header.mh_type = GFS_METATYPE_LH;
+		header.lh_header.mh_format = GFS_FORMAT_LH;
+		header.lh_first = tr->tr_log_head;
+		header.lh_sequence = sdp->sd_sequence + 1;
+		header.lh_tail = current_tail(sdp);
+		header.lh_last_dump = sdp->sd_log_dump_last;
+	} else {
+		/* Fill in another header for this transaction */
+		header.lh_header.mh_magic = GFS_MAGIC;
+		header.lh_header.mh_type = GFS_METATYPE_LH;
+		header.lh_header.mh_format = GFS_FORMAT_LH;
+		header.lh_first = tr->tr_first_head;
+		header.lh_sequence = sdp->sd_sequence;
+		header.lh_tail = current_tail(sdp);
+		header.lh_last_dump = sdp->sd_log_dump_last;
+
+		/* Attach log header buf to trans' list of bufs going to log */
+		list_add(&lb->lb_list, &tr->tr_bufs);
+	}
+
+	/* Copy log header struct to beginning and end of buffer's 1st 512B */
+	gfs_log_header_out(&header, lb->lb_bh.b_data);
+	gfs_log_header_out(&header,
+			   lb->lb_bh.b_data + GFS_BASIC_BLOCK -
+			   sizeof(struct gfs_log_header));
+
+	/* Find next log buffer to fill */
+	log_incr_head(sdp, &tr->tr_log_head);
+
+	return lb;
+}
+
+/**
+ * gfs_log_get_buf - Get and initialize a buffer to use for log control data
+ * @sdp: The GFS superblock
+ * @tr: The GFS transaction
+ *
+ * Initialize one of the transaction's pre-allocated buffers (and associated
+ *   log buffer descriptor) to be used for log control data (e.g. log tags).
+ * Make sure this buffer is attached to the transaction, to be logged to disk.
+ * NOTE:  This buffer will *not* get written to an in-place location in the
+ *        filesystem; it is for use only within the log.
+ *
+ * Returns: the log buffer descriptor
+ */
+
+struct gfs_log_buf *
+gfs_log_get_buf(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct gfs_log_buf *lb;
+	struct list_head *bmem;
+
+	/* If next block in log is on a segment boundary, we need to
+	    write a log header */
+	if (gfs_log_is_header(sdp, tr->tr_log_head))
+		log_get_header(sdp, tr, FALSE);
+
+	/* Grab a free buffer descriptor (attached to trans) */
+	gfs_assert(sdp, tr->tr_num_free_bufs &&
+		   !list_empty(&tr->tr_free_bufs),);
+	lb = list_entry(tr->tr_free_bufs.next, struct gfs_log_buf, lb_list);
+	list_del(&lb->lb_list);
+	tr->tr_num_free_bufs--;
+
+	/* Grab a free buffer (attached to trans) */
+	gfs_assert(sdp, tr->tr_num_free_bmem
+		   && !list_empty(&tr->tr_free_bmem),);
+	bmem = tr->tr_free_bmem.next;
+	list_del(bmem);
+	tr->tr_num_free_bmem--;
+
+	/* Create "fake" bh to write bmem to log block */
+	gfs_logbh_init(sdp, &lb->lb_bh, tr->tr_log_head, (char *)bmem);
+	memset(bmem, 0, sdp->sd_sb.sb_bsize);
+
+	list_add(&lb->lb_list, &tr->tr_bufs);
+
+	/* Find next log buffer to fill */
+	log_incr_head(sdp, &tr->tr_log_head);
+
+	return lb;
+}
+
+/**
+ * gfs_log_fake_buf - Build a fake buffer head to write metadata buffer to log
+ * @sdp: the filesystem
+ * @tr: the transaction this is part of
+ * @data: the data the buffer_head should point to
+ * @unlock: a buffer_head to be unlocked when struct gfs_log_buf is torn down
+ *    (i.e. the "real" buffer_head that will write to in-place location)
+ *
+ * Initialize one of the transaction's pre-allocated log buffer descriptors
+ *   to be used for writing a metadata buffer into the log.
+ * Make sure this buffer is attached to the transaction, to be logged to disk.
+ * NOTE:  This buffer *will* be written to in-place location within filesytem,
+ *        in addition to being written into the log.
+ * 
+ */
+
+void
+gfs_log_fake_buf(struct gfs_sbd *sdp, struct gfs_trans *tr, char *data,
+		 struct buffer_head *unlock)
+{
+	struct gfs_log_buf *lb;
+
+	if (gfs_log_is_header(sdp, tr->tr_log_head))
+		log_get_header(sdp, tr, FALSE);
+
+	/* Grab a free buffer descriptor (attached to trans) */
+	gfs_assert(sdp, tr->tr_num_free_bufs &&
+		   !list_empty(&tr->tr_free_bufs),);
+	lb = list_entry(tr->tr_free_bufs.next, struct gfs_log_buf, lb_list);
+	list_del(&lb->lb_list);
+	tr->tr_num_free_bufs--;
+
+	/* Create "fake" bh to write data to log block */
+	gfs_logbh_init(sdp, &lb->lb_bh, tr->tr_log_head, data);
+	lb->lb_unlock = unlock;
+
+	list_add(&lb->lb_list, &tr->tr_bufs);
+
+	/* Find next log buffer to fill */
+	log_incr_head(sdp, &tr->tr_log_head);
+}
+
+/**
+ * check_seg_usage - Check that we didn't use too many segments
+ * @sdp: The GFS superblock
+ * @tr: The transaction
+ *
+ * Also, make sure we don't write ever get to a point where there are
+ * no dumps in the log (corrupting the log).  Panic before we let
+ * that happen.
+ * 
+ */
+
+static void
+check_seg_usage(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct gfs_jindex *jdesc = &sdp->sd_jdesc;
+	unsigned int dist;
+	unsigned int segments;
+	uint64_t head_off, head_wrap;
+	uint64_t dump_off, dump_wrap;
+
+	dist = log_distance(sdp, tr->tr_log_head, tr->tr_first_head);
+
+	segments = dist / sdp->sd_sb.sb_seg_size;
+	gfs_assert(sdp, segments * sdp->sd_sb.sb_seg_size == dist,);
+	gfs_assert(sdp, segments == tr->tr_seg_reserved,);
+
+	if (sdp->sd_log_dump_last) {
+		int diff;
+
+		head_off = tr->tr_first_head +
+			tr->tr_seg_reserved * sdp->sd_sb.sb_seg_size;
+		head_wrap = sdp->sd_log_wrap;
+		if (head_off >= jdesc->ji_addr +
+		    jdesc->ji_nsegment * sdp->sd_sb.sb_seg_size) {
+			head_off -= jdesc->ji_nsegment * sdp->sd_sb.sb_seg_size;
+			head_wrap++;
+		}
+
+		dump_off = sdp->sd_log_dump_last;
+		dump_wrap = sdp->sd_log_dump_last_wrap;
+
+		diff = (int)(head_wrap - dump_wrap);
+		switch (diff) {
+		case 0:
+			break;
+
+		case 1:
+			if (head_off < dump_off - sdp->sd_sb.sb_seg_size)
+				break;
+			else if (head_off <= dump_off &&
+				 (tr->tr_flags & TRF_LOG_DUMP))
+				break;
+
+		default:
+			gfs_assert(sdp, FALSE,
+				   printk("GFS: fsid=%s: head_off = %"PRIu64", head_wrap = %"PRIu64"\n"
+					  "GFS: fsid=%s: dump_off = %"PRIu64", dump_wrap = %"PRIu64"\n",
+					  sdp->sd_fsname, head_off, head_wrap,
+					  sdp->sd_fsname, dump_off, dump_wrap););
+			break;
+		}
+	}
+}
+
+/**
+ * log_free_buf - Free a struct gfs_log_buf (and possibly the data it points to)
+ * @sdp: the filesystem
+ * @lb: the log buffer descriptor
+ *
+ * If buffer contains (meta)data to be written into filesystem in-place block,
+ *   descriptor will point to the "real" (lb_unlock) buffer head.  Unlock it.
+ * If buffer was used only for log header or control data (e.g. tags), we're
+ *   done with it as soon as it gets written to on-disk log.  Free it.
+ * Either way, we can free the log descriptor structure.
+ */
+
+static void
+log_free_buf(struct gfs_sbd *sdp, struct gfs_log_buf *lb)
+{
+	char *bmem;
+
+	bmem = lb->lb_bh.b_data;
+	gfs_logbh_uninit(sdp, &lb->lb_bh);
+
+	if (lb->lb_unlock)
+		gfs_unlock_buffer(lb->lb_unlock);
+	else
+		kfree(bmem);
+
+	kfree(lb);
+}
+
+/**
+ * sync_trans - Add "last" descriptor, sync transaction to on-disk log
+ * @sdp: The GFS superblock
+ * @tr: The transaction
+ *
+ * Add the "last" descriptor onto the end of the current transaction
+ *   and sync the whole transaction out to on-disk log.
+ * Don't log-commit (i.e. write next transaction's log header) yet, though.
+ *
+ * Returns: errno
+ */
+
+static int
+sync_trans(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct list_head *tmp, *head, *prev;
+	struct gfs_log_descriptor desc;
+	struct gfs_log_buf *lb;
+	uint64_t blk;
+	int error = 0, e;
+
+	/*  Build LAST descriptor  */
+
+	lb = gfs_log_get_buf(sdp, tr);
+
+	memset(&desc, 0, sizeof(struct gfs_log_descriptor));
+	desc.ld_header.mh_magic = GFS_MAGIC;
+	desc.ld_header.mh_type = GFS_METATYPE_LD;
+	desc.ld_header.mh_format = GFS_FORMAT_LD;
+	desc.ld_type = GFS_LOG_DESC_LAST;
+	desc.ld_length = 1;
+	for (blk = tr->tr_log_head; !gfs_log_is_header(sdp, blk); blk++)
+		desc.ld_length++;
+	gfs_desc_out(&desc, lb->lb_bh.b_data);
+
+	while (!gfs_log_is_header(sdp, tr->tr_log_head))
+		log_incr_head(sdp, &tr->tr_log_head);
+
+	check_seg_usage(sdp, tr);
+
+	/* Start I/O
+	   Go in "prev" direction to start the I/O in order. */
+
+	for (head = &tr->tr_bufs, tmp = head->prev, prev = tmp->prev;
+	     tmp != head;
+	     tmp = prev, prev = tmp->prev) {
+		lb = list_entry(tmp, struct gfs_log_buf, lb_list);
+		gfs_logbh_start(sdp, &lb->lb_bh);
+	}
+
+	/* Wait on I/O
+	   Go in "next" direction to minimize sleeps/wakeups. */
+
+	while (!list_empty(&tr->tr_bufs)) {
+		lb = list_entry(tr->tr_bufs.next, struct gfs_log_buf, lb_list);
+
+		e = gfs_logbh_wait(sdp, &lb->lb_bh);
+		if (e)
+			error = e;
+
+		list_del(&lb->lb_list);
+		log_free_buf(sdp, lb);
+	}
+
+	return error;
+}
+
+/**
+ * commit_trans - Commit the current transaction
+ * @sdp: The GFS superblock
+ * @tr: The transaction
+ *
+ * Write next header to commit
+ *
+ * Returns: errno
+ */
+
+static int
+commit_trans(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct gfs_log_buf *lb;
+	int error;
+
+	lb = log_get_header(sdp, tr, TRUE);
+
+	gfs_logbh_start(sdp, &lb->lb_bh);
+	error = gfs_logbh_wait(sdp, &lb->lb_bh);
+	if (!error) {
+		spin_lock(&sdp->sd_log_seg_lock);
+		if (!(tr->tr_flags & TRF_DUMMY))
+			sdp->sd_log_seg_free += sdp->sd_log_seg_ail2;
+		else
+			sdp->sd_log_seg_free += (sdp->sd_log_seg_ail2 - 1);
+		sdp->sd_log_seg_ail2 = 0;
+		spin_unlock(&sdp->sd_log_seg_lock);
+	}
+	log_free_buf(sdp, lb);
+
+	return error;
+}
+
+/**
+ * disk_commit - Write a transaction to the on-disk journal
+ * @sdp: The GFS superblock
+ * @tr: The transaction
+ *
+ * Returns: errno
+ */
+
+static int
+disk_commit(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	uint64_t last_dump, last_dump_wrap;
+	int error = 0;
+
+	gfs_assert(sdp, !test_bit(SDF_ROFS, &sdp->sd_flags),);
+	tr->tr_log_head = sdp->sd_log_head;
+	tr->tr_first_head = tr->tr_log_head - 1;
+	gfs_assert(sdp, gfs_log_is_header(sdp, tr->tr_first_head),);
+
+	LO_BUILD_BHLIST(sdp, tr);
+
+	if (!(tr->tr_flags & TRF_DUMMY))
+		gfs_assert(sdp, !list_empty(&tr->tr_bufs),);
+
+	error = sync_trans(sdp, tr);
+	if (error) {
+		/* Eat unusable commit buffer */
+		log_free_buf(sdp, log_get_header(sdp, tr, TRUE));
+		goto out;
+	}
+
+	if (tr->tr_flags & TRF_LOG_DUMP) {
+		/* This commit header should point to the log dump we're
+		   commiting as the current one.  But save the copy of the
+		   old one in case we have problems commiting the dump. */
+
+		last_dump = sdp->sd_log_dump_last;
+		last_dump_wrap = sdp->sd_log_dump_last_wrap;
+
+		sdp->sd_log_dump_last = tr->tr_first_head;
+		sdp->sd_log_dump_last_wrap = sdp->sd_log_wrap;
+
+		error = commit_trans(sdp, tr);
+		if (error) {
+			sdp->sd_log_dump_last = last_dump;
+			sdp->sd_log_dump_last_wrap = last_dump_wrap;
+			goto out;
+		}
+	} else {
+		error = commit_trans(sdp, tr);
+		if (error)
+			goto out;
+	}
+
+	if (sdp->sd_log_head > tr->tr_log_head)
+		sdp->sd_log_wrap++;
+   sdp->sd_log_head = tr->tr_log_head;
+   sdp->sd_sequence++;
+
+ out:
+	gfs_assert_warn(sdp, !tr->tr_num_free_bufs &&
+			list_empty(&tr->tr_free_bufs));
+	gfs_assert_warn(sdp, !tr->tr_num_free_bmem &&
+			list_empty(&tr->tr_free_bmem));
+
+	return error;
+}
+
+/**
+ * add_trans_to_ail - Add a ondisk commited transaction to the AIL
+ * @sdp: the filesystem
+ * @tr: the transaction 
+ *
+ */
+
+static void
+add_trans_to_ail(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct gfs_log_element *le;
+
+	while (!list_empty(&tr->tr_elements)) {
+		le = list_entry(tr->tr_elements.next,
+				struct gfs_log_element, le_list);
+		LO_ADD_TO_AIL(sdp, le);
+	}
+
+	list_add(&tr->tr_list, &sdp->sd_log_ail);
+}
+
+/**
+ * log_refund - Refund log segments to the free pool
+ * @sdp: The GFS superblock
+ * @tr: The transaction to examine
+ *
+ * Look at the number of segments reserved for this transaction and the
+ * number of segments actually needed for it.  If they aren't the
+ * same, refund the difference to the free segment pool.
+ *
+ * De-alloc any unneeded log buffers and log buffer descriptors.
+ *
+ * Called with the log lock held.
+ */
+
+static void
+log_refund(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct gfs_log_buf *lb;
+	struct list_head *bmem;
+	unsigned int num_bufs = 0, num_bmem = 0;
+	unsigned int segments;
+
+	LO_TRANS_SIZE(sdp, tr, NULL, NULL, &num_bufs, &num_bmem);
+
+	segments = gfs_blk2seg(sdp, num_bufs + 1);
+	num_bufs += segments + 1;
+	num_bmem += segments + 1;
+
+	/* Unreserve unneeded log segments */
+	if (tr->tr_seg_reserved > segments) {
+		spin_lock(&sdp->sd_log_seg_lock);
+		sdp->sd_log_seg_free += tr->tr_seg_reserved - segments;
+		gfs_assert(sdp, sdp->sd_log_seg_free + sdp->sd_log_seg_ail2 <=
+			   sdp->sd_jdesc.ji_nsegment,);
+		spin_unlock(&sdp->sd_log_seg_lock);
+
+		tr->tr_seg_reserved = segments;
+	} else
+		gfs_assert(sdp, tr->tr_seg_reserved == segments,);
+
+	/* De-alloc unneeded log buffer descriptors */
+	gfs_assert(sdp, tr->tr_num_free_bufs >= num_bufs,);
+	while (tr->tr_num_free_bufs > num_bufs) {
+		lb = list_entry(tr->tr_free_bufs.next,
+				struct gfs_log_buf, lb_list);
+		list_del(&lb->lb_list);
+		kfree(lb);
+		tr->tr_num_free_bufs--;
+	}
+
+	/* De-alloc unneeded log buffers */
+	gfs_assert(sdp, tr->tr_num_free_bmem >= num_bmem,);
+	while (tr->tr_num_free_bmem > num_bmem) {
+		bmem = tr->tr_free_bmem.next;
+		list_del(bmem);
+		kfree(bmem);
+		tr->tr_num_free_bmem--;
+	}
+}
+
+/**
+ * trans_combine - combine two transactions
+ * @sdp: the filesystem
+ * @tr: the surviving transaction
+ * @new_tr: the transaction that gets freed
+ *
+ * Assumes that the two transactions are independent.
+ */
+
+static void
+trans_combine(struct gfs_sbd *sdp, struct gfs_trans *tr,
+	      struct gfs_trans *new_tr)
+{
+	struct gfs_log_element *le;
+	struct gfs_log_buf *lb;
+	struct list_head *bmem;
+
+	tr->tr_file = __FILE__;
+	tr->tr_line = __LINE__;
+	tr->tr_seg_reserved += new_tr->tr_seg_reserved;
+	tr->tr_flags |= new_tr->tr_flags;
+	tr->tr_num_free_bufs += new_tr->tr_num_free_bufs;
+	tr->tr_num_free_bmem += new_tr->tr_num_free_bmem;
+
+	/*  Combine the log elements of the two transactions  */
+
+	while (!list_empty(&new_tr->tr_elements)) {
+		le = list_entry(new_tr->tr_elements.next,
+				struct gfs_log_element, le_list);
+		gfs_assert(sdp, le->le_trans == new_tr,);
+		le->le_trans = tr;
+		list_move(&le->le_list, &tr->tr_elements);
+	}
+
+	LO_TRANS_COMBINE(sdp, tr, new_tr);
+
+	/* Move free log buffer descriptors to surviving trans */
+	while (!list_empty(&new_tr->tr_free_bufs)) {
+		lb = list_entry(new_tr->tr_free_bufs.next,
+				struct gfs_log_buf, lb_list);
+		list_move(&lb->lb_list, &tr->tr_free_bufs);
+		new_tr->tr_num_free_bufs--;
+	}
+	/* Move free log buffers to surviving trans */
+	while (!list_empty(&new_tr->tr_free_bmem)) {
+		bmem = new_tr->tr_free_bmem.next;
+		list_move(bmem, &tr->tr_free_bmem);
+		new_tr->tr_num_free_bmem--;
+	}
+
+	gfs_assert_warn(sdp, !new_tr->tr_num_free_bufs);
+	gfs_assert_warn(sdp, !new_tr->tr_num_free_bmem);
+
+	kfree(new_tr);
+}
+
+static void
+make_dummy_transaction(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct gfs_log_buf *lb;
+	struct list_head *bmem;
+
+	memset(tr, 0, sizeof(struct gfs_trans));
+	INIT_LIST_HEAD(&tr->tr_list);
+	INIT_LIST_HEAD(&tr->tr_elements);
+	INIT_LIST_HEAD(&tr->tr_free_bufs);
+	INIT_LIST_HEAD(&tr->tr_free_bmem);
+	INIT_LIST_HEAD(&tr->tr_bufs);
+	INIT_LIST_HEAD(&tr->tr_ail_bufs);
+	tr->tr_flags = TRF_DUMMY;
+	tr->tr_file = __FILE__;
+	tr->tr_line = __LINE__;
+	tr->tr_seg_reserved = 1;
+	while (tr->tr_num_free_bufs < 2) {
+		lb = gmalloc(sizeof(struct gfs_log_buf));
+		memset(lb, 0, sizeof(struct gfs_log_buf));
+		list_add(&lb->lb_list, &tr->tr_free_bufs);
+		tr->tr_num_free_bufs++;
+	}
+	while (tr->tr_num_free_bmem < 2) {
+		bmem = gmalloc(sdp->sd_sb.sb_bsize);
+		list_add(bmem, &tr->tr_free_bmem);
+		tr->tr_num_free_bmem++;
+	}
+}
+
+
+/**
+ * log_flush_internal - flush incore transaction(s)
+ * @sdp: the filesystem
+ * @gl: The glock structure to flush.  If NULL, flush the whole incore log
+ *
+ * If a glock is provided, we flush, to on-disk log, all of the metadata for
+ *   the one incore-committed (complete, but not-yet-flushed-to-log)
+ *   transaction that the glock protects.
+ * If NULL, we combine *all* of the filesystem's incore-committed
+ *   transactions into one big transaction, and flush it to the log.
+ */
+
+static void
+log_flush_internal(struct gfs_sbd *sdp, struct gfs_glock *gl)
+{
+	
+	struct gfs_trans *trans = NULL, *tr;
+	int error;
+
+	gfs_log_lock(sdp);
+
+	if (!gl && list_empty(&sdp->sd_log_incore)) {
+		if (sdp->sd_log_seg_ail2) {
+			trans = gmalloc(sizeof(struct gfs_trans));
+			make_dummy_transaction(sdp, trans);
+		}
+		else
+			goto out;
+	}
+
+	if (gl) {
+		if (!gl->gl_incore_le.le_trans)
+			goto out;
+
+		trans = gl->gl_incore_le.le_trans;
+
+		list_del(&trans->tr_list);
+	} else {
+		/* combine *all* transactions in incore list */
+		while (!list_empty(&sdp->sd_log_incore)) {
+			tr = list_entry(sdp->sd_log_incore.next,
+					struct gfs_trans, tr_list);
+
+			list_del(&tr->tr_list);
+
+			if (trans)
+				trans_combine(sdp, trans, tr);
+			else
+				trans = tr;
+		}
+	}
+
+	log_refund(sdp, trans);
+
+	/*  Actually do the stuff to commit the transaction  */
+
+	error = disk_commit(sdp, trans);
+	if (error)
+		gfs_io_error(sdp);
+
+	add_trans_to_ail(sdp, trans);
+
+	if (log_distance(sdp, sdp->sd_log_head, sdp->sd_log_dump_last) * GFS_DUMPS_PER_LOG >=
+	    sdp->sd_jdesc.ji_nsegment * sdp->sd_sb.sb_seg_size)
+		set_bit(SDF_NEED_LOG_DUMP, &sdp->sd_flags);
+
+ out:
+	if (list_empty(&sdp->sd_log_incore))
+		sdp->sd_vfs->s_dirt = FALSE;
+
+	gfs_log_unlock(sdp);
+
+	/*  Dump if we need to.  */
+
+	if (test_bit(SDF_NEED_LOG_DUMP, &sdp->sd_flags))
+		gfs_log_dump(sdp, FALSE);
+}
+
+/**
+ * gfs_log_flush - flush the whole incore log
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_log_flush(struct gfs_sbd *sdp)
+{
+	log_flush_internal(sdp, NULL);
+}
+
+/**
+ * gfs_log_flush_glock - flush the incore log for a glock
+ * @gl: the glock
+ *
+ */
+
+void
+gfs_log_flush_glock(struct gfs_glock *gl)
+{
+	log_flush_internal(gl->gl_sbd, gl);
+}
+
+/**
+ * incore_commit - commit a transaction in-core
+ * @sdp: the filesystem
+ * @new_tr: the transaction to commit
+ *
+ * Add the transaction @new_tr to the end of the incore commit list.
+ * Pull up and merge any previously committed transactions that share
+ * locks.  Also pull up any rename transactions that need it.
+ */
+
+static void
+incore_commit(struct gfs_sbd *sdp, struct gfs_trans *new_tr)
+{
+	struct gfs_log_element *le;
+	struct gfs_trans *trans = NULL, *exist_tr;
+	struct gfs_log_buf *lb;
+	struct list_head *bmem;
+	struct list_head *tmp, *head, *next;
+
+	for (head = &new_tr->tr_elements, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		le = list_entry(tmp, struct gfs_log_element, le_list);
+
+		/* Do overlap_trans log-op, if any, to find another
+		   incore transaction with which we can combine new_tr */
+		exist_tr = LO_OVERLAP_TRANS(sdp, le);
+		if (!exist_tr)
+			continue;
+
+		if (exist_tr != trans) {
+			/* remove trans from superblock's sd_log_incore list */
+			list_del(&exist_tr->tr_list);
+
+			/* Maybe there's more than one that can be combined.
+			   If so, combine them together before merging new_tr */
+			if (trans)
+				trans_combine(sdp, trans, exist_tr);
+			else
+				trans = exist_tr;
+		}
+	}
+
+	/* Yes, we can combine new_tr with pre-existing transaction(s) */
+	if (trans) {
+		trans->tr_file = __FILE__;
+		trans->tr_line = __LINE__;
+		trans->tr_seg_reserved += new_tr->tr_seg_reserved;
+		trans->tr_flags |= new_tr->tr_flags;
+		trans->tr_num_free_bufs += new_tr->tr_num_free_bufs;
+		trans->tr_num_free_bmem += new_tr->tr_num_free_bmem;
+
+		/* Move free log buffer descriptors to surviving trans */
+		while (!list_empty(&new_tr->tr_free_bufs)) {
+			lb = list_entry(new_tr->tr_free_bufs.next,
+					struct gfs_log_buf, lb_list);
+			list_move(&lb->lb_list, &trans->tr_free_bufs);
+			new_tr->tr_num_free_bufs--;
+		}
+
+		/* Move free log buffers to surviving trans */
+		while (!list_empty(&new_tr->tr_free_bmem)) {
+			bmem = new_tr->tr_free_bmem.next;
+			list_move(bmem, &trans->tr_free_bmem);
+			new_tr->tr_num_free_bmem--;
+		}
+	} else
+		trans = new_tr;
+
+	/* Do incore_commit log-op for each *new* log element (in new_tr).
+	   Each commit log-op removes its log element from "new_tr" LE list,
+	   and attaches an LE to "trans" LE list; if there was no trans
+	   combining, "new_tr" is the same transaction as "trans". */
+	for (head = &new_tr->tr_elements, tmp = head->next, next = tmp->next;
+	     tmp != head;
+	     tmp = next, next = next->next) {
+		le = list_entry(tmp, struct gfs_log_element, le_list);
+		LO_INCORE_COMMIT(sdp, trans, le);
+	}
+
+	/* If we successfully combined transactions, new_tr should be empty */
+	if (trans != new_tr) {
+		gfs_assert_warn(sdp, !new_tr->tr_num_free_bufs);
+		gfs_assert_warn(sdp, !new_tr->tr_num_free_bmem);
+		gfs_assert_warn(sdp, list_empty(&new_tr->tr_elements));
+		kfree(new_tr);
+	}
+
+	/* If we successfully combined transactions, we might have some log
+	   segments that we reserved, and log buffers and buffer descriptors
+	   that we allocated, but now don't need. */
+	log_refund(sdp, trans);
+
+	list_add(&trans->tr_list, &sdp->sd_log_incore);
+}
+
+/**
+ * gfs_log_commit - Commit a transaction to the log
+ * @sdp: the filesystem
+ * @tr: the transaction
+ *
+ * Returns: errno
+ */
+
+void
+gfs_log_commit(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct gfs_log_buf *lb;
+	struct list_head *bmem;
+	unsigned int num_mblks = 0, num_eblks = 0, num_bufs = 0, num_bmem = 0;
+	unsigned int segments;
+
+	/* Calculate actual log area needed for this trans */
+	LO_TRANS_SIZE(sdp, tr, &num_mblks, &num_eblks, &num_bufs, &num_bmem);
+
+	gfs_assert(sdp, num_mblks <= tr->tr_mblks_asked &&
+		   num_eblks <= tr->tr_eblks_asked,
+		   printk("GFS: fsid=%s: type = (%s, %u)\n"
+			  "GFS: fsid=%s: num_mblks = %u, tr->tr_mblks_asked = %u\n"
+			  "GFS: fsid=%s: num_eblks = %u, tr->tr_eblks_asked = %u\n",
+			  sdp->sd_fsname, tr->tr_file, tr->tr_line,
+			  sdp->sd_fsname, num_mblks, tr->tr_mblks_asked,
+			  sdp->sd_fsname, num_eblks, tr->tr_eblks_asked););
+
+	segments = gfs_blk2seg(sdp, num_bufs + 1);
+	num_bufs += segments + 1;
+	num_bmem += segments + 1;
+
+	/* Alloc log buffer descriptors */
+	while (num_bufs--) {
+		lb = gmalloc(sizeof(struct gfs_log_buf));
+		memset(lb, 0, sizeof(struct gfs_log_buf));
+		list_add(&lb->lb_list, &tr->tr_free_bufs);
+		tr->tr_num_free_bufs++;
+	}
+	/* Alloc log buffers */
+	while (num_bmem--) {
+		bmem = gmalloc(sdp->sd_sb.sb_bsize);
+		list_add(bmem, &tr->tr_free_bmem);
+		tr->tr_num_free_bmem++;
+	}
+
+	gfs_log_lock(sdp);
+
+	incore_commit(sdp, tr);
+
+	/* Flush log buffers to disk if we're over the threshold */
+	if (sdp->sd_log_buffers > gfs_tune_get(sdp, gt_incore_log_blocks)) {
+		gfs_log_unlock(sdp);
+		gfs_log_flush(sdp);
+	} else {
+		sdp->sd_vfs->s_dirt = TRUE;
+		gfs_log_unlock(sdp);
+	}
+
+}
+
+/**
+ * gfs_log_dump - make a Log Dump entry in the log
+ * @sdp: the filesystem
+ * @force: if TRUE, always make the dump even if one has been made recently
+ *
+ */
+
+void
+gfs_log_dump(struct gfs_sbd *sdp, int force)
+{
+	struct gfs_log_element *le;
+	struct gfs_trans tr;
+	struct gfs_log_buf *lb;
+	struct list_head *bmem;
+	unsigned int num_bufs, num_bmem;
+	unsigned int segments;
+	int error;
+
+	if (test_and_set_bit(SDF_IN_LOG_DUMP, &sdp->sd_flags)) {
+		gfs_assert(sdp, !force,);
+		return;
+	}
+
+	memset(&tr, 0, sizeof(struct gfs_trans));
+	INIT_LIST_HEAD(&tr.tr_elements);
+	INIT_LIST_HEAD(&tr.tr_free_bufs);
+	INIT_LIST_HEAD(&tr.tr_free_bmem);
+	INIT_LIST_HEAD(&tr.tr_bufs);
+	tr.tr_flags = TRF_LOG_DUMP;
+	tr.tr_file = __FILE__;
+	tr.tr_line = __LINE__;
+
+	for (;;) {
+		gfs_log_lock(sdp);
+
+		if (!force && !test_bit(SDF_NEED_LOG_DUMP, &sdp->sd_flags))
+			goto out;
+
+		num_bufs = num_bmem = 0;
+		LO_DUMP_SIZE(sdp, NULL, &num_bufs, &num_bmem);
+		gfs_assert(sdp, num_bufs,);
+		segments = gfs_blk2seg(sdp, num_bufs + 1);
+		num_bufs += segments + 1;
+		num_bmem += segments + 1;
+
+		if (tr.tr_seg_reserved >= segments &&
+		    tr.tr_num_free_bufs >= num_bufs &&
+		    tr.tr_num_free_bmem >= num_bmem)
+			break;
+
+		gfs_log_unlock(sdp);
+
+		if (tr.tr_seg_reserved < segments) {
+			error = gfs_log_reserve(sdp,
+						segments - tr.tr_seg_reserved,
+						TRUE);
+			gfs_assert(sdp, !error,);
+			tr.tr_seg_reserved = segments;
+		}
+		while (tr.tr_num_free_bufs < num_bufs) {
+			lb = gmalloc(sizeof(struct gfs_log_buf));
+			memset(lb, 0, sizeof(struct gfs_log_buf));
+			list_add(&lb->lb_list, &tr.tr_free_bufs);
+			tr.tr_num_free_bufs++;
+		}
+		while (tr.tr_num_free_bmem < num_bmem) {
+			bmem = gmalloc(sdp->sd_sb.sb_bsize);
+			list_add(bmem, &tr.tr_free_bmem);
+			tr.tr_num_free_bmem++;
+		}
+	}
+
+	if (tr.tr_seg_reserved > segments) {
+		spin_lock(&sdp->sd_log_seg_lock);
+		sdp->sd_log_seg_free += tr.tr_seg_reserved - segments;
+		gfs_assert(sdp, sdp->sd_log_seg_free + sdp->sd_log_seg_ail2 <=
+			   sdp->sd_jdesc.ji_nsegment,);
+		spin_unlock(&sdp->sd_log_seg_lock);
+		tr.tr_seg_reserved = segments;
+	}
+	while (tr.tr_num_free_bufs > num_bufs) {
+		lb = list_entry(tr.tr_free_bufs.next,
+				struct gfs_log_buf, lb_list);
+		list_del(&lb->lb_list);
+		kfree(lb);
+		tr.tr_num_free_bufs--;
+	}
+	while (tr.tr_num_free_bmem > num_bmem) {
+		bmem = tr.tr_free_bmem.next;
+		list_del(bmem);
+		kfree(bmem);
+		tr.tr_num_free_bmem--;
+	}
+
+	LO_BUILD_DUMP(sdp, &tr);
+
+	error = disk_commit(sdp, &tr);
+	if (error)
+		gfs_io_error(sdp);
+
+	while (!list_empty(&tr.tr_elements)) {
+		le = list_entry(tr.tr_elements.next,
+				struct gfs_log_element, le_list);
+		LO_CLEAN_DUMP(sdp, le);
+	}
+
+	/* If there isn't anything in the AIL, we won't get back the log
+	   space we reserved unless we do it ourselves. */
+
+	if (list_empty(&sdp->sd_log_ail)) {
+		spin_lock(&sdp->sd_log_seg_lock);
+		sdp->sd_log_seg_free += tr.tr_seg_reserved;
+		gfs_assert(sdp, sdp->sd_log_seg_free + sdp->sd_log_seg_ail2 <=
+			   sdp->sd_jdesc.ji_nsegment,);
+		spin_unlock(&sdp->sd_log_seg_lock);
+	}
+
+	clear_bit(SDF_NEED_LOG_DUMP, &sdp->sd_flags);
+
+ out:
+	gfs_log_unlock(sdp);
+	clear_bit(SDF_IN_LOG_DUMP, &sdp->sd_flags);
+}
+
+/**
+ * gfs_log_shutdown - write a shutdown header into a journal
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_log_shutdown(struct gfs_sbd *sdp)
+{
+	struct gfs_log_buf *lb;
+	char *bmem;
+	struct gfs_log_header head;
+	struct gfs_log_descriptor desc;
+	unsigned int elements = 0;
+	int error;
+
+	lb = gmalloc(sizeof(struct gfs_log_buf));
+	memset(lb, 0, sizeof(struct gfs_log_buf));
+	bmem = gmalloc(sdp->sd_sb.sb_bsize);
+
+	gfs_log_lock(sdp);
+
+	gfs_assert_withdraw(sdp, list_empty(&sdp->sd_log_ail));
+	gfs_assert_withdraw(sdp, sdp->sd_log_seg_free + sdp->sd_log_seg_ail2 ==
+			    sdp->sd_jdesc.ji_nsegment);
+	gfs_assert_withdraw(sdp, !sdp->sd_log_buffers);
+	gfs_assert_withdraw(sdp, gfs_log_is_header(sdp, sdp->sd_log_head - 1));
+	if (test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
+		goto out;
+
+	/*  Build a "last" log descriptor  */
+
+	memset(&desc, 0, sizeof(struct gfs_log_descriptor));
+	desc.ld_header.mh_magic = GFS_MAGIC;
+	desc.ld_header.mh_type = GFS_METATYPE_LD;
+	desc.ld_header.mh_format = GFS_FORMAT_LD;
+	desc.ld_type = GFS_LOG_DESC_LAST;
+	desc.ld_length = sdp->sd_sb.sb_seg_size - 1;
+
+	/*  Write the descriptor  */
+
+	gfs_logbh_init(sdp, &lb->lb_bh, sdp->sd_log_head, bmem);
+	memset(bmem, 0, sdp->sd_sb.sb_bsize);
+	gfs_desc_out(&desc, lb->lb_bh.b_data);
+	gfs_logbh_start(sdp, &lb->lb_bh);
+	error = gfs_logbh_wait(sdp, &lb->lb_bh);
+	gfs_logbh_uninit(sdp, &lb->lb_bh);
+
+	if (error)
+		goto out;
+
+	/*  Move to the next header  */
+
+	while (!gfs_log_is_header(sdp, sdp->sd_log_head))
+		log_incr_head(sdp, &sdp->sd_log_head);
+
+	LO_DUMP_SIZE(sdp, &elements, NULL, NULL);
+
+	/*  Build the shutdown header  */
+
+	memset(&head, 0, sizeof (struct gfs_log_header));
+	head.lh_header.mh_magic = GFS_MAGIC;
+	head.lh_header.mh_type = GFS_METATYPE_LH;
+	head.lh_header.mh_format = GFS_FORMAT_LH;
+	head.lh_flags = GFS_LOG_HEAD_UNMOUNT;
+	head.lh_first = sdp->sd_log_head;
+	head.lh_sequence = sdp->sd_sequence + 1;
+	/*  Don't care about tail  */
+	head.lh_last_dump = (elements) ? sdp->sd_log_dump_last : 0;
+
+	/*  Write out the shutdown header  */
+
+	gfs_logbh_init(sdp, &lb->lb_bh, sdp->sd_log_head, bmem);
+	memset(bmem, 0, sdp->sd_sb.sb_bsize);
+	gfs_log_header_out(&head, lb->lb_bh.b_data);
+	gfs_log_header_out(&head,
+			   lb->lb_bh.b_data + GFS_BASIC_BLOCK -
+			   sizeof(struct gfs_log_header));
+	gfs_logbh_start(sdp, &lb->lb_bh);
+	gfs_logbh_wait(sdp, &lb->lb_bh);
+	gfs_logbh_uninit(sdp, &lb->lb_bh);
+
+   /* If a withdraw is called before we've a chance to relock the trans
+    * lock, the sd_log_head points to the wrong place, and a umount will
+    * fail on asserts because of this.
+    * Adding one puts sd_log_head at a value that passes the assert.  The
+    * value may not be correct for on disk, but we've withdrawn so there is
+    * no more disk io.
+    * If we're not withdrawn, the next io will grab the trans lock, which
+    * will fill sd_log_head with the correct value.
+    */
+   sdp->sd_log_head += 1;
+
+ out:
+	gfs_log_unlock(sdp);
+
+	kfree(lb);
+	kfree(bmem);
+}
diff -pruN linux-2.6.9.orig/fs/gfs/log.h linux-2.6.9.debug/fs/gfs/log.h
--- linux-2.6.9.orig/fs/gfs/log.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/log.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,79 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __LOG_DOT_H__
+#define __LOG_DOT_H__
+
+/**
+ * gfs_log_lock - acquire the right to mess with the log manager
+ * @sdp: the filesystem
+ *
+ */
+
+static __inline__ void
+gfs_log_lock(struct gfs_sbd *sdp)
+{
+	down_write(&sdp->sd_log_lock);
+}
+
+/**
+ * gfs_log_unlock - release the right to mess with the log manager
+ * @sdp: the filesystem
+ *
+ */
+
+static __inline__ void
+gfs_log_unlock(struct gfs_sbd *sdp)
+{
+	up_write(&sdp->sd_log_lock);
+}
+
+unsigned int gfs_struct2blk(struct gfs_sbd *sdp, unsigned int nstruct,
+			    unsigned int ssize);
+unsigned int gfs_blk2seg(struct gfs_sbd *sdp, unsigned int blocks);
+
+int gfs_log_reserve(struct gfs_sbd *sdp, unsigned int segments, int jump_queue);
+void gfs_log_release(struct gfs_sbd *sdp, unsigned int segments);
+
+void gfs_ail_start(struct gfs_sbd *sdp, int flags);
+int gfs_ail_empty(struct gfs_sbd *sdp);
+
+void gfs_log_commit(struct gfs_sbd *sdp, struct gfs_trans *trans);
+void gfs_log_flush(struct gfs_sbd *sdp);
+void gfs_log_flush_glock(struct gfs_glock *gl);
+
+void gfs_log_shutdown(struct gfs_sbd *sdp);
+
+void gfs_log_dump(struct gfs_sbd *sdp, int force);
+
+/*  Internal crap used the log operations  */
+
+/**
+ * gfs_log_is_header - Discover if block is on journal header
+ * @sdp: The GFS superblock
+ * @block: The block number
+ *
+ * Returns: TRUE if the block is on a journal segment boundary, FALSE otherwise
+ */
+
+static __inline__ int
+gfs_log_is_header(struct gfs_sbd *sdp, uint64_t block)
+{
+	return !do_mod(block, sdp->sd_sb.sb_seg_size);
+}
+
+struct gfs_log_buf *gfs_log_get_buf(struct gfs_sbd *sdp, struct gfs_trans *tr);
+void gfs_log_fake_buf(struct gfs_sbd *sdp, struct gfs_trans *tr, char *data,
+		      struct buffer_head *unlock);
+
+#endif /* __LOG_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/lops.c linux-2.6.9.debug/fs/gfs/lops.c
--- linux-2.6.9.orig/fs/gfs/lops.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/lops.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1661 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "glock.h"
+#include "log.h"
+#include "lops.h"
+#include "quota.h"
+#include "recovery.h"
+#include "trans.h"
+#include "unlinked.h"
+
+/**
+ * generic_le_add - generic routine to add a log element to a transaction
+ * @sdp: the filesystem
+ * @le: the log entry
+ *
+ */
+
+static void
+generic_le_add(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	struct gfs_trans *tr;
+
+	/* Make sure it's not attached to a transaction already */
+	gfs_assert(sdp, le->le_ops &&
+		   !le->le_trans &&
+		   list_empty(&le->le_list),);
+
+	/* Attach it to the (one) transaction being built by this process */
+	tr = current_transaction;
+	gfs_assert(sdp, tr,);
+
+	le->le_trans = tr;
+	list_add(&le->le_list, &tr->tr_elements);
+}
+
+/**
+ * glock_trans_end - drop a glock reference
+ * @sdp: the filesystem
+ * @le: the log element
+ *
+ * Called before incore-committing a transaction
+ * Release reference that was taken in gfs_trans_add_gl()
+ */
+
+static void
+glock_trans_end(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	struct gfs_glock *gl = container_of(le, struct gfs_glock, gl_new_le);
+
+	gfs_assert(sdp, gfs_glock_is_locked_by_me(gl) &&
+		   gfs_glock_is_held_excl(gl),);
+	gfs_glock_put(gl);
+}
+
+/**
+ * glock_print - print debug info about a log element
+ * @sdp: the filesystem
+ * @le: the log element
+ * @where: is this a new transaction or a incore transaction
+ *
+ */
+
+static void
+glock_print(struct gfs_sbd *sdp, struct gfs_log_element *le, unsigned int where)
+{
+	struct gfs_glock *gl;
+
+	switch (where) {
+	case TRANS_IS_NEW:
+		gl = container_of(le, struct gfs_glock, gl_new_le);
+		break;
+	case TRANS_IS_INCORE:
+		gl = container_of(le, struct gfs_glock, gl_incore_le);
+		break;
+	default:
+		gfs_assert_warn(sdp, FALSE);
+		return;
+	}
+
+	printk("  Glock:  (%u, %"PRIu64")\n",
+	       gl->gl_name.ln_type,
+	       gl->gl_name.ln_number);
+}
+
+/**
+ * glock_overlap_trans - Find any incore transactions that might overlap with
+ *   (i.e. be combinable with the transaction containing) this LE
+ * @sdp: the filesystem
+ * @le: the log element
+ *
+ * Transactions that share a given glock are combinable.
+ *
+ * For a glock, the scope of the "search" is just the (max) one unique incore
+ *   committed transaction to which the glock may be attached via its
+ *   gl->gl_incore_le embedded log element.  This trans may have previously
+ *   been combined with other transactions, though (i.e. previous
+ *   incore committed transactions that shared the same glock).
+ *  
+ * Called as a beginning part of the incore commit of a transaction.
+ */
+
+static struct gfs_trans *
+glock_overlap_trans(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	struct gfs_glock *gl = container_of(le, struct gfs_glock, gl_new_le);
+
+	return gl->gl_incore_le.le_trans;
+}
+
+/**
+ * glock_incore_commit - commit this LE to the incore log
+ * @sdp: the filesystem
+ * @tr: the being-incore-committed transaction this LE is to be a part of
+ * @le: the log element (should be a gl->gl_new_le), which is attached
+ *      to a "new" (just-ended) transaction.
+ *      
+ * Attach glock's gl_incore_le to the being-incore-committed trans' LE list.
+ * Remove glock's gl_new_le from the just-ended new trans' LE list.
+ * If the just-ended new trans (le->le_trans) was combined (in incore_commit())
+ *   with a pre-existing incore trans (tr), this function effectively moves
+ *   the LE from the new to the combined incore trans.
+ * If there was no combining, then the new trans itself is being committed
+ *   (le->le_trans == tr); this function simply replaces the gl_new_le with a
+ *   gl_incore_le on the trans' LE list.
+ * 
+ * Make sure that this glock's gl_incore_le is attached to one and only one
+ *   incore-committed transaction's (this one's) tr_elements list.
+ *   One transaction (instead of a list of transactions) is sufficient,
+ *   because incore_commit() combines multiple transactions that share a glock
+ *   into one trans.
+ * Since transactions can contain multiple glocks, there are multiple
+ *   possibilities for shared glocks, therefore multiple potential "bridges"
+ *   for combining transactions.
+ */
+
+static void
+glock_incore_commit(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		    struct gfs_log_element *le)
+{
+	struct gfs_glock *gl = container_of(le, struct gfs_glock, gl_new_le);
+
+	/* Transactions were combined, based on this glock */
+	if (gl->gl_incore_le.le_trans)
+		gfs_assert(sdp, gl->gl_incore_le.le_trans == tr,);
+	else {
+		/* Attach gl->gl_incore_le to being-committed trans */
+		gl->gl_incore_le.le_trans = tr;
+		list_add(&gl->gl_incore_le.le_list, &tr->tr_elements);
+
+		/* If transactions were combined (via another shared glock),
+		   the combined trans is getting a new glock log element */
+		if (tr != le->le_trans)
+			tr->tr_num_gl++;
+	}
+
+	/* Remove gl->gl_new_le from "new" trans */
+	le->le_trans = NULL;
+	list_del_init(&le->le_list);
+}
+
+/**
+ * glock_add_to_ail - Add this LE to the AIL
+ * @sdp: the filesystem
+ * @le: the log element
+ *
+ * Glocks don't really get added to AIL (there's nothing to write to disk),
+ * they just get removed from the transaction at this time.
+ */
+
+static void
+glock_add_to_ail(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	le->le_trans = NULL;
+	list_del_init(&le->le_list);
+}
+
+/**
+ * glock_trans_combine - combine two incore transactions
+ * @sdp: the filesystem
+ * @tr: the surviving transaction
+ * @new_tr: the transaction that's going to disappear
+ *
+ */
+
+static void
+glock_trans_combine(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		    struct gfs_trans *new_tr)
+{
+	tr->tr_num_gl += new_tr->tr_num_gl;
+}
+
+/**
+ * buf_print - print debug info about a log element
+ * @sdp: the filesystem
+ * @le: the log element
+ * @where: is this a new transaction or a incore transaction
+ *
+ */
+
+static void
+buf_print(struct gfs_sbd *sdp, struct gfs_log_element *le, unsigned int where)
+{
+	struct gfs_bufdata *bd;
+
+	switch (where) {
+	case TRANS_IS_NEW:
+		bd = container_of(le, struct gfs_bufdata, bd_new_le);
+		break;
+	case TRANS_IS_INCORE:
+		bd = container_of(le, struct gfs_bufdata, bd_incore_le);
+		break;
+	default:
+		gfs_assert_warn(sdp, FALSE);
+		return;
+	}
+
+	printk("  Buffer:  %"PRIu64"\n", (uint64_t)bd->bd_bh->b_blocknr);
+}
+
+/**
+ * buf_incore_commit - commit this buffer LE to the incore log
+ * @sdp: the filesystem
+ * @tr: the incore transaction this LE is a part of
+ * @le: the log element for the "new" (just now complete) trans
+ *
+ * Invoked from incore_commit().
+ * Move this buffer from "new" stage to "incore committed" stage of the
+ *   transaction pipeline.
+ * If this buffer was already attached to a pre-existing incore trans, GFS is
+ *   combining the new and incore transactions; decrement buffer's recursive
+ *   pin count that was incremented when it was added to the new transaction,
+ *   and remove the reference to the "new" (being swallowed) trans.
+ * Else, move this buffer's attach point from "new" to "incore" embedded LE
+ *   (same transaction, just new status) and add this buf to (incore) trans'
+ *   LE list.
+ */
+
+static void
+buf_incore_commit(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		  struct gfs_log_element *le)
+{
+	struct gfs_bufdata *bd = container_of(le, struct gfs_bufdata, bd_new_le);
+
+	/* We've completed our (atomic) changes to this buffer for this trans.
+	   We no longer need the frozen copy.  If frozen copy was not written
+	   to on-disk log already, there's no longer a need to; we can now
+	   write the "real" buffer (with more up-to-date content) instead. */
+	if (bd->bd_frozen) {
+		kfree(bd->bd_frozen);
+		bd->bd_frozen = NULL;
+	}
+
+	/* New trans being combined with pre-existing incore trans? */
+	if (bd->bd_incore_le.le_trans) {
+		gfs_assert(sdp, bd->bd_incore_le.le_trans == tr,);
+		gfs_dunpin(sdp, bd->bd_bh, NULL);
+	} else {
+		bd->bd_incore_le.le_trans = tr;
+		list_add(&bd->bd_incore_le.le_list, &tr->tr_elements);
+		if (tr != le->le_trans)
+			tr->tr_num_buf++;
+
+		sdp->sd_log_buffers++;
+	}
+
+	/* Reset buffer's bd_new_le */
+	le->le_trans = NULL;
+	list_del_init(&le->le_list);
+}
+
+/**
+ * buf_add_to_ail - Add this LE to the AIL
+ * @sdp: the filesystem
+ * @le: the log element
+ *
+ */
+
+static void
+buf_add_to_ail(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	struct gfs_bufdata *bd = container_of(le,
+					       struct gfs_bufdata,
+					       bd_incore_le);
+
+	gfs_dunpin(sdp, bd->bd_bh, le->le_trans);
+
+	le->le_trans = NULL;
+	list_del_init(&le->le_list);
+
+	gfs_assert(sdp, sdp->sd_log_buffers,);
+	sdp->sd_log_buffers--;
+}
+
+/**
+ * buf_trans_size - compute how much space the LE class takes up in a transaction
+ * @sdp: the filesystem
+ * @tr: the transaction
+ * @mblks: the number of regular metadata blocks
+ * @eblks: the number of extra blocks
+ * @blocks: the number of log blocks
+ * @bmem: the number of buffer-sized chunks of memory we need
+ *
+ */
+
+static void
+buf_trans_size(struct gfs_sbd *sdp, struct gfs_trans *tr,
+	       unsigned int *mblks, unsigned int *eblks,
+	       unsigned int *blocks, unsigned int *bmem)
+{
+	unsigned int cblks;
+
+	if (tr->tr_num_buf) {
+		cblks = gfs_struct2blk(sdp, tr->tr_num_buf,
+				       sizeof(struct gfs_block_tag));
+
+		if (mblks)
+			*mblks += tr->tr_num_buf;
+		if (blocks)
+			*blocks += tr->tr_num_buf + cblks;
+		if (bmem)
+			*bmem += cblks;
+	}
+}
+
+/**
+ * buf_trans_combine - combine two incore transactions
+ * @sdp: the filesystem
+ * @tr: the surviving transaction
+ * @new_tr: the transaction that's going to disappear
+ *
+ */
+
+static void
+buf_trans_combine(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		  struct gfs_trans *new_tr)
+{
+	tr->tr_num_buf += new_tr->tr_num_buf;
+}
+
+/**
+ * increment_generation - increment the generation number in metadata buffer
+ * @sdp: the filesystem
+ * @bd: the struct gfs_bufdata structure associated with the buffer
+ *
+ * Increment the generation # of the most recent buffer contents, as well as
+ *   that of frozen buffer, if any.  If there is a frozen buffer, only *it*
+ *   will be going to the log now ... in this case, the current buffer will
+ *   have its gen # incremented again later, when it gets written to log.
+ * Gen # is used by journal recovery (replay_block()) to determine whether
+ *   to overwrite an inplace block with the logged block contents.
+ */
+
+static void
+increment_generation(struct gfs_sbd *sdp, struct gfs_bufdata *bd)
+{
+	struct gfs_meta_header *mh, *mh2;
+	uint64_t tmp64;
+
+	mh = (struct gfs_meta_header *)bd->bd_bh->b_data;
+
+	tmp64 = gfs64_to_cpu(mh->mh_generation) + 1;
+	tmp64 = cpu_to_gfs64(tmp64);
+
+	if (bd->bd_frozen) {
+		mh2 = (struct gfs_meta_header *)bd->bd_frozen;
+		gfs_assert(sdp, mh->mh_generation == mh2->mh_generation,);
+		mh2->mh_generation = tmp64;
+	}
+	mh->mh_generation = tmp64;
+}
+
+/**
+ * buf_build_bhlist - create the buffers that will make up the ondisk part of a transaction
+ * @sdp: the filesystem
+ * @tr: the transaction
+ *
+ * Create the log (transaction) descriptor block
+ */
+
+static void
+buf_build_bhlist(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct list_head *tmp, *head;
+	struct gfs_log_element *le;
+	struct gfs_bufdata *bd;
+	struct gfs_log_descriptor desc;
+	struct gfs_block_tag tag;
+	struct gfs_log_buf *clb = NULL;
+	unsigned int num_ctl;
+	unsigned int offset = sizeof(struct gfs_log_descriptor);
+	unsigned int x, bufs;
+
+	if (!tr->tr_num_buf)
+		return;
+
+	/* set up control buffers for descriptor and tags */
+
+	num_ctl = gfs_struct2blk(sdp, tr->tr_num_buf,
+				 sizeof(struct gfs_block_tag));
+
+	for (x = 0; x < num_ctl; x++) {
+		if (clb)
+			gfs_log_get_buf(sdp, tr);
+		else
+			clb = gfs_log_get_buf(sdp, tr);
+	}
+
+	/* Init and copy log descriptor into 1st control block */
+	memset(&desc, 0, sizeof(struct gfs_log_descriptor));
+	desc.ld_header.mh_magic = GFS_MAGIC;
+	desc.ld_header.mh_type = GFS_METATYPE_LD;
+	desc.ld_header.mh_format = GFS_FORMAT_LD;
+	desc.ld_type = GFS_LOG_DESC_METADATA;
+	desc.ld_length = num_ctl + tr->tr_num_buf;
+	desc.ld_data1 = tr->tr_num_buf;
+	gfs_desc_out(&desc, clb->lb_bh.b_data);
+
+	x = 1;
+	bufs = 0;
+
+	for (head = &tr->tr_elements, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		le = list_entry(tmp, struct gfs_log_element, le_list);
+
+		/* Skip over non-buffer (e.g. glock, unlinked, etc.) LEs */
+		if (le->le_ops != &gfs_buf_lops)
+			continue;
+
+		bd = container_of(le, struct gfs_bufdata, bd_incore_le);
+
+		gfs_meta_check(sdp, bd->bd_bh);
+
+		gfs_lock_buffer(bd->bd_bh);
+
+		increment_generation(sdp, bd);
+
+		/* Create "fake" buffer head to write block to on-disk log.  Use
+		   frozen copy if another transaction is modifying the "real"
+		   buffer contents.  Unlock real bh after log write completes,
+		   so Linux can write real contents to inplace block. */
+		gfs_log_fake_buf(sdp, tr,
+				 (bd->bd_frozen) ? bd->bd_frozen : bd->bd_bh->b_data,
+				 bd->bd_bh);
+
+		/* find another buffer for tags if we're overflowing this one */
+		if (offset + sizeof(struct gfs_block_tag) > sdp->sd_sb.sb_bsize) {
+			clb = list_entry(clb->lb_list.prev,
+					 struct gfs_log_buf, lb_list);
+			if (gfs_log_is_header(sdp, clb->lb_bh.b_blocknr))
+				clb = list_entry(clb->lb_list.prev,
+						 struct gfs_log_buf, lb_list);
+			x++;
+			offset = 0;
+		}
+
+		/* Write this LE's tag into a control buffer */
+		memset(&tag, 0, sizeof(struct gfs_block_tag));
+		tag.bt_blkno = bd->bd_bh->b_blocknr;
+
+		gfs_block_tag_out(&tag, clb->lb_bh.b_data + offset);
+
+		offset += sizeof(struct gfs_block_tag);
+		bufs++;
+	}
+
+	gfs_assert(sdp, x == num_ctl,);
+	gfs_assert(sdp, bufs == tr->tr_num_buf,);
+}
+
+/**
+ * buf_before_scan - called before journal replay
+ * @sdp: the filesystem
+ * @jid: the journal ID about to be replayed
+ * @head: the current head of the log
+ * @pass: the pass through the journal
+ *
+ */
+
+static void
+buf_before_scan(struct gfs_sbd *sdp, unsigned int jid,
+		struct gfs_log_header *head, unsigned int pass)
+{
+	if (pass == GFS_RECPASS_A1)
+		sdp->sd_recovery_replays =
+			sdp->sd_recovery_skips =
+			sdp->sd_recovery_sames = 0;
+}
+
+/**
+ * replay_block - Replay a single metadata block
+ * @sdp: the filesystem
+ * @jdesc: the struct gfs_jindex structure for the journal being replayed
+ * @gl: the journal's glock
+ * @tag: the block tag describing the inplace location of the block
+ * @blkno: the location of the log's copy of the block
+ *
+ * Returns: errno
+ *
+ * Read in-place block from disk
+ * Read log (journal) block from disk
+ * Compare generation numbers
+ * Copy log block to in-place block on-disk if:
+ *   log generation # > in-place generation #
+ *   OR generation #s are ==, but data contained in block is different (corrupt)
+ */
+
+static int
+replay_block(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+	     struct gfs_glock *gl, struct gfs_block_tag *tag, uint64_t blkno)
+{
+	struct buffer_head *inplace_bh, *log_bh;
+	struct gfs_meta_header inplace_mh, log_mh;
+	int replay_block = TRUE;
+	int error = 0;
+
+	gfs_replay_check(sdp);
+
+	/* Warning:  Using a real buffer here instead of a tempbh can be bad
+	   on a OS that won't support multiple simultaneous buffers for the
+	   same block on different glocks. */
+
+	error = gfs_dread(gl, tag->bt_blkno,
+			  DIO_START | DIO_WAIT, &inplace_bh);
+	if (error)
+		return error;
+	if (gfs_meta_check(sdp, inplace_bh)) {
+		brelse(inplace_bh);
+		return -EIO;
+	}
+	gfs_meta_header_in(&inplace_mh, inplace_bh->b_data);
+
+	error = gfs_dread(gl, blkno, DIO_START | DIO_WAIT, &log_bh);
+	if (error) {
+		brelse(inplace_bh);
+		return error;
+	}
+	if (gfs_meta_check(sdp, log_bh)) {
+		brelse(inplace_bh);
+		brelse(log_bh);
+		return -EIO;
+	}
+	gfs_meta_header_in(&log_mh, log_bh->b_data);
+
+	if (log_mh.mh_generation < inplace_mh.mh_generation) {
+		replay_block = FALSE;
+		sdp->sd_recovery_skips++;
+	} else if (log_mh.mh_generation == inplace_mh.mh_generation) {
+		if (memcmp(log_bh->b_data,
+			   inplace_bh->b_data,
+			   sdp->sd_sb.sb_bsize) == 0) {
+			replay_block = FALSE;
+			sdp->sd_recovery_sames++;
+		}
+	}
+
+	if (replay_block) {
+		memcpy(inplace_bh->b_data,
+		       log_bh->b_data,
+		       sdp->sd_sb.sb_bsize);
+
+		error = gfs_replay_buf(gl, inplace_bh);
+		if (!error)
+			sdp->sd_recovery_replays++;
+	}
+
+	brelse(log_bh);
+	brelse(inplace_bh);
+
+	return error;
+}
+
+/**
+ * buf_scan_elements - Replay a metadata log descriptor
+ * @sdp: the filesystem
+ * @jdesc: the struct gfs_jindex structure for the journal being replayed
+ * @gl: the journal's glock
+ * @start: the starting block of the descriptor
+ * @desc: the descriptor structure
+ * @pass: the pass through the journal
+ *
+ * Returns: errno
+ */
+
+static int
+buf_scan_elements(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+		  struct gfs_glock *gl, uint64_t start,
+		  struct gfs_log_descriptor *desc, unsigned int pass)
+{
+	struct gfs_block_tag tag;
+	struct buffer_head *bh;
+	uint64_t cblk = start;
+	unsigned int num_tags = desc->ld_data1;
+	unsigned int offset = sizeof(struct gfs_log_descriptor);
+	unsigned int x;
+	int error;
+
+	if (pass != GFS_RECPASS_A1)
+		return 0;
+	if (desc->ld_type != GFS_LOG_DESC_METADATA)
+		return 0;
+
+	x = gfs_struct2blk(sdp, num_tags, sizeof(struct gfs_block_tag));
+	while (x--) {
+		error = gfs_increment_blkno(sdp, jdesc, gl, &start, TRUE);
+		if (error)
+			return error;
+	}
+
+	for (;;) {
+		gfs_assert(sdp, num_tags,);
+
+		error = gfs_dread(gl, cblk, DIO_START | DIO_WAIT, &bh);
+		if (error)
+			return error;
+
+		/* Do readahead for the inplace blocks in this control block */
+		{
+			unsigned int o2 = offset;
+			unsigned int nt2 = num_tags;
+
+			while (o2 + sizeof(struct gfs_block_tag) <=
+			       sdp->sd_sb.sb_bsize) {
+				gfs_block_tag_in(&tag, bh->b_data + o2);
+				gfs_start_ra(gl, tag.bt_blkno, 1);
+				if (!--nt2)
+					break;
+				o2 += sizeof(struct gfs_block_tag);
+			}
+		}
+
+		while (offset + sizeof(struct gfs_block_tag) <=
+		       sdp->sd_sb.sb_bsize) {
+			gfs_block_tag_in(&tag, bh->b_data + offset);
+
+			error = replay_block(sdp, jdesc, gl, &tag, start);
+			if (error)
+				goto out_drelse;
+
+			if (!--num_tags)
+				goto out_drelse;
+
+			error = gfs_increment_blkno(sdp, jdesc, gl, &start, TRUE);
+			if (error)
+				goto out_drelse;
+
+			offset += sizeof(struct gfs_block_tag);
+		}
+
+		brelse(bh);
+
+		error = gfs_increment_blkno(sdp, jdesc, gl, &cblk, TRUE);
+		if (error)
+			return error;
+
+		offset = 0;
+	}
+
+	return 0;
+
+ out_drelse:
+	brelse(bh);
+
+	return error;
+}
+
+/**
+ * buf_after_scan - called after journal replay
+ * @sdp: the filesystem
+ * @jid: the journal ID about to be replayed
+ * @pass: the pass through the journal
+ *
+ */
+
+static void
+buf_after_scan(struct gfs_sbd *sdp, unsigned int jid, unsigned int pass)
+{
+	if (pass == GFS_RECPASS_A1) {
+		printk("GFS: fsid=%s: jid=%u: Replayed %u of %u blocks\n",
+		       sdp->sd_fsname, jid,
+		       sdp->sd_recovery_replays,
+		       sdp->sd_recovery_replays + sdp->sd_recovery_skips +
+		       sdp->sd_recovery_sames);
+		printk("GFS: fsid=%s: jid=%u: replays = %u, skips = %u, sames = %u\n",
+		       sdp->sd_fsname, jid, sdp->sd_recovery_replays,
+		       sdp->sd_recovery_skips, sdp->sd_recovery_sames);
+	}
+}
+
+/**
+ * unlinked_print - print debug info about a log element
+ * @sdp: the filesystem
+ * @le: the log element
+ * @where: is this a new transaction or a incore transaction
+ *
+ */
+
+static void
+unlinked_print(struct gfs_sbd *sdp, struct gfs_log_element *le,
+	       unsigned int where)
+{
+	struct gfs_unlinked *ul;
+	char *type;
+
+	switch (where) {
+	case TRANS_IS_NEW:
+		ul = container_of(le, struct gfs_unlinked, ul_new_le);
+		type = (test_bit(ULF_NEW_UL, &ul->ul_flags)) ?
+			"unlink" : "dealloc";
+		break;
+	case TRANS_IS_INCORE:
+		ul = container_of(le, struct gfs_unlinked, ul_incore_le);
+		type = (test_bit(ULF_INCORE_UL, &ul->ul_flags)) ?
+			"unlink" : "dealloc";
+		break;
+	default:
+		gfs_assert_warn(sdp, FALSE);
+		return;
+	}
+
+	printk("  unlinked:  %"PRIu64"/%"PRIu64", %s\n",
+	       ul->ul_inum.no_formal_ino, ul->ul_inum.no_addr,
+	       type);
+}
+
+/**
+ * unlinked_incore_commit - commit this LE to the incore log
+ * @sdp: the filesystem
+ * @tr: the incore transaction this LE is a part of
+ * @le: the log element
+ *
+ */
+
+static void
+unlinked_incore_commit(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		       struct gfs_log_element *le)
+{
+	struct gfs_unlinked *ul = container_of(le,
+					       struct gfs_unlinked,
+					       ul_new_le);
+	int n = !!test_bit(ULF_NEW_UL, &ul->ul_flags);
+	int i = !!test_bit(ULF_INCORE_UL, &ul->ul_flags);
+
+	if (ul->ul_incore_le.le_trans) {
+		gfs_assert(sdp, ul->ul_incore_le.le_trans == tr,);
+		gfs_assert(sdp, n != i,);
+
+		ul->ul_incore_le.le_trans = NULL;
+		list_del_init(&ul->ul_incore_le.le_list);
+		gfs_unlinked_put(sdp, ul);
+
+		if (i) {
+			gfs_assert(sdp, tr->tr_num_iul,);
+			tr->tr_num_iul--;
+		} else {
+			gfs_assert(sdp, tr->tr_num_ida,);
+			tr->tr_num_ida--;
+		}
+	} else {
+		gfs_unlinked_hold(sdp, ul);
+		ul->ul_incore_le.le_trans = tr;
+		list_add(&ul->ul_incore_le.le_list, &tr->tr_elements);
+
+		if (n) {
+			set_bit(ULF_INCORE_UL, &ul->ul_flags);
+			if (tr != le->le_trans)
+				tr->tr_num_iul++;
+		} else {
+			clear_bit(ULF_INCORE_UL, &ul->ul_flags);
+			if (tr != le->le_trans)
+				tr->tr_num_ida++;
+		}
+	}
+
+	if (n) {
+		gfs_unlinked_hold(sdp, ul);
+		gfs_assert(sdp, !test_bit(ULF_IC_LIST, &ul->ul_flags),);
+		set_bit(ULF_IC_LIST, &ul->ul_flags);
+		atomic_inc(&sdp->sd_unlinked_ic_count);
+	} else {
+		gfs_assert(sdp, test_bit(ULF_IC_LIST, &ul->ul_flags),);
+		clear_bit(ULF_IC_LIST, &ul->ul_flags);
+		gfs_unlinked_put(sdp, ul);
+		gfs_assert(sdp, atomic_read(&sdp->sd_unlinked_ic_count),);
+		atomic_dec(&sdp->sd_unlinked_ic_count);
+	}
+
+	le->le_trans = NULL;
+	list_del_init(&le->le_list);
+	gfs_unlinked_put(sdp, ul);
+}
+
+/**
+ * unlinked_add_to_ail - Add this LE to the AIL
+ * @sdp: the filesystem
+ * @le: the log element
+ *
+ */
+
+static void
+unlinked_add_to_ail(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	struct gfs_unlinked *ul = container_of(le,
+						struct gfs_unlinked,
+						ul_incore_le);
+	int i = !!test_bit(ULF_INCORE_UL, &ul->ul_flags);
+
+	if (i) {
+		gfs_unlinked_hold(sdp, ul);
+		gfs_assert(sdp, !test_bit(ULF_OD_LIST, &ul->ul_flags),);
+		set_bit(ULF_OD_LIST, &ul->ul_flags);
+		atomic_inc(&sdp->sd_unlinked_od_count);
+	} else {
+		gfs_assert(sdp, test_bit(ULF_OD_LIST, &ul->ul_flags),);
+		clear_bit(ULF_OD_LIST, &ul->ul_flags);
+		gfs_unlinked_put(sdp, ul);
+		gfs_assert(sdp, atomic_read(&sdp->sd_unlinked_od_count),);
+		atomic_dec(&sdp->sd_unlinked_od_count);
+	}
+
+	le->le_trans = NULL;
+	list_del_init(&le->le_list);
+	gfs_unlinked_put(sdp, ul);
+}
+
+/**
+ * unlinked_clean_dump - clean up a LE after a log dump
+ * @sdp: the filesystem
+ * @le: the log element
+ *
+ */
+
+static void
+unlinked_clean_dump(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	le->le_trans = NULL;
+	list_del_init(&le->le_list);
+}
+
+/**
+ * unlinked_trans_size - compute how much space the LE class takes up in a transaction
+ * @sdp: the filesystem
+ * @tr: the transaction
+ * @mblks: the number of regular metadata blocks
+ * @eblks: the number of extra blocks
+ * @blocks: the number of log blocks
+ * @bmem: the number of buffer-sized chunks of memory we need
+ *
+ */
+
+static void
+unlinked_trans_size(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		    unsigned int *mblks, unsigned int *eblks,
+		    unsigned int *blocks, unsigned int *bmem)
+{
+	unsigned int ublks = 0;
+
+	if (tr->tr_num_iul)
+		ublks = gfs_struct2blk(sdp, tr->tr_num_iul,
+				       sizeof(struct gfs_inum));
+	if (tr->tr_num_ida)
+		ublks += gfs_struct2blk(sdp, tr->tr_num_ida,
+					sizeof(struct gfs_inum));
+
+	if (eblks)
+		*eblks += ublks;
+	if (blocks)
+		*blocks += ublks;
+	if (bmem)
+		*bmem += ublks;
+}
+
+/**
+ * unlinked_trans_combine - combine two incore transactions
+ * @sdp: the filesystem
+ * @tr: the surviving transaction
+ * @new_tr: the transaction that's going to disappear
+ *
+ */
+
+static void
+unlinked_trans_combine(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		       struct gfs_trans *new_tr)
+{
+	tr->tr_num_iul += new_tr->tr_num_iul;
+	tr->tr_num_ida += new_tr->tr_num_ida;
+}
+
+/**
+ * unlinked_build_bhlist - create the buffers that will make up the ondisk part of a transaction
+ * @sdp: the filesystem
+ * @tr: the transaction
+ *
+ * For unlinked and/or deallocated inode log elements (separately):
+ *   Get a log block
+ *   Create a log descriptor in beginning of that block
+ *   Fill rest of block with gfs_inum structs to identify each inode
+ *     that became unlinked/deallocated during this transaction.
+ *   Get another log block if needed, continue filling with gfs_inums.
+ */
+
+static void
+unlinked_build_bhlist(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct list_head *tmp, *head;
+	struct gfs_log_element *le;
+	struct gfs_unlinked *ul;
+	struct gfs_log_descriptor desc;
+	struct gfs_log_buf *lb;
+	unsigned int pass = 2;
+	unsigned int type, number;
+	unsigned int offset, entries;
+
+	/* 2 passes:  1st for Unlinked, 2nd for De-Alloced inodes,
+	     unless this is a log dump:  just 1 pass, for Unlinked */
+	while (pass--) {
+		if (tr->tr_flags & TRF_LOG_DUMP) {
+			if (pass) {
+				type = GFS_LOG_DESC_IUL;
+				number = tr->tr_num_iul;
+			} else
+				break;
+		} else {
+			if (pass) {
+				type = GFS_LOG_DESC_IUL;
+				number = tr->tr_num_iul;
+			} else {
+				type = GFS_LOG_DESC_IDA;
+				number = tr->tr_num_ida;
+			}
+
+			if (!number)
+				continue;
+		}
+
+		lb = gfs_log_get_buf(sdp, tr);
+
+		/* Header:  log descriptor */
+		memset(&desc, 0, sizeof(struct gfs_log_descriptor));
+		desc.ld_header.mh_magic = GFS_MAGIC;
+		desc.ld_header.mh_type = GFS_METATYPE_LD;
+		desc.ld_header.mh_format = GFS_FORMAT_LD;
+		desc.ld_type = type;
+		desc.ld_length = gfs_struct2blk(sdp, number, sizeof(struct gfs_inum));
+		desc.ld_data1 = (tr->tr_flags & TRF_LOG_DUMP) ? TRUE : FALSE;
+		gfs_desc_out(&desc, lb->lb_bh.b_data);
+
+		offset = sizeof(struct gfs_log_descriptor);
+		entries = 0;
+
+		/* Look through transaction's log elements for Unlinked LEs */
+		for (head = &tr->tr_elements, tmp = head->next;
+		     tmp != head;
+		     tmp = tmp->next) {
+			le = list_entry(tmp, struct gfs_log_element, le_list);
+			if (le->le_ops != &gfs_unlinked_lops)
+				continue;
+			if (tr->tr_flags & TRF_LOG_DUMP)
+				ul = container_of(le,
+						  struct gfs_unlinked,
+						  ul_ondisk_le);
+			else {
+				ul = container_of(le,
+						  struct gfs_unlinked,
+						  ul_incore_le);
+				if (!!test_bit(ULF_INCORE_UL, &ul->ul_flags) != pass)
+					continue;
+			}
+
+			if (offset + sizeof(struct gfs_inum) > sdp->sd_sb.sb_bsize) {
+				offset = 0;
+				lb = gfs_log_get_buf(sdp, tr);
+			}
+
+			/* Payload:  write the inode identifier */
+			gfs_inum_out(&ul->ul_inum,
+				     lb->lb_bh.b_data + offset);
+
+			offset += sizeof(struct gfs_inum);
+			entries++;
+		}
+
+		gfs_assert(sdp, entries == number,);
+	}
+}
+
+/**
+ * unlinked_dump_size - compute how much space the LE class takes up in a log dump
+ * @sdp: the filesystem
+ * @elements: the number of log elements in the dump
+ * @blocks: the number of blocks in the dump
+ * @bmem: the number of buffer-sized chunks of memory we need
+ *
+ */
+
+static void
+unlinked_dump_size(struct gfs_sbd *sdp, unsigned int *elements,
+		   unsigned int *blocks, unsigned int *bmem)
+{
+	unsigned int c = atomic_read(&sdp->sd_unlinked_od_count);
+	unsigned int b = gfs_struct2blk(sdp, c, sizeof(struct gfs_inum));
+
+	if (elements)
+		*elements += c;
+	if (blocks)
+		*blocks += b;
+	if (bmem)
+		*bmem += b;
+}
+
+/**
+ * unlinked_build_dump - create a transaction that represents a log dump for this LE class
+ * @sdp: the filesystem
+ * @tr: the transaction to fill
+ *
+ */
+
+static void
+unlinked_build_dump(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct list_head *tmp, *head;
+	struct gfs_unlinked *ul;
+	unsigned int x = 0;
+
+	tr->tr_num_iul = atomic_read(&sdp->sd_unlinked_od_count);
+
+	spin_lock(&sdp->sd_unlinked_lock);
+
+	for (head = &sdp->sd_unlinked_list, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		ul = list_entry(tmp, struct gfs_unlinked, ul_list);
+		if (!test_bit(ULF_OD_LIST, &ul->ul_flags))
+			continue;
+
+		gfs_assert(sdp, !ul->ul_ondisk_le.le_trans,);
+		ul->ul_ondisk_le.le_trans = tr;
+		list_add(&ul->ul_ondisk_le.le_list, &tr->tr_elements);
+
+		x++;
+	}
+
+	spin_unlock(&sdp->sd_unlinked_lock);
+
+	gfs_assert(sdp, x == atomic_read(&sdp->sd_unlinked_od_count),);
+}
+
+/**
+ * unlinked_before_scan - called before a log dump is recovered
+ * @sdp: the filesystem
+ * @jid: the journal ID about to be scanned
+ * @head: the current head of the log
+ * @pass: the pass through the journal
+ *
+ */
+
+static void
+unlinked_before_scan(struct gfs_sbd *sdp, unsigned int jid,
+		     struct gfs_log_header *head, unsigned int pass)
+{
+	if (pass == GFS_RECPASS_B1)
+		clear_bit(SDF_FOUND_UL_DUMP, &sdp->sd_flags);
+}
+
+/**
+ * unlinked_scan_elements - scan unlinked inodes from the journal
+ * @sdp: the filesystem
+ * @jdesc: the struct gfs_jindex structure for the journal being scaned
+ * @gl: the journal's glock
+ * @start: the starting block of the descriptor
+ * @desc: the descriptor structure
+ * @pass: the pass through the journal
+ *
+ * Returns: errno
+ */
+
+static int
+unlinked_scan_elements(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+		       struct gfs_glock *gl, uint64_t start,
+		       struct gfs_log_descriptor *desc, unsigned int pass)
+{
+	struct gfs_inum inum;
+	struct buffer_head *bh;
+	unsigned int offset = sizeof(struct gfs_log_descriptor);
+	unsigned int x;
+	int error;
+
+	if (pass != GFS_RECPASS_B1)
+		return 0;
+
+	switch (desc->ld_type) {
+	case GFS_LOG_DESC_IUL:
+		if (test_bit(SDF_FOUND_UL_DUMP, &sdp->sd_flags))
+			gfs_assert(sdp, !desc->ld_data1,);
+		else {
+			gfs_assert(sdp, desc->ld_data1,);
+			set_bit(SDF_FOUND_UL_DUMP, &sdp->sd_flags);
+		}
+		break;
+
+	case GFS_LOG_DESC_IDA:
+		gfs_assert(sdp, test_bit(SDF_FOUND_UL_DUMP, &sdp->sd_flags),);
+		break;
+
+	default:
+		return 0;
+	}
+
+	for (x = 0; x < desc->ld_length; x++) {
+		error = gfs_dread(gl, start, DIO_START | DIO_WAIT, &bh);
+		if (error)
+			return error;
+
+		for (;
+		     offset + sizeof(struct gfs_inum) <= sdp->sd_sb.sb_bsize;
+		     offset += sizeof(struct gfs_inum)) {
+			gfs_inum_in(&inum, bh->b_data + offset);
+
+			if (inum.no_addr)
+				gfs_unlinked_merge(sdp, desc->ld_type, &inum);
+		}
+
+		brelse(bh);
+
+		error = gfs_increment_blkno(sdp, jdesc, gl, &start, TRUE);
+		if (error)
+			return error;
+
+		offset = 0;
+	}
+
+	return 0;
+}
+
+/**
+ * unlinked_after_scan - called after a log dump is recovered
+ * @sdp: the filesystem
+ * @jid: the journal ID about to be scanned
+ * @pass: the pass through the journal
+ *
+ */
+
+static void
+unlinked_after_scan(struct gfs_sbd *sdp, unsigned int jid, unsigned int pass)
+{
+	if (pass == GFS_RECPASS_B1) {
+		gfs_assert(sdp, test_bit(SDF_FOUND_UL_DUMP, &sdp->sd_flags),);
+		printk("GFS: fsid=%s: Found %d unlinked inodes\n",
+		       sdp->sd_fsname, atomic_read(&sdp->sd_unlinked_ic_count));
+	}
+}
+
+/**
+ * quota_print - print debug info about a log element
+ * @sdp: the filesystem
+ * @le: the log element
+ * @where: is this a new transaction or a incore transaction
+ *
+ */
+
+static void
+quota_print(struct gfs_sbd *sdp, struct gfs_log_element *le, unsigned int where)
+{
+	struct gfs_quota_le *ql;
+
+	ql = container_of(le, struct gfs_quota_le, ql_le);
+	printk("  quota:  %s %u:  %"PRId64" blocks\n",
+	       (test_bit(QDF_USER, &ql->ql_data->qd_flags)) ? "user" : "group",
+	       ql->ql_data->qd_id, ql->ql_change);
+}
+
+/**
+ * quota_incore_commit - commit this LE to the incore log
+ * @sdp: the filesystem
+ * @tr: the incore transaction this LE is a part of
+ * @le: the log element
+ *
+ */
+
+static void
+quota_incore_commit(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		    struct gfs_log_element *le)
+{
+	struct gfs_quota_le *ql = container_of(le, struct gfs_quota_le, ql_le);
+	struct gfs_quota_data *qd = ql->ql_data;
+
+	gfs_assert(sdp, ql->ql_change,);
+
+	/*  Make this change under the sd_quota_lock, so other processes
+	   checking qd_change_ic don't have to acquire the log lock.  */
+
+	spin_lock(&sdp->sd_quota_lock);
+	qd->qd_change_new -= ql->ql_change;
+	qd->qd_change_ic += ql->ql_change;
+	spin_unlock(&sdp->sd_quota_lock);
+
+	if (le->le_trans == tr)
+		list_add(&ql->ql_data_list, &qd->qd_le_list);
+	else {
+		struct list_head *tmp, *head;
+		struct gfs_quota_le *tmp_ql;
+		int found = FALSE;
+
+		for (head = &qd->qd_le_list, tmp = head->next;
+		     tmp != head;
+		     tmp = tmp->next) {
+			tmp_ql = list_entry(tmp, struct gfs_quota_le, ql_data_list);
+			if (tmp_ql->ql_le.le_trans != tr)
+				continue;
+
+			tmp_ql->ql_change += ql->ql_change;
+
+			list_del(&le->le_list);
+			gfs_quota_put(sdp, qd);
+			kfree(ql);
+
+			if (!tmp_ql->ql_change) {
+				list_del(&tmp_ql->ql_data_list);
+				list_del(&tmp_ql->ql_le.le_list);
+				gfs_quota_put(sdp, tmp_ql->ql_data);
+				kfree(tmp_ql);
+				tr->tr_num_q--;
+			}
+
+			found = TRUE;
+			break;
+		}
+
+		if (!found) {
+			le->le_trans = tr;
+			list_move(&le->le_list, &tr->tr_elements);
+			tr->tr_num_q++;
+			list_add(&ql->ql_data_list, &qd->qd_le_list);
+		}
+	}
+}
+
+/**
+ * quota_add_to_ail - Add this LE to the AIL
+ * @sdp: the filesystem
+ * @le: the log element
+ *
+ */
+
+static void
+quota_add_to_ail(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	struct gfs_quota_le *ql = container_of(le, struct gfs_quota_le, ql_le);
+	struct gfs_quota_data *qd = ql->ql_data;
+
+	qd->qd_change_od += ql->ql_change;
+	if (qd->qd_change_od) {
+		if (!test_bit(QDF_OD_LIST, &qd->qd_flags)) {
+			gfs_quota_hold(sdp, qd);
+			set_bit(QDF_OD_LIST, &qd->qd_flags);
+			atomic_inc(&sdp->sd_quota_od_count);
+		}
+	} else {
+		gfs_assert(sdp, test_bit(QDF_OD_LIST, &qd->qd_flags),);
+		clear_bit(QDF_OD_LIST, &qd->qd_flags);
+		gfs_quota_put(sdp, qd);
+		gfs_assert(sdp, atomic_read(&sdp->sd_quota_od_count),);
+		atomic_dec(&sdp->sd_quota_od_count);
+	}
+
+	list_del(&ql->ql_data_list);
+	list_del(&le->le_list);
+	gfs_quota_put(sdp, qd);
+	kfree(ql);
+}
+
+/**
+ * quota_clean_dump - clean up a LE after a log dump
+ * @sdp: the filesystem
+ * @le: the log element
+ *
+ */
+
+static void
+quota_clean_dump(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	le->le_trans = NULL;
+	list_del_init(&le->le_list);
+}
+
+/**
+ * quota_trans_size - compute how much space the LE class takes up in a transaction
+ * @sdp: the filesystem
+ * @tr: the transaction
+ * @mblks: the number of regular metadata blocks
+ * @eblks: the number of extra blocks
+ * @blocks: the number of log blocks
+ * @bmem: the number of buffer-sized chunks of memory we need
+ *
+ */
+
+static void
+quota_trans_size(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		 unsigned int *mblks, unsigned int *eblks,
+		 unsigned int *blocks, unsigned int *bmem)
+{
+	unsigned int qblks;
+
+	if (tr->tr_num_q) {
+		qblks = gfs_struct2blk(sdp, tr->tr_num_q,
+				       sizeof(struct gfs_quota_tag));
+
+		if (eblks)
+			*eblks += qblks;
+		if (blocks)
+			*blocks += qblks;
+		if (bmem)
+			*bmem += qblks;
+	}
+}
+
+/**
+ * quota_trans_combine - combine two incore transactions
+ * @sdp: the filesystem
+ * @tr: the surviving transaction
+ * @new_tr: the transaction that's going to disappear
+ *
+ */
+
+static void
+quota_trans_combine(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		    struct gfs_trans *new_tr)
+{
+	tr->tr_num_q += new_tr->tr_num_q;
+}
+
+/**
+ * quota_build_bhlist - create the buffers that will make up the ondisk part of a transaction
+ * @sdp: the filesystem
+ * @tr: the transaction
+ *
+ */
+
+static void
+quota_build_bhlist(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct list_head *tmp, *head;
+	struct gfs_log_element *le;
+	struct gfs_quota_le *ql;
+	struct gfs_log_descriptor desc;
+	struct gfs_quota_tag tag;
+	struct gfs_log_buf *lb;
+	unsigned int offset = sizeof(struct gfs_log_descriptor), entries = 0;
+
+	if (!tr->tr_num_q && !(tr->tr_flags & TRF_LOG_DUMP))
+		return;
+
+	lb = gfs_log_get_buf(sdp, tr);
+
+	memset(&desc, 0, sizeof(struct gfs_log_descriptor));
+	desc.ld_header.mh_magic = GFS_MAGIC;
+	desc.ld_header.mh_type = GFS_METATYPE_LD;
+	desc.ld_header.mh_format = GFS_FORMAT_LD;
+	desc.ld_type = GFS_LOG_DESC_Q;
+	desc.ld_length = gfs_struct2blk(sdp, tr->tr_num_q,
+					sizeof(struct gfs_quota_tag));
+	desc.ld_data1 = tr->tr_num_q;
+	desc.ld_data2 = (tr->tr_flags & TRF_LOG_DUMP) ? TRUE : FALSE;
+	gfs_desc_out(&desc, lb->lb_bh.b_data);
+
+	for (head = &tr->tr_elements, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		le = list_entry(tmp, struct gfs_log_element, le_list);
+		if (le->le_ops != &gfs_quota_lops)
+			continue;
+
+		ql = container_of(le, struct gfs_quota_le, ql_le);
+
+		if (offset + sizeof(struct gfs_quota_tag) >
+		    sdp->sd_sb.sb_bsize) {
+			offset = 0;
+			lb = gfs_log_get_buf(sdp, tr);
+		}
+
+		memset(&tag, 0, sizeof(struct gfs_quota_tag));
+		tag.qt_change = ql->ql_change;
+		tag.qt_flags = (test_bit(QDF_USER, &ql->ql_data->qd_flags)) ?
+			GFS_QTF_USER : 0;
+		tag.qt_id = ql->ql_data->qd_id;
+
+		gfs_quota_tag_out(&tag, lb->lb_bh.b_data + offset);
+
+		offset += sizeof(struct gfs_quota_tag);
+		entries++;
+	}
+
+	gfs_assert(sdp, entries == tr->tr_num_q,);
+}
+
+/**
+ * quota_dump_size - compute how much space the LE class takes up in a log dump
+ * @sdp: the filesystem
+ * @elements: the number of log elements in the dump
+ * @blocks: the number of blocks in the dump
+ * @bmem: the number of buffer-sized chunks of memory we need
+ *
+ */
+
+static void
+quota_dump_size(struct gfs_sbd *sdp, unsigned int *elements,
+		unsigned int *blocks, unsigned int *bmem)
+{
+	unsigned int c = atomic_read(&sdp->sd_quota_od_count);
+	unsigned int b = gfs_struct2blk(sdp, c, sizeof(struct gfs_quota_tag));
+
+	if (elements)
+		*elements += c;
+	if (blocks)
+		*blocks += b;
+	if (bmem)
+		*bmem += b;
+}
+
+/**
+ * quota_build_dump - create a transaction that represents a log dump for this LE class
+ * @sdp: the filesystem
+ * @tr: the transaction to fill
+ *
+ */
+
+static void
+quota_build_dump(struct gfs_sbd *sdp, struct gfs_trans *tr)
+{
+	struct list_head *tmp, *head;
+	struct gfs_quota_data *qd;
+	struct gfs_quota_le *ql;
+	unsigned int x = 0;
+
+	tr->tr_num_q = atomic_read(&sdp->sd_quota_od_count);
+
+	spin_lock(&sdp->sd_quota_lock);
+
+	for (head = &sdp->sd_quota_list, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		qd = list_entry(tmp, struct gfs_quota_data, qd_list);
+		if (!test_bit(QDF_OD_LIST, &qd->qd_flags))
+			continue;
+
+		ql = &qd->qd_ondisk_ql;
+
+		ql->ql_le.le_ops = &gfs_quota_lops;
+		gfs_assert(sdp, !ql->ql_le.le_trans,);
+		ql->ql_le.le_trans = tr;
+		list_add(&ql->ql_le.le_list, &tr->tr_elements);
+
+		ql->ql_data = qd;
+		ql->ql_change = qd->qd_change_od;
+
+		x++;
+	}
+
+	spin_unlock(&sdp->sd_quota_lock);
+
+	gfs_assert(sdp, x == atomic_read(&sdp->sd_quota_od_count),);
+}
+
+/**
+ * quota_before_scan - called before a log dump is recovered
+ * @sdp: the filesystem
+ * @jid: the journal ID about to be scanned
+ * @head: the current head of the log
+ * @pass: the pass through the journal
+ *
+ */
+
+static void
+quota_before_scan(struct gfs_sbd *sdp, unsigned int jid,
+		  struct gfs_log_header *head, unsigned int pass)
+{
+	if (pass == GFS_RECPASS_B1)
+		clear_bit(SDF_FOUND_Q_DUMP, &sdp->sd_flags);
+}
+
+/**
+ * quota_scan_elements - scan quota inodes from the journal
+ * @sdp: the filesystem
+ * @jdesc: the struct gfs_jindex structure for the journal being scaned
+ * @gl: the journal's glock
+ * @start: the starting block of the descriptor
+ * @desc: the descriptor structure
+ * @pass: the pass through the journal
+ *
+ * Returns: errno
+ */
+
+static int
+quota_scan_elements(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+		    struct gfs_glock *gl, uint64_t start,
+		    struct gfs_log_descriptor *desc, unsigned int pass)
+{
+	struct gfs_quota_tag tag;
+	struct buffer_head *bh;
+	unsigned int num_tags = desc->ld_data1;
+	unsigned int offset = sizeof(struct gfs_log_descriptor);
+	unsigned int x;
+	int error;
+
+	if (pass != GFS_RECPASS_B1)
+		return 0;
+	if (desc->ld_type != GFS_LOG_DESC_Q)
+		return 0;
+
+	if (test_bit(SDF_FOUND_Q_DUMP, &sdp->sd_flags))
+		gfs_assert(sdp, !desc->ld_data2,);
+	else {
+		gfs_assert(sdp, desc->ld_data2,);
+		set_bit(SDF_FOUND_Q_DUMP, &sdp->sd_flags);
+	}
+
+	if (!num_tags)
+		return 0;
+
+	for (x = 0; x < desc->ld_length; x++) {
+		error = gfs_dread(gl, start, DIO_START | DIO_WAIT, &bh);
+		if (error)
+			return error;
+
+		while (offset + sizeof(struct gfs_quota_tag) <=
+		       sdp->sd_sb.sb_bsize) {
+			gfs_quota_tag_in(&tag, bh->b_data + offset);
+
+			error = gfs_quota_merge(sdp, &tag);
+			if (error)
+				goto out_drelse;
+
+			if (!--num_tags)
+				goto out_drelse;
+
+			offset += sizeof(struct gfs_quota_tag);
+		}
+
+		brelse(bh);
+
+		error = gfs_increment_blkno(sdp, jdesc, gl, &start, TRUE);
+		if (error)
+			return error;
+
+		offset = 0;
+	}
+
+	return 0;
+
+ out_drelse:
+	brelse(bh);
+
+	return error;
+}
+
+/**
+ * quota_after_scan - called after a log dump is recovered
+ * @sdp: the filesystem
+ * @jid: the journal ID about to be scanned
+ * @pass: the pass through the journal
+ *
+ */
+
+static void
+quota_after_scan(struct gfs_sbd *sdp, unsigned int jid, unsigned int pass)
+{
+	if (pass == GFS_RECPASS_B1) {
+		gfs_assert(sdp, !sdp->sd_sb.sb_quota_di.no_formal_ino ||
+			   test_bit(SDF_FOUND_Q_DUMP, &sdp->sd_flags),);
+		printk("GFS: fsid=%s: Found quota changes for %d IDs\n",
+		       sdp->sd_fsname, atomic_read(&sdp->sd_quota_od_count));
+	}
+}
+
+struct gfs_log_operations gfs_glock_lops = {
+	.lo_add = generic_le_add,
+	.lo_trans_end = glock_trans_end,
+	.lo_print = glock_print,
+	.lo_overlap_trans = glock_overlap_trans,
+	.lo_incore_commit = glock_incore_commit,
+	.lo_add_to_ail = glock_add_to_ail,
+	.lo_trans_combine = glock_trans_combine,
+	.lo_name = "glock"
+};
+
+struct gfs_log_operations gfs_buf_lops = {
+	.lo_add = generic_le_add,
+	.lo_print = buf_print,
+	.lo_incore_commit = buf_incore_commit,
+	.lo_add_to_ail = buf_add_to_ail,
+	.lo_trans_size = buf_trans_size,
+	.lo_trans_combine = buf_trans_combine,
+	.lo_build_bhlist = buf_build_bhlist,
+	.lo_before_scan = buf_before_scan,
+	.lo_scan_elements = buf_scan_elements,
+	.lo_after_scan = buf_after_scan,
+	.lo_name = "buf"
+};
+
+struct gfs_log_operations gfs_unlinked_lops = {
+	.lo_add = generic_le_add,
+	.lo_print = unlinked_print,
+	.lo_incore_commit = unlinked_incore_commit,
+	.lo_add_to_ail = unlinked_add_to_ail,
+	.lo_clean_dump = unlinked_clean_dump,
+	.lo_trans_size = unlinked_trans_size,
+	.lo_trans_combine = unlinked_trans_combine,
+	.lo_build_bhlist = unlinked_build_bhlist,
+	.lo_dump_size = unlinked_dump_size,
+	.lo_build_dump = unlinked_build_dump,
+	.lo_before_scan = unlinked_before_scan,
+	.lo_scan_elements = unlinked_scan_elements,
+	.lo_after_scan = unlinked_after_scan,
+	.lo_name = "unlinked"
+};
+
+struct gfs_log_operations gfs_quota_lops = {
+	.lo_add = generic_le_add,
+	.lo_print = quota_print,
+	.lo_incore_commit = quota_incore_commit,
+	.lo_add_to_ail = quota_add_to_ail,
+	.lo_clean_dump = quota_clean_dump,
+	.lo_trans_size = quota_trans_size,
+	.lo_trans_combine = quota_trans_combine,
+	.lo_build_bhlist = quota_build_bhlist,
+	.lo_dump_size = quota_dump_size,
+	.lo_build_dump = quota_build_dump,
+	.lo_before_scan = quota_before_scan,
+	.lo_scan_elements = quota_scan_elements,
+	.lo_after_scan = quota_after_scan,
+	.lo_name = "quota"
+};
+
+struct gfs_log_operations *gfs_log_ops[] = {
+	&gfs_glock_lops,
+	&gfs_buf_lops,
+	&gfs_unlinked_lops,
+	&gfs_quota_lops,
+	NULL
+};
diff -pruN linux-2.6.9.orig/fs/gfs/lops.h linux-2.6.9.debug/fs/gfs/lops.h
--- linux-2.6.9.orig/fs/gfs/lops.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/lops.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,179 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __LOPS_DOT_H__
+#define __LOPS_DOT_H__
+
+extern struct gfs_log_operations gfs_glock_lops;
+extern struct gfs_log_operations gfs_buf_lops;
+extern struct gfs_log_operations gfs_unlinked_lops;
+extern struct gfs_log_operations gfs_quota_lops;
+
+extern struct gfs_log_operations *gfs_log_ops[];
+
+#define INIT_LE(le, lops) \
+do \
+{ \
+  (le)->le_ops = (lops); \
+  (le)->le_trans = NULL; \
+  INIT_LIST_HEAD(&(le)->le_list); \
+} \
+while (0)
+
+#define LO_ADD(sdp, le) \
+do \
+{ \
+  if ((le)->le_ops->lo_add) \
+    (le)->le_ops->lo_add((sdp), (le)); \
+} \
+while (0)
+
+#define LO_TRANS_END(sdp, le) \
+do \
+{ \
+  if ((le)->le_ops->lo_trans_end) \
+    (le)->le_ops->lo_trans_end((sdp), (le)); \
+} \
+while (0)
+
+#define LO_PRINT(sdp, le, where) \
+do \
+{ \
+  if ((le)->le_ops->lo_print) \
+    (le)->le_ops->lo_print((sdp), (le), (where)); \
+} \
+while (0)
+
+static __inline__ struct gfs_trans *
+LO_OVERLAP_TRANS(struct gfs_sbd *sdp, struct gfs_log_element *le)
+{
+	if (le->le_ops->lo_overlap_trans)
+		return le->le_ops->lo_overlap_trans(sdp, le);
+	else
+		return NULL;
+}
+
+#define LO_INCORE_COMMIT(sdp, tr, le) \
+do \
+{ \
+  if ((le)->le_ops->lo_incore_commit) \
+    (le)->le_ops->lo_incore_commit((sdp), (tr), (le)); \
+} \
+while (0)
+
+#define LO_ADD_TO_AIL(sdp, le) \
+do \
+{ \
+  if ((le)->le_ops->lo_add_to_ail) \
+    (le)->le_ops->lo_add_to_ail((sdp), (le)); \
+} \
+while (0)
+
+#define LO_CLEAN_DUMP(sdp, le) \
+do \
+{ \
+  if ((le)->le_ops->lo_clean_dump) \
+    (le)->le_ops->lo_clean_dump((sdp), (le)); \
+} \
+while (0)
+
+#define LO_TRANS_SIZE(sdp, tr, mblks, eblks, blocks, bmem) \
+do \
+{ \
+  int __lops_x; \
+  for (__lops_x = 0; gfs_log_ops[__lops_x]; __lops_x++) \
+    if (gfs_log_ops[__lops_x]->lo_trans_size) \
+      gfs_log_ops[__lops_x]->lo_trans_size((sdp), (tr), (mblks), (eblks), (blocks), (bmem)); \
+} \
+while (0)
+
+#define LO_TRANS_COMBINE(sdp, tr, new_tr) \
+do \
+{ \
+  int __lops_x; \
+  for (__lops_x = 0; gfs_log_ops[__lops_x]; __lops_x++) \
+    if (gfs_log_ops[__lops_x]->lo_trans_combine) \
+      gfs_log_ops[__lops_x]->lo_trans_combine((sdp), (tr), (new_tr)); \
+} \
+while (0)
+
+#define LO_BUILD_BHLIST(sdp, tr) \
+do \
+{ \
+  int __lops_x; \
+  for (__lops_x = 0; gfs_log_ops[__lops_x]; __lops_x++) \
+    if (gfs_log_ops[__lops_x]->lo_build_bhlist) \
+      gfs_log_ops[__lops_x]->lo_build_bhlist((sdp), (tr)); \
+} \
+while (0)
+
+#define LO_DUMP_SIZE(sdp, elements, blocks, bmem) \
+do \
+{ \
+  int __lops_x; \
+  for (__lops_x = 0; gfs_log_ops[__lops_x]; __lops_x++) \
+    if (gfs_log_ops[__lops_x]->lo_dump_size) \
+      gfs_log_ops[__lops_x]->lo_dump_size((sdp), (elements), (blocks), (bmem)); \
+} \
+while (0)
+
+#define LO_BUILD_DUMP(sdp, tr) \
+do \
+{ \
+  int __lops_x; \
+  for (__lops_x = 0; gfs_log_ops[__lops_x]; __lops_x++) \
+    if (gfs_log_ops[__lops_x]->lo_build_dump) \
+      gfs_log_ops[__lops_x]->lo_build_dump((sdp), (tr)); \
+} \
+while (0)
+
+#define LO_BEFORE_SCAN(sdp, jid, head, pass) \
+do \
+{ \
+  int __lops_x; \
+  for (__lops_x = 0; gfs_log_ops[__lops_x]; __lops_x++) \
+    if (gfs_log_ops[__lops_x]->lo_before_scan) \
+      gfs_log_ops[__lops_x]->lo_before_scan((sdp), (jid), (head), (pass)); \
+} \
+while (0)
+
+static __inline__ int
+LO_SCAN_ELEMENTS(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+		 struct gfs_glock *gl, uint64_t start,
+		 struct gfs_log_descriptor *desc, unsigned int pass)
+{
+	int x;
+	int error;
+
+	for (x = 0; gfs_log_ops[x]; x++)
+		if (gfs_log_ops[x]->lo_scan_elements) {
+			error = gfs_log_ops[x]->lo_scan_elements(sdp, jdesc, gl,
+								 start, desc, pass);
+			if (error)
+				return error;
+		}
+
+	return 0;
+}
+
+#define LO_AFTER_SCAN(sdp, jid, pass) \
+do \
+{ \
+  int __lops_x; \
+  for (__lops_x = 0; gfs_log_ops[__lops_x]; __lops_x++) \
+    if (gfs_log_ops[__lops_x]->lo_after_scan) \
+      gfs_log_ops[__lops_x]->lo_after_scan((sdp), (jid), (pass)); \
+} \
+while (0)
+
+#endif /* __LOPS_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/lvb.c linux-2.6.9.debug/fs/gfs/lvb.c
--- linux-2.6.9.orig/fs/gfs/lvb.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/lvb.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,148 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+
+#define pv(struct, member, fmt) printk("  "#member" = "fmt"\n", struct->member);
+
+#define CPIN_08(s1, s2, member, count) {memcpy((s1->member), (s2->member), (count));}
+#define CPOUT_08(s1, s2, member, count) {memcpy((s2->member), (s1->member), (count));}
+#define CPIN_16(s1, s2, member) {(s1->member) = gfs16_to_cpu((s2->member));}
+#define CPOUT_16(s1, s2, member) {(s2->member) = cpu_to_gfs16((s1->member));}
+#define CPIN_32(s1, s2, member) {(s1->member) = gfs32_to_cpu((s2->member));}
+#define CPOUT_32(s1, s2, member) {(s2->member) = cpu_to_gfs32((s1->member));}
+#define CPIN_64(s1, s2, member) {(s1->member) = gfs64_to_cpu((s2->member));}
+#define CPOUT_64(s1, s2, member) {(s2->member) = cpu_to_gfs64((s1->member));}
+
+/**
+ * gfs_rgrp_lvb_in - Read in rgrp data
+ * @rb: the cpu-order structure
+ * @lvb: the lvb
+ *
+ */
+
+void
+gfs_rgrp_lvb_in(struct gfs_rgrp_lvb *rb, char *lvb)
+{
+	struct gfs_rgrp_lvb *str = (struct gfs_rgrp_lvb *)lvb;
+
+	CPIN_32(rb, str, rb_magic);
+	CPIN_32(rb, str, rb_free);
+	CPIN_32(rb, str, rb_useddi);
+	CPIN_32(rb, str, rb_freedi);
+	CPIN_32(rb, str, rb_usedmeta);
+	CPIN_32(rb, str, rb_freemeta);
+}
+
+/**
+ * gfs_rgrp_lvb_out - Write out rgrp data
+ * @rb: the cpu-order structure
+ * @lvb: the lvb
+ *
+ */
+
+void
+gfs_rgrp_lvb_out(struct gfs_rgrp_lvb *rb, char *lvb)
+{
+	struct gfs_rgrp_lvb *str = (struct gfs_rgrp_lvb *)lvb;
+
+	CPOUT_32(rb, str, rb_magic);
+	CPOUT_32(rb, str, rb_free);
+	CPOUT_32(rb, str, rb_useddi);
+	CPOUT_32(rb, str, rb_freedi);
+	CPOUT_32(rb, str, rb_usedmeta);
+	CPOUT_32(rb, str, rb_freemeta);
+}
+
+/**
+ * gfs_rgrp_lvb_print - Print out rgrp data
+ * @rb: the cpu-order structure
+ * @console - TRUE if this should be printed to the console,
+ *            FALSE if it should be just printed to the incore debug
+ *            buffer
+ */
+
+void
+gfs_rgrp_lvb_print(struct gfs_rgrp_lvb *rb)
+{
+	pv(rb, rb_magic, "%u");
+	pv(rb, rb_free, "%u");
+	pv(rb, rb_useddi, "%u");
+	pv(rb, rb_freedi, "%u");
+	pv(rb, rb_usedmeta, "%u");
+	pv(rb, rb_freemeta, "%u");
+}
+
+/**
+ * gfs_quota_lvb_in - Read in quota data
+ * @rb: the cpu-order structure
+ * @lvb: the lvb
+ *
+ */
+
+void
+gfs_quota_lvb_in(struct gfs_quota_lvb *qb, char *lvb)
+{
+	struct gfs_quota_lvb *str = (struct gfs_quota_lvb *)lvb;
+
+	CPIN_32(qb, str, qb_magic);
+	CPIN_32(qb, str, qb_pad);
+	CPIN_64(qb, str, qb_limit);
+	CPIN_64(qb, str, qb_warn);
+	CPIN_64(qb, str, qb_value);
+}
+
+/**
+ * gfs_quota_lvb_out - Write out quota data
+ * @rb: the cpu-order structure
+ * @lvb: the lvb
+ *
+ */
+
+void
+gfs_quota_lvb_out(struct gfs_quota_lvb *qb, char *lvb)
+{
+	struct gfs_quota_lvb *str = (struct gfs_quota_lvb *)lvb;
+
+	CPOUT_32(qb, str, qb_magic);
+	CPOUT_32(qb, str, qb_pad);
+	CPOUT_64(qb, str, qb_limit);
+	CPOUT_64(qb, str, qb_warn);
+	CPOUT_64(qb, str, qb_value);
+}
+
+/**
+ * gfs_quota_lvb_print - Print out quota data
+ * @rb: the cpu-order structure
+ * @console - TRUE if this should be printed to the console,
+ *            FALSE if it should be just printed to the incore debug
+ *            buffer
+ */
+
+void
+gfs_quota_lvb_print(struct gfs_quota_lvb *qb)
+{
+	pv(qb, qb_magic, "%u");
+	pv(qb, qb_pad, "%u");
+	pv(qb, qb_limit, "%"PRIu64);
+	pv(qb, qb_warn, "%"PRIu64);
+	pv(qb, qb_value, "%"PRId64);
+}
diff -pruN linux-2.6.9.orig/fs/gfs/lvb.h linux-2.6.9.debug/fs/gfs/lvb.h
--- linux-2.6.9.orig/fs/gfs/lvb.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/lvb.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,66 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+ * Formats of Lock Value Blocks (LVBs) for various types of locks.
+ * These 32-bit data chunks can be shared quickly between nodes
+ *   via the inter-node lock manager (via LAN instead of on-disk).
+ */
+
+#ifndef __LVB_DOT_H__
+#define __LVB_DOT_H__
+
+#define GFS_MIN_LVB_SIZE (32)
+
+/*
+ * Resource Group block allocation statistics
+ * Each resource group lock contains one of these in its LVB.
+ * Used for sharing approximate current statistics for statfs.
+ * Not used for actual block allocation.
+ */
+struct gfs_rgrp_lvb {
+	uint32_t rb_magic;      /* GFS_MAGIC sanity check value */
+	uint32_t rb_free;       /* # free data blocks */
+	uint32_t rb_useddi;     /* # used dinode blocks */
+	uint32_t rb_freedi;     /* # free dinode blocks */
+	uint32_t rb_usedmeta;   /* # used metadata blocks */
+	uint32_t rb_freemeta;   /* # free metadata blocks */
+};
+
+/*
+ * Quota
+ * Each quota lock contains one of these in its LVB.
+ * Keeps track of block allocation limits and current block allocation
+ *   for either a cluster-wide user or a cluster-wide group.
+ */
+struct gfs_quota_lvb {
+	uint32_t qb_magic;      /* GFS_MAGIC sanity check value */
+	uint32_t qb_pad;
+	uint64_t qb_limit;      /* Hard limit of # blocks to alloc */
+	uint64_t qb_warn;       /* Warn user when alloc is above this # */
+	int64_t qb_value;       /* Current # blocks allocated */
+};
+
+/*  Translation functions  */
+
+void gfs_rgrp_lvb_in(struct gfs_rgrp_lvb *rb, char *lvb);
+void gfs_rgrp_lvb_out(struct gfs_rgrp_lvb *rb, char *lvb);
+void gfs_quota_lvb_in(struct gfs_quota_lvb *qb, char *lvb);
+void gfs_quota_lvb_out(struct gfs_quota_lvb *qb, char *lvb);
+
+/*  Printing functions  */
+
+void gfs_rgrp_lvb_print(struct gfs_rgrp_lvb *rb);
+void gfs_quota_lvb_print(struct gfs_quota_lvb *qb);
+
+#endif /* __LVB_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/main.c linux-2.6.9.debug/fs/gfs/main.c
--- linux-2.6.9.orig/fs/gfs/main.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/main.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,132 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "gfs.h"
+#include "diaper.h"
+#include "ops_fstype.h"
+#include "proc.h"
+
+/**
+ * init_gfs_fs - Register GFS as a filesystem
+ *
+ * Returns: 0 on success, error code on failure
+ */
+
+int __init
+init_gfs_fs(void)
+{
+	int error;
+
+	gfs_proc_init();
+
+	error = gfs_diaper_init();
+	if (error)
+		goto fail;
+
+	gfs_random_number = xtime.tv_nsec;
+
+	gfs_glock_cachep = kmem_cache_create("gfs_glock", sizeof(struct gfs_glock),
+					     0, 0,
+					     NULL, NULL);
+	gfs_inode_cachep = NULL;
+	gfs_bufdata_cachep = NULL;
+	gfs_mhc_cachep = NULL;
+	error = -ENOMEM;
+	if (!gfs_glock_cachep)
+		goto fail_diaper;
+
+	gfs_inode_cachep = kmem_cache_create("gfs_inode", sizeof(struct gfs_inode),
+					     0, 0,
+					     NULL, NULL);
+	if (!gfs_inode_cachep)
+		goto fail_diaper;
+
+	gfs_bufdata_cachep = kmem_cache_create("gfs_bufdata", sizeof(struct gfs_bufdata),
+					       0, 0,
+					       NULL, NULL);
+	if (!gfs_bufdata_cachep)
+		goto fail_diaper;
+
+	gfs_mhc_cachep = kmem_cache_create("gfs_meta_header_cache", sizeof(struct gfs_meta_header_cache),
+					   0, 0,
+					   NULL, NULL);
+	if (!gfs_mhc_cachep)
+		goto fail_diaper;
+
+	error = register_filesystem(&gfs_fs_type);
+	if (error)
+		goto fail_diaper;
+
+	printk("GFS %s (built %s %s) installed\n",
+	       GFS_RELEASE_NAME, __DATE__, __TIME__);
+
+	return 0;
+
+ fail_diaper:
+	if (gfs_mhc_cachep)
+		kmem_cache_destroy(gfs_mhc_cachep);
+
+	if (gfs_bufdata_cachep)
+		kmem_cache_destroy(gfs_bufdata_cachep);
+
+	if (gfs_inode_cachep)
+		kmem_cache_destroy(gfs_inode_cachep);
+
+	if (gfs_glock_cachep)
+		kmem_cache_destroy(gfs_glock_cachep);
+
+	gfs_diaper_uninit();
+
+ fail:
+	gfs_proc_uninit();
+
+	return error;
+}
+
+/**
+ * exit_gfs_fs - Unregister the file system
+ *
+ */
+
+void __exit
+exit_gfs_fs(void)
+{
+	unregister_filesystem(&gfs_fs_type);
+
+	kmem_cache_destroy(gfs_mhc_cachep);
+	kmem_cache_destroy(gfs_bufdata_cachep);
+	kmem_cache_destroy(gfs_inode_cachep);
+	kmem_cache_destroy(gfs_glock_cachep);
+
+	gfs_diaper_uninit();
+	gfs_proc_uninit();
+}
+
+MODULE_DESCRIPTION("Global File System " GFS_RELEASE_NAME);
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+module_init(init_gfs_fs);
+module_exit(exit_gfs_fs);
+
diff -pruN linux-2.6.9.orig/fs/gfs/mount.c linux-2.6.9.debug/fs/gfs/mount.c
--- linux-2.6.9.orig/fs/gfs/mount.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/mount.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,153 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "mount.h"
+#include "proc.h"
+
+/**
+ * gfs_make_args - Parse mount arguments
+ * @data:
+ * @args:
+ *
+ * Return: errno
+ */
+
+int
+gfs_make_args(char *data_arg, struct gfs_args *args)
+{
+	char *data = data_arg;
+	char *options, *x, *y;
+	int error = 0;
+
+	/*  If someone preloaded options, use those instead  */
+
+	spin_lock(&gfs_proc_margs_lock);
+	if (gfs_proc_margs) {
+		data = gfs_proc_margs;
+		gfs_proc_margs = NULL;
+	}
+	spin_unlock(&gfs_proc_margs_lock);
+
+	/*  Set some defaults  */
+
+	memset(args, 0, sizeof(struct gfs_args));
+	args->ar_num_glockd = GFS_GLOCKD_DEFAULT;
+
+	/*  Split the options into tokens with the "," character and
+	    process them  */
+
+	for (options = data; (x = strsep(&options, ",")); ) {
+		if (!*x)
+			continue;
+
+		y = strchr(x, '=');
+		if (y)
+			*y++ = 0;
+
+		if (!strcmp(x, "lockproto")) {
+			if (!y) {
+				printk("GFS: need argument to lockproto\n");
+				error = -EINVAL;
+				break;
+			}
+			strncpy(args->ar_lockproto, y, GFS_LOCKNAME_LEN);
+			args->ar_lockproto[GFS_LOCKNAME_LEN - 1] = 0;
+		}
+
+		else if (!strcmp(x, "locktable")) {
+			if (!y) {
+				printk("GFS: need argument to locktable\n");
+				error = -EINVAL;
+				break;
+			}
+			strncpy(args->ar_locktable, y, GFS_LOCKNAME_LEN);
+			args->ar_locktable[GFS_LOCKNAME_LEN - 1] = 0;
+		}
+
+		else if (!strcmp(x, "hostdata")) {
+			if (!y) {
+				printk("GFS: need argument to hostdata\n");
+				error = -EINVAL;
+				break;
+			}
+			strncpy(args->ar_hostdata, y, GFS_LOCKNAME_LEN);
+			args->ar_hostdata[GFS_LOCKNAME_LEN - 1] = 0;
+		}
+
+		else if (!strcmp(x, "ignore_local_fs"))
+			args->ar_ignore_local_fs = TRUE;
+
+		else if (!strcmp(x, "localflocks"))
+			args->ar_localflocks = TRUE;
+
+		else if (!strcmp(x, "localcaching"))
+			args->ar_localcaching = TRUE;
+
+		else if (!strcmp(x, "oopses_ok"))
+			args->ar_oopses_ok = TRUE;
+
+		else if (!strcmp(x, "debug")) {
+			args->ar_oopses_ok = TRUE;
+			args->ar_debug = TRUE;
+
+		} else if (!strcmp(x, "upgrade"))
+			args->ar_upgrade = TRUE;
+
+		else if (!strcmp(x, "num_glockd")) {
+			if (!y) {
+				printk("GFS: need argument to num_glockd\n");
+				error = -EINVAL;
+				break;
+			}
+			sscanf(y, "%u", &args->ar_num_glockd);
+			if (!args->ar_num_glockd || args->ar_num_glockd > GFS_GLOCKD_MAX) {
+				printk("GFS: 0 < num_glockd <= %u  (not %u)\n",
+				       GFS_GLOCKD_MAX, args->ar_num_glockd);
+				error = -EINVAL;
+				break;
+			}
+		}
+
+		else if (!strcmp(x, "acl"))
+			args->ar_posix_acls = TRUE;
+
+		else if (!strcmp(x, "suiddir"))
+			args->ar_suiddir = TRUE;
+
+		/*  Unknown  */
+
+		else {
+			printk("GFS: unknown option: %s\n", x);
+			error = -EINVAL;
+			break;
+		}
+	}
+
+	if (error)
+		printk("GFS: invalid mount option(s)\n");
+
+	if (data != data_arg)
+		kfree(data);
+
+	return error;
+}
+
diff -pruN linux-2.6.9.orig/fs/gfs/mount.h linux-2.6.9.debug/fs/gfs/mount.h
--- linux-2.6.9.orig/fs/gfs/mount.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/mount.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,19 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __MOUNT_DOT_H__
+#define __MOUNT_DOT_H__
+
+int gfs_make_args(char *data, struct gfs_args *args);
+
+#endif /* __MOUNT_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ondisk.c linux-2.6.9.debug/fs/gfs/ondisk.c
--- linux-2.6.9.orig/fs/gfs/ondisk.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ondisk.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,28 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+
+#define pv(struct, member, fmt) printk("  "#member" = "fmt"\n", struct->member);
+
+#define WANT_GFS_CONVERSION_FUNCTIONS
+#include <linux/gfs_ondisk.h>
+
diff -pruN linux-2.6.9.orig/fs/gfs/ops_address.c linux-2.6.9.debug/fs/gfs/ops_address.c
--- linux-2.6.9.orig/fs/gfs/ops_address.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_address.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,479 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+
+#include "gfs.h"
+#include "bmap.h"
+#include "dio.h"
+#include "file.h"
+#include "glock.h"
+#include "inode.h"
+#include "ops_address.h"
+#include "page.h"
+#include "quota.h"
+#include "trans.h"
+
+/**
+ * get_block - Fills in a buffer head with details about a block
+ * @inode: The inode
+ * @lblock: The block number to look up
+ * @bh_result: The buffer head to return the result in
+ * @create: Non-zero if we may add block to the file
+ *
+ * Returns: errno
+ */
+
+static int
+get_block(struct inode *inode, sector_t lblock, 
+	  struct buffer_head *bh_result, int create)
+{
+	struct gfs_inode *ip = vn2ip(inode);
+	int new = create;
+	uint64_t dblock;
+	int error;
+
+	error = gfs_block_map(ip, lblock, &new, &dblock, NULL);
+	if (error)
+		return error;
+
+	if (!dblock)
+		return 0;
+
+	map_bh(bh_result, inode->i_sb, dblock);
+	if (new)
+		set_buffer_new(bh_result);
+
+	return 0;
+}
+
+/**
+ * get_block_noalloc - Fills in a buffer head with details about a block
+ * @inode: The inode
+ * @lblock: The block number to look up
+ * @bh_result: The buffer head to return the result in
+ * @create: Non-zero if we may add block to the file
+ *
+ * Returns: errno
+ */
+
+static int
+get_block_noalloc(struct inode *inode, sector_t lblock,
+		  struct buffer_head *bh_result, int create)
+{
+	int error;
+
+	error = get_block(inode, lblock, bh_result, FALSE);
+	if (error)
+		return error;
+
+	if (gfs_assert_withdraw(vfs2sdp(inode->i_sb),
+				!create || buffer_mapped(bh_result)))
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * get_blocks - 
+ * @inode:
+ * @lblock:
+ * @max_blocks:
+ * @bh_result:
+ * @create:
+ *
+ * Returns: errno
+ */
+
+static int
+get_blocks(struct inode *inode, sector_t lblock,
+	   unsigned long max_blocks,
+	   struct buffer_head *bh_result, int create)
+{
+	struct gfs_inode *ip = vn2ip(inode);
+	int new = create;
+	uint64_t dblock;
+	uint32_t extlen;
+	int error;
+
+	error = gfs_block_map(ip, lblock, &new, &dblock, &extlen);
+	if (error)
+		return error;
+
+	if (!dblock)
+		return 0;
+
+	map_bh(bh_result, inode->i_sb, dblock);
+	if (new)
+		set_buffer_new(bh_result);
+
+	if (extlen > max_blocks)
+		extlen = max_blocks;
+	bh_result->b_size = extlen << inode->i_blkbits;
+
+	return 0;
+}
+
+/**
+ * get_blocks_noalloc - 
+ * @inode:
+ * @lblock:
+ * @max_blocks:
+ * @bh_result:
+ * @create:
+ *
+ * Returns: errno
+ */
+
+static int
+get_blocks_noalloc(struct inode *inode, sector_t lblock,
+		   unsigned long max_blocks,
+		   struct buffer_head *bh_result, int create)
+{
+	int error;
+
+	error = get_blocks(inode, lblock, max_blocks, bh_result, FALSE);
+	if (error)
+		return error;
+
+	if (gfs_assert_withdraw(vfs2sdp(inode->i_sb),
+				!create || buffer_mapped(bh_result)))
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * gfs_writepage - Write complete page
+ * @page: Page to write
+ *
+ * Returns: errno
+ *
+ * Use Linux VFS block_write_full_page() to write one page,
+ *   using GFS's get_block_noalloc to find which blocks to write.
+ */
+
+static int
+gfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	struct gfs_inode *ip = vn2ip(page->mapping->host);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_address);
+
+	if (gfs_assert_withdraw(sdp, gfs_glock_is_held_excl(ip->i_gl))) {
+		unlock_page(page);
+		return -EIO;
+	}
+	if (current_transaction) {
+		redirty_page_for_writepage(wbc, page);
+		unlock_page(page);
+		return 0;
+	}
+
+	error = block_write_full_page(page, get_block_noalloc, wbc);
+
+	gfs_flush_meta_cache(ip);
+
+	return error;
+}
+
+/**
+ * stuffed_readpage - Fill in a Linux page with stuffed file data
+ * @ip: the inode
+ * @page: the page
+ *
+ * Returns: errno
+ */
+
+static int
+stuffed_readpage(struct gfs_inode *ip, struct page *page)
+{
+	struct buffer_head *dibh;
+	void *kaddr;
+	int error;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (!error) {
+		kaddr = kmap(page);
+		memcpy((char *)kaddr,
+		       dibh->b_data + sizeof(struct gfs_dinode),
+		       ip->i_di.di_size);
+		memset((char *)kaddr + ip->i_di.di_size,
+		       0,
+		       PAGE_CACHE_SIZE - ip->i_di.di_size);
+		kunmap(page);
+
+		brelse(dibh);
+
+		SetPageUptodate(page);
+	}
+
+	return error;
+}
+
+/**
+ * readi_readpage - readpage that goes through gfs_internal_read()
+ * @page: The page to read
+ *
+ * Returns: errno
+ */
+
+static int
+readi_readpage(struct page *page)
+{
+	struct gfs_inode *ip = vn2ip(page->mapping->host);
+	void *kaddr;
+	int ret;
+
+	kaddr = kmap(page);
+
+	ret = gfs_internal_read(ip, kaddr,
+				(uint64_t)page->index << PAGE_CACHE_SHIFT,
+				PAGE_CACHE_SIZE);
+	if (ret >= 0) {
+		if (ret < PAGE_CACHE_SIZE)
+			memset(kaddr + ret, 0, PAGE_CACHE_SIZE - ret);
+		SetPageUptodate(page);
+		ret = 0;
+	}
+
+	kunmap(page);
+
+	unlock_page(page);
+
+	return ret;
+}
+
+/**
+ * gfs_readpage - readpage with locking
+ * @file: The file to read a page for
+ * @page: The page to read
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_readpage(struct file *file, struct page *page)
+{
+	struct gfs_inode *ip = vn2ip(page->mapping->host);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_address);
+
+	if (gfs_assert_warn(sdp, gfs_glock_is_locked_by_me(ip->i_gl))) {
+		unlock_page(page);
+		return -ENOSYS;
+	}
+
+	if (!gfs_is_jdata(ip)) {
+		if (gfs_is_stuffed(ip) && !page->index) {
+			error = stuffed_readpage(ip, page);
+			unlock_page(page);
+		} else
+			error = block_read_full_page(page, get_block);
+	} else
+		error = readi_readpage(page);
+
+	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+		error = -EIO;
+
+	return error;
+}
+
+/**
+ * gfs_prepare_write - Prepare to write a page to a file
+ * @file: The file to write to
+ * @page: The page which is to be prepared for writing
+ * @from: From (byte range within page)
+ * @to: To (byte range within page)
+ *
+ * Returns: errno
+ *
+ * Make sure file's inode is glocked; we shouldn't write without that!
+ * If GFS dinode is currently stuffed (small enough that all data fits within
+ *   the dinode block), and new file size is too large, unstuff it.
+ * Use Linux VFS block_prepare_write() to write blocks, using GFS' get_block()
+ *   to find which blocks to write.
+ */
+
+static int
+gfs_prepare_write(struct file *file, struct page *page,
+		  unsigned from, unsigned to)
+{
+	struct gfs_inode *ip = vn2ip(page->mapping->host);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	int error = 0;
+
+	atomic_inc(&sdp->sd_ops_address);
+
+	if (gfs_assert_warn(sdp, gfs_glock_is_locked_by_me(ip->i_gl)))
+		return -ENOSYS;
+
+	if (gfs_is_stuffed(ip)) {
+		uint64_t file_size = ((uint64_t)page->index << PAGE_CACHE_SHIFT) + to;
+
+		if (file_size > sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode)) {
+			error = gfs_unstuff_dinode(ip, gfs_unstuffer_page, page);
+			if (!error)
+				error = block_prepare_write(page, from, to, get_block);
+		} else if (!PageUptodate(page))
+			error = stuffed_readpage(ip, page);
+	} else
+		error = block_prepare_write(page, from, to, get_block);
+
+	return error;
+}
+
+/**
+ * gfs_commit_write - Commit write to a file
+ * @file: The file to write to
+ * @page: The page containing the data
+ * @from: From (byte range within page)
+ * @to: To (byte range within page)
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_commit_write(struct file *file, struct page *page,
+		 unsigned from, unsigned to)
+{
+	struct inode *inode = page->mapping->host;
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_address);
+
+	if (gfs_is_stuffed(ip)) {
+		struct buffer_head *dibh;
+		uint64_t file_size = ((uint64_t)page->index << PAGE_CACHE_SHIFT) + to;
+		void *kaddr;
+
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (error)
+			goto fail;
+
+		gfs_trans_add_bh(ip->i_gl, dibh);
+
+		kaddr = kmap(page);
+		memcpy(dibh->b_data + sizeof(struct gfs_dinode) + from,
+		       (char *)kaddr + from,
+		       to - from);
+		kunmap(page);
+
+		brelse(dibh);
+
+		SetPageUptodate(page);
+
+		if (inode->i_size < file_size)
+			i_size_write(inode, file_size);
+	} else {
+		error = generic_commit_write(file, page, from, to);
+		if (error)
+			goto fail;
+	}
+
+	return 0;
+
+ fail:
+	ClearPageUptodate(page);
+
+	return error;
+}
+
+/**
+ * gfs_bmap - Block map function
+ * @mapping: Address space info
+ * @lblock: The block to map
+ *
+ * Returns: The disk address for the block or 0 on hole or error
+ */
+
+static sector_t
+gfs_bmap(struct address_space *mapping, sector_t lblock)
+{
+	struct gfs_inode *ip = vn2ip(mapping->host);
+	struct gfs_holder i_gh;
+	int dblock = 0;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_address);
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		return 0;
+
+	if (!gfs_is_stuffed(ip))
+		dblock = generic_block_bmap(mapping, lblock, get_block);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return dblock;
+}
+
+/**
+ * gfs_direct_IO - 
+ * @rw:
+ * @iocb:
+ * @iov:
+ * @offset:
+ * @nr_segs:
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
+	      loff_t offset, unsigned long nr_segs)
+{
+	struct file *file = iocb->ki_filp;
+	struct inode *inode = file->f_mapping->host;
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	get_blocks_t *gb = get_blocks;
+
+	atomic_inc(&sdp->sd_ops_address);
+
+	if (gfs_assert_warn(sdp, gfs_glock_is_locked_by_me(ip->i_gl)) ||
+	    gfs_assert_warn(sdp, !gfs_is_stuffed(ip)))
+		return -EINVAL;
+
+	if (rw == WRITE && !current_transaction)
+		gb = get_blocks_noalloc;
+
+	return blockdev_direct_IO_cluster_locking(rw, iocb, inode,
+				  inode->i_sb->s_bdev, iov,
+				  offset, nr_segs, gb, NULL);
+}
+
+struct address_space_operations gfs_file_aops = {
+	.writepage = gfs_writepage,
+	.readpage = gfs_readpage,
+	.sync_page = block_sync_page,
+	.prepare_write = gfs_prepare_write,
+	.commit_write = gfs_commit_write,
+	.bmap = gfs_bmap,
+	.direct_IO = gfs_direct_IO,
+};
diff -pruN linux-2.6.9.orig/fs/gfs/ops_address.h linux-2.6.9.debug/fs/gfs/ops_address.h
--- linux-2.6.9.orig/fs/gfs/ops_address.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_address.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,19 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __OPS_ADDRESS_DOT_H__
+#define __OPS_ADDRESS_DOT_H__
+
+extern struct address_space_operations gfs_file_aops;
+
+#endif /* __OPS_ADDRESS_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ops_dentry.c linux-2.6.9.debug/fs/gfs/ops_dentry.c
--- linux-2.6.9.orig/fs/gfs/ops_dentry.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_dentry.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,124 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dir.h"
+#include "glock.h"
+#include "ops_dentry.h"
+
+/**
+ * gfs_drevalidate - Check directory lookup consistency
+ * @dentry: the mapping to check
+ * @nd:
+ *
+ * Check to make sure the lookup necessary to arrive at this inode from its
+ * parent is still good.
+ *
+ * Returns: 1 if the dentry is ok, 0 if it isn't
+ */
+
+static int
+gfs_drevalidate(struct dentry *dentry, struct nameidata *nd)
+{
+	struct dentry *parent = dget_parent(dentry);
+	struct gfs_inode *dip = vn2ip(parent->d_inode);
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct inode *inode;
+	struct gfs_holder d_gh;
+	struct gfs_inode *ip;
+	struct gfs_inum inum;
+	unsigned int type;
+	int error;
+
+	lock_kernel();
+
+	atomic_inc(&sdp->sd_ops_dentry);
+
+	if (sdp->sd_args.ar_localcaching)
+		goto valid;
+
+	inode = dentry->d_inode;
+	if (inode && is_bad_inode(inode))
+		goto invalid;
+
+	error = gfs_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
+	if (error)
+		goto fail;
+
+	error = gfs_dir_search(dip, &dentry->d_name, &inum, &type);
+	switch (error) {
+	case 0:
+		if (!inode)
+			goto invalid_gunlock;
+		break;
+	case -ENOENT:
+		if (!inode)
+			goto valid_gunlock;
+		goto invalid_gunlock;
+	default:
+		goto fail_gunlock;
+	}
+
+	ip = vn2ip(inode);
+
+	if (ip->i_num.no_formal_ino != inum.no_formal_ino)
+		goto invalid_gunlock;
+
+	if (ip->i_di.di_type != type) {
+		gfs_consist_inode(dip);
+		goto fail_gunlock;
+	}
+
+ valid_gunlock:
+	gfs_glock_dq_uninit(&d_gh);
+
+ valid:
+	unlock_kernel();
+	dput(parent);
+	return 1;
+
+ invalid_gunlock:
+	gfs_glock_dq_uninit(&d_gh);
+
+ invalid:
+	if (inode && S_ISDIR(inode->i_mode)) {
+		if (have_submounts(dentry))
+			goto valid;
+		shrink_dcache_parent(dentry);
+	}
+	d_drop(dentry);
+
+	unlock_kernel();
+	dput(parent);
+	return 0;
+
+ fail_gunlock:
+	gfs_glock_dq_uninit(&d_gh);
+
+ fail:
+	unlock_kernel();
+	dput(parent);
+	return 0;
+}
+
+struct dentry_operations gfs_dops = {
+	.d_revalidate = gfs_drevalidate,
+};
diff -pruN linux-2.6.9.orig/fs/gfs/ops_dentry.h linux-2.6.9.debug/fs/gfs/ops_dentry.h
--- linux-2.6.9.orig/fs/gfs/ops_dentry.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_dentry.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,19 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __OPS_DENTRY_DOT_H__
+#define __OPS_DENTRY_DOT_H__
+
+extern struct dentry_operations gfs_dops;
+
+#endif /* __OPS_DENTRY_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ops_export.c linux-2.6.9.debug/fs/gfs/ops_export.c
--- linux-2.6.9.orig/fs/gfs/ops_export.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_export.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,416 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "dir.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "ops_export.h"
+#include "rgrp.h"
+
+struct inode_cookie
+{
+	uint64_t formal_ino;
+	uint32_t gen;
+	int gen_valid;
+};
+
+struct get_name_filldir
+{
+	uint64_t formal_ino;
+	char *name;
+};
+
+/**
+ * gfs_decode_fh -
+ * @param1: description
+ * @param2: description
+ * @param3: description
+ *
+ * Function description
+ *
+ * Returns: what is returned
+ */
+
+struct dentry *
+gfs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
+	      int (*acceptable)(void *context, struct dentry *dentry),
+	      void *context)
+{
+	struct inode_cookie this, parent;
+
+	atomic_inc(&vfs2sdp(sb)->sd_ops_export);
+
+	if (fh_type != fh_len)
+		return NULL;
+
+	memset(&parent, 0, sizeof(struct inode_cookie));
+
+	switch (fh_type) {
+	case 6:
+		parent.gen_valid = TRUE;
+		parent.gen = gfs32_to_cpu(fh[5]);
+	case 5:
+		parent.formal_ino = ((uint64_t)gfs32_to_cpu(fh[3])) << 32;
+		parent.formal_ino |= (uint64_t)gfs32_to_cpu(fh[4]);
+	case 3:
+		this.gen_valid = TRUE;
+		this.gen = gfs32_to_cpu(fh[2]);
+		this.formal_ino = ((uint64_t)gfs32_to_cpu(fh[0])) << 32;
+		this.formal_ino |= (uint64_t)gfs32_to_cpu(fh[1]);
+		break;
+	default:
+		return NULL;
+	}
+
+	return gfs_export_ops.find_exported_dentry(sb, &this, &parent,
+						   acceptable, context);
+}
+
+/**
+ * gfs_encode_fh -
+ * @param1: description
+ * @param2: description
+ * @param3: description
+ *
+ * Function description
+ *
+ * Returns: what is returned
+ */
+
+int 
+gfs_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
+	      int connectable)
+{
+	struct inode *inode = dentry->d_inode;
+	struct gfs_inode *ip = vn2ip(inode);
+	int maxlen = *len;
+
+	atomic_inc(&ip->i_sbd->sd_ops_export);
+
+	if (maxlen < 3)
+		return 255;
+
+	fh[0] = cpu_to_gfs32((uint32_t)(ip->i_num.no_formal_ino >> 32));
+	fh[1] = cpu_to_gfs32((uint32_t)(ip->i_num.no_formal_ino & 0xFFFFFFFF));
+	fh[2] = cpu_to_gfs32(inode->i_generation);  /* dinode's mh_incarn */
+	*len = 3;
+
+	if (maxlen < 5 || !connectable)
+		return 3;
+
+	spin_lock(&dentry->d_lock);
+
+	inode = dentry->d_parent->d_inode;
+	ip = vn2ip(inode);
+
+	fh[3] = cpu_to_gfs32((uint32_t)(ip->i_num.no_formal_ino >> 32));
+	fh[4] = cpu_to_gfs32((uint32_t)(ip->i_num.no_formal_ino & 0xFFFFFFFF));
+	*len = 5;
+
+	if (maxlen < 6) {
+		spin_unlock(&dentry->d_lock);
+		return 5;
+	}
+
+	fh[5] = cpu_to_gfs32(inode->i_generation);  /* dinode's mh_incarn */
+
+	spin_unlock(&dentry->d_lock);
+
+	*len = 6;
+
+	return 6;
+}
+
+/**
+ * get_name_filldir - 
+ * @param1: description
+ * @param2: description
+ * @param3: description
+ *
+ * Function description
+ *
+ * Returns: what is returned
+ */
+
+static int
+get_name_filldir(void *opaque,
+		 const char *name, unsigned int length,
+		 uint64_t offset,
+		 struct gfs_inum *inum, unsigned int type)
+{
+	struct get_name_filldir *gnfd = (struct get_name_filldir *)opaque;
+
+	if (inum->no_formal_ino != gnfd->formal_ino)
+		return 0;
+
+	memcpy(gnfd->name, name, length);
+	gnfd->name[length] = 0;
+
+	return 1;
+}
+
+/**
+ * gfs_get_name -
+ * @param1: description
+ * @param2: description
+ * @param3: description
+ *
+ * Function description
+ *
+ * Returns: what is returned
+ */
+
+int gfs_get_name(struct dentry *parent, char *name,
+		 struct dentry *child)
+{
+	struct inode *dir = parent->d_inode;
+	struct inode *inode = child->d_inode;
+	struct gfs_inode *dip, *ip;
+	struct get_name_filldir gnfd;
+	struct gfs_holder gh;
+	uint64_t offset = 0;
+	int error;
+
+	if (!dir)
+		return -EINVAL;
+
+	atomic_inc(&vfs2sdp(dir->i_sb)->sd_ops_export);
+
+	if (!S_ISDIR(dir->i_mode) || !inode)
+		return -EINVAL;
+
+	dip = vn2ip(dir);
+	ip = vn2ip(inode);
+
+	*name = 0;
+	gnfd.formal_ino = ip->i_num.no_formal_ino;
+	gnfd.name = name;
+
+	error = gfs_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &gh);
+	if (error)
+		return error;
+
+	error = gfs_dir_read(dip, &offset, &gnfd, get_name_filldir);
+
+	gfs_glock_dq_uninit(&gh);
+
+	if (!error & !*name)
+		error = -ENOENT;
+
+	return error;
+}
+
+/**
+ * gfs_get_parent -
+ * @param1: description
+ * @param2: description
+ * @param3: description
+ *
+ * Function description
+ *
+ * Returns: what is returned
+ */
+
+struct dentry *
+gfs_get_parent(struct dentry *child)
+{
+	struct gfs_inode *dip = vn2ip(child->d_inode);
+	struct gfs_holder d_gh, i_gh;
+	struct qstr dotdot = { .name = "..", .len = 2 };
+	struct gfs_inode *ip;
+	struct inode *inode;
+	struct dentry *dentry;
+	int error;
+
+	atomic_inc(&dip->i_sbd->sd_ops_export);
+
+	gfs_holder_init(dip->i_gl, 0, 0, &d_gh);
+	error = gfs_lookupi(&d_gh, &dotdot, TRUE, &i_gh);
+	if (error)
+		goto fail;
+
+	error = -ENOENT;
+	if (!i_gh.gh_gl)
+		goto fail;
+
+	ip = gl2ip(i_gh.gh_gl);
+
+	gfs_glock_dq_uninit(&d_gh);
+	gfs_glock_dq_uninit(&i_gh);
+
+	inode = gfs_iget(ip, CREATE);
+	gfs_inode_put(ip);
+
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	dentry = d_alloc_anon(inode);
+	if (!dentry) {
+		iput(inode);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return dentry;
+
+ fail:
+	gfs_holder_uninit(&d_gh);
+	return ERR_PTR(error);
+}
+
+/**
+ * gfs_get_dentry -
+ * @param1: description
+ * @param2: description
+ * @param3: description
+ *
+ * Function description
+ *
+ * Returns: what is returned
+ */
+
+struct dentry *
+gfs_get_dentry(struct super_block *sb, void *inump)
+{
+	struct gfs_sbd *sdp = vfs2sdp(sb);
+	struct inode_cookie *cookie = (struct inode_cookie *)inump;
+	struct gfs_inum inum;
+	struct gfs_holder i_gh, ri_gh, rgd_gh;
+	struct gfs_rgrpd *rgd;
+	struct buffer_head *bh;
+	struct gfs_dinode *di;
+	struct gfs_inode *ip;
+	struct inode *inode;
+	struct dentry *dentry;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_export);
+
+	if (!cookie->formal_ino ||
+	    cookie->formal_ino == sdp->sd_jiinode->i_num.no_formal_ino ||
+	    cookie->formal_ino == sdp->sd_riinode->i_num.no_formal_ino ||
+	    cookie->formal_ino == sdp->sd_qinode->i_num.no_formal_ino ||
+	    cookie->formal_ino == sdp->sd_linode->i_num.no_formal_ino)
+		return ERR_PTR(-EINVAL);
+
+	inum.no_formal_ino = cookie->formal_ino;
+	inum.no_addr = cookie->formal_ino;
+
+	error = gfs_glock_nq_num(sdp,
+				 inum.no_formal_ino, &gfs_inode_glops,
+				 LM_ST_SHARED, LM_FLAG_ANY | GL_LOCAL_EXCL,
+				 &i_gh);
+	if (error)
+		return ERR_PTR(error);
+
+	error = gfs_inode_get(i_gh.gh_gl, &inum, NO_CREATE, &ip);
+	if (error)
+		goto fail;
+	if (ip)
+		goto out;
+
+	error = gfs_rindex_hold(sdp, &ri_gh);
+	if (error)
+		goto fail;
+
+	error = -EINVAL;
+	rgd = gfs_blk2rgrpd(sdp, inum.no_addr);
+	if (!rgd)
+		goto fail_rindex;
+
+	error = gfs_glock_nq_init(rgd->rd_gl, LM_ST_SHARED, 0, &rgd_gh);
+	if (error)
+		goto fail_rindex;
+
+	error = -ESTALE;
+	if (gfs_get_block_type(rgd, inum.no_addr) != GFS_BLKST_USEDMETA)
+		goto fail_rgd;
+
+	error = gfs_dread(i_gh.gh_gl, inum.no_addr,
+			  DIO_START | DIO_WAIT, &bh);
+	if (error)
+		goto fail_rgd;
+
+	di = (struct gfs_dinode *)bh->b_data;
+
+	error = -ESTALE;
+	if (gfs32_to_cpu(di->di_header.mh_magic) != GFS_MAGIC ||
+	    gfs32_to_cpu(di->di_header.mh_type) != GFS_METATYPE_DI ||
+	    (gfs32_to_cpu(di->di_flags) & GFS_DIF_UNUSED))
+		goto fail_relse;
+
+	brelse(bh);
+	gfs_glock_dq_uninit(&rgd_gh);
+	gfs_glock_dq_uninit(&ri_gh);
+
+	error = gfs_inode_get(i_gh.gh_gl, &inum, CREATE, &ip);
+	if (error)
+		goto fail;
+
+	atomic_inc(&sdp->sd_fh2dentry_misses);
+
+ out:
+	gfs_glock_dq_uninit(&i_gh);
+
+	inode = gfs_iget(ip, CREATE);
+	gfs_inode_put(ip);
+
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	/* inode->i_generation is GFS dinode's mh_incarn value */
+	if (cookie->gen_valid && cookie->gen != inode->i_generation) {
+		iput(inode);
+		return ERR_PTR(-ESTALE);
+	}
+
+	dentry = d_alloc_anon(inode);
+	if (!dentry) {
+		iput(inode);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return dentry;
+
+ fail_relse:
+        brelse(bh);
+
+ fail_rgd:
+	gfs_glock_dq_uninit(&rgd_gh);
+
+ fail_rindex:
+	gfs_glock_dq_uninit(&ri_gh);
+
+ fail:
+	gfs_glock_dq_uninit(&i_gh);
+	return ERR_PTR(error);
+}
+
+struct export_operations gfs_export_ops = {
+	.decode_fh = gfs_decode_fh,
+	.encode_fh = gfs_encode_fh,
+	.get_name = gfs_get_name,
+	.get_parent = gfs_get_parent,
+	.get_dentry = gfs_get_dentry,
+};
+
diff -pruN linux-2.6.9.orig/fs/gfs/ops_export.h linux-2.6.9.debug/fs/gfs/ops_export.h
--- linux-2.6.9.orig/fs/gfs/ops_export.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_export.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,19 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __OPS_EXPORT_DOT_H__
+#define __OPS_EXPORT_DOT_H__
+
+extern struct export_operations gfs_export_ops;
+
+#endif /* __OPS_EXPORT_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ops_file.c linux-2.6.9.debug/fs/gfs/ops_file.c
--- linux-2.6.9.orig/fs/gfs/ops_file.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_file.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1763 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/uaccess.h>
+#include <linux/pagemap.h>
+#include <linux/uio.h>
+#include <linux/blkdev.h>
+#include <linux/mm.h>
+#include <linux/aio.h>
+#include <asm/uaccess.h>
+#include <linux/gfs_ioctl.h>
+#include <linux/writeback.h>
+
+#include "gfs.h"
+#include "bmap.h"
+#include "dio.h"
+#include "dir.h"
+#include "file.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "ioctl.h"
+#include "lm.h"
+#include "log.h"
+#include "ops_file.h"
+#include "ops_vm.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "trans.h"
+
+/* "bad" is for NFS support */
+struct filldir_bad_entry {
+	char *fbe_name;
+	unsigned int fbe_length;
+	uint64_t fbe_offset;
+	struct gfs_inum fbe_inum;
+	unsigned int fbe_type;
+};
+
+struct filldir_bad {
+	struct gfs_sbd *fdb_sbd;
+
+	struct filldir_bad_entry *fdb_entry;
+	unsigned int fdb_entry_num;
+	unsigned int fdb_entry_off;
+
+	char *fdb_name;
+	unsigned int fdb_name_size;
+	unsigned int fdb_name_off;
+};
+
+/* For regular, non-NFS */
+struct filldir_reg {
+	struct gfs_sbd *fdr_sbd;
+	int fdr_prefetch;
+
+	filldir_t fdr_filldir;
+	void *fdr_opaque;
+};
+
+typedef ssize_t(*do_rw_t) (struct file * file,
+			   char *buf,
+			   size_t size, loff_t * offset,
+                           struct kiocb *iocb,
+			   unsigned int num_gh, struct gfs_holder * ghs);
+
+/**
+ * gfs_llseek - seek to a location in a file
+ * @file: the file
+ * @offset: the offset
+ * @origin: Where to seek from (SEEK_SET, SEEK_CUR, or SEEK_END)
+ *
+ * SEEK_END requires the glock for the file because it references the
+ * file's size.
+ *
+ * Returns: The new offset, or errno
+ */
+
+static loff_t
+gfs_llseek(struct file *file, loff_t offset, int origin)
+{
+	struct gfs_inode *ip = vn2ip(file->f_mapping->host);
+	struct gfs_holder i_gh;
+	loff_t error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_file);
+
+	if (origin == 2) {
+		error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+		if (!error) {
+			error = remote_llseek(file, offset, origin);
+			gfs_glock_dq_uninit(&i_gh);
+		}
+	} else
+		error = remote_llseek(file, offset, origin);
+
+	return error;
+}
+
+#define vma2state(vma) \
+((((vma)->vm_flags & (VM_MAYWRITE | VM_MAYSHARE)) == \
+ (VM_MAYWRITE | VM_MAYSHARE)) ? \
+ LM_ST_EXCLUSIVE : LM_ST_SHARED) \
+
+/**
+ * functionname - summary
+ * @param1: description
+ * @param2: description
+ * @param3: description
+ *
+ * Function description
+ *
+ * Returns: what is returned
+ */
+
+static ssize_t
+walk_vm_hard(struct file *file, char *buf, size_t size, loff_t *offset,
+             struct kiocb *iocb, do_rw_t operation)
+{
+	struct gfs_holder *ghs;
+	unsigned int num_gh = 0;
+	ssize_t count;
+
+	{
+		struct super_block *sb = file->f_dentry->d_inode->i_sb;
+		struct mm_struct *mm = current->mm;
+		struct vm_area_struct *vma;
+		unsigned long start = (unsigned long)buf;
+		unsigned long end = start + size;
+		int dumping = (current->flags & PF_DUMPCORE);
+		unsigned int x = 0;
+
+		for (vma = find_vma(mm, start); vma; vma = vma->vm_next) {
+			if (end <= vma->vm_start)
+				break;
+			if (vma->vm_file &&
+			    vma->vm_file->f_dentry->d_inode->i_sb == sb) {
+				num_gh++;
+			}
+		}
+
+		ghs = kmalloc((num_gh + 1) * sizeof(struct gfs_holder), GFP_KERNEL);
+		if (!ghs) {
+			if (!dumping)
+				up_read(&mm->mmap_sem);
+			return -ENOMEM;
+		}
+
+		for (vma = find_vma(mm, start); vma; vma = vma->vm_next) {
+			if (end <= vma->vm_start)
+				break;
+			if (vma->vm_file) {
+				struct inode *inode = vma->vm_file->f_dentry->d_inode;
+				if (inode->i_sb == sb)
+					gfs_holder_init(vn2ip(inode)->i_gl,
+							vma2state(vma),
+							0, &ghs[x++]);
+			}
+		}
+
+		if (!dumping)
+			up_read(&mm->mmap_sem);
+
+		gfs_assert(vfs2sdp(sb), x == num_gh,);
+	}
+
+	count = operation(file, buf, size, offset, iocb, num_gh, ghs);
+
+	while (num_gh--)
+		gfs_holder_uninit(&ghs[num_gh]);
+	kfree(ghs);
+
+	return count;
+}
+
+/**
+ * walk_vm - Walk the vmas associated with a buffer for read or write.
+ *    If any of them are gfs, pass the gfs inode down to the read/write
+ *    worker function so that locks can be acquired in the correct order.
+ * @file: The file to read/write from/to
+ * @buf: The buffer to copy to/from
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ * @operation: The read or write worker function
+ *
+ * Outputs: Offset - updated according to number of bytes written
+ *
+ * Returns: The number of bytes written, errno on failure
+ */
+
+static ssize_t
+walk_vm(struct file *file, char *buf, size_t size, loff_t *offset,
+	struct kiocb *iocb,	
+	do_rw_t operation)
+{
+	if (current->mm) {
+		struct super_block *sb = file->f_dentry->d_inode->i_sb;
+		struct mm_struct *mm = current->mm;
+		struct vm_area_struct *vma;
+		unsigned long start = (unsigned long)buf;
+		unsigned long end = start + size;
+		int dumping = (current->flags & PF_DUMPCORE);
+
+		if (!dumping)
+			down_read(&mm->mmap_sem);
+
+		for (vma = find_vma(mm, start); vma; vma = vma->vm_next) {
+			if (end <= vma->vm_start)
+				break;
+			if (vma->vm_file &&
+			    vma->vm_file->f_dentry->d_inode->i_sb == sb)
+				goto do_locks;
+		}
+
+		if (!dumping)
+			up_read(&mm->mmap_sem);
+	}
+
+	{
+		struct gfs_holder gh;
+		return operation(file, buf, size, offset, iocb, 0, &gh);
+	}
+
+ do_locks:
+	return walk_vm_hard(file, buf, size, offset, iocb, operation);
+}
+
+/**
+ * functionname - summary
+ * @param1: description
+ * @param2: description
+ * @param3: description
+ *
+ * Function description
+ *
+ * Returns: what is returned
+ */
+
+static ssize_t
+do_read_readi(struct file *file, char *buf, size_t size, loff_t *offset,
+              struct kiocb *iocb)
+{
+	struct gfs_inode *ip = vn2ip(file->f_mapping->host);
+	ssize_t count = 0;
+
+	if (*offset < 0)
+		return -EINVAL;
+	if (!access_ok(VERIFY_WRITE, buf, size))
+		return -EFAULT;
+
+	if (!(file->f_flags & O_LARGEFILE)) {
+		if (*offset >= 0x7FFFFFFFull)
+			return -EFBIG;
+		if (*offset + size > 0x7FFFFFFFull)
+			size = 0x7FFFFFFFull - *offset;
+	}
+
+	/* ToDo: not sure about iocb .. wcheng
+	 */
+	count = gfs_readi(ip, buf, *offset, size, gfs_copy2user);
+
+	if (count > 0)
+		*offset += count;
+
+	return count;
+}
+
+/**
+ * do_read_direct - Read bytes from a file
+ * @file: The file to read from
+ * @buf: The buffer to copy into
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ * @num_gh: The number of other locks we need to do the read
+ * @ghs: the locks we need plus one for our lock
+ *
+ * Outputs: Offset - updated according to number of bytes read
+ *
+ * Returns: The number of bytes read, errno on failure
+ */
+
+static ssize_t
+do_read_direct(struct file *file, char *buf, size_t size, loff_t *offset,
+		struct kiocb *iocb,
+		unsigned int num_gh, struct gfs_holder *ghs)
+{
+	struct inode *inode = file->f_mapping->host;
+	struct gfs_inode *ip = vn2ip(inode);
+	unsigned int state = LM_ST_DEFERRED;
+	int flags = 0;
+	unsigned int x;
+	ssize_t count = 0;
+	int error;
+
+	for (x = 0; x < num_gh; x++)
+		if (ghs[x].gh_gl == ip->i_gl) {
+			state = LM_ST_SHARED;
+			flags |= GL_LOCAL_EXCL;
+			break;
+		}
+
+	down_read(&inode->i_alloc_sem);
+
+	gfs_holder_init(ip->i_gl, state, flags, &ghs[num_gh]);
+
+	error = gfs_glock_nq_m(num_gh + 1, ghs);
+	if (error)
+		goto out;
+
+	error = -EINVAL;
+	if (gfs_is_jdata(ip))
+		goto out_gunlock;
+
+	if (gfs_is_stuffed(ip)) {
+		size_t mask = bdev_hardsect_size(inode->i_sb->s_bdev) - 1;
+
+		if (((*offset) & mask) || (((unsigned long)buf) & mask))
+			goto out_gunlock;
+
+		count = do_read_readi(file, buf, size & ~mask, offset, iocb);
+	}
+	else {
+		if (!iocb) 
+			count = generic_file_read(file, buf, size, offset);
+		else {
+		        struct iovec local_iov = { .iov_base = buf, .iov_len = size};
+			count = __generic_file_aio_read(iocb, &local_iov, 1, offset);
+		}
+	}
+
+	error = 0;
+
+ out_gunlock:
+	gfs_glock_dq_m(num_gh + 1, ghs);
+
+ out:
+	up_read(&inode->i_alloc_sem);
+	gfs_holder_uninit(&ghs[num_gh]);
+
+	return (count) ? count : error;
+}
+
+/**
+ * do_read_buf - Read bytes from a file
+ * @file: The file to read from
+ * @buf: The buffer to copy into
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ * @num_gh: The number of other locks we need to do the read
+ * @ghs: the locks we need plus one for our lock
+ *
+ * Outputs: Offset - updated according to number of bytes read
+ *
+ * Returns: The number of bytes read, errno on failure
+ */
+
+static ssize_t
+do_read_buf(struct file *file, char *buf, size_t size, loff_t *offset,
+		struct kiocb *iocb,
+		unsigned int num_gh, struct gfs_holder *ghs)
+{
+	struct gfs_inode *ip = vn2ip(file->f_mapping->host);
+	ssize_t count = 0;
+	int error;
+
+	gfs_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &ghs[num_gh]);
+
+	error = gfs_glock_nq_m_atime(num_gh + 1, ghs);
+	if (error)
+		goto out;
+
+	if (gfs_is_jdata(ip) ||
+	    (gfs_is_stuffed(ip) && !test_bit(GIF_PAGED, &ip->i_flags)))
+		count = do_read_readi(file, buf, size, offset, iocb);
+	else {
+		if (!iocb) {
+			count = generic_file_read(file, buf, size, offset);
+		} else {
+			struct iovec local_iov = {
+				.iov_base = (char __user *)buf,
+				.iov_len = size 
+			};
+
+		        count = __generic_file_aio_read(iocb, 
+					&local_iov, 1, offset);
+			if (count == -EIOCBQUEUED)
+				count = wait_on_sync_kiocb(iocb);
+		}
+	}
+
+	gfs_glock_dq_m(num_gh + 1, ghs);
+
+ out:
+	gfs_holder_uninit(&ghs[num_gh]);
+
+	return (count) ? count : error;
+}
+
+static ssize_t
+__gfs_read(struct file *file, char *buf, size_t size, loff_t *offset, struct kiocb *iocb)
+{
+	atomic_inc(&vfs2sdp(file->f_mapping->host->i_sb)->sd_ops_file);
+
+	if (file->f_flags & O_DIRECT)
+		return walk_vm(file, buf, size, offset, iocb, do_read_direct);
+	else
+		return walk_vm(file, buf, size, offset, iocb, do_read_buf);
+}
+
+/**
+ * gfs_read - Read bytes from a file
+ * @file: The file to read from
+ * @buf: The buffer to copy into
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ *
+ * Outputs: Offset - updated according to number of bytes read
+ *
+ * Returns: The number of bytes read, errno on failure
+ */
+
+static ssize_t
+gfs_read(struct file *file, char *buf, size_t size, loff_t *offset)
+{
+	return(__gfs_read(file, buf, size, offset, NULL));
+}
+
+/*
+ * generic_file_aio_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t pos)
+ */
+static ssize_t
+gfs_aio_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t pos)
+{
+        struct file *filp = iocb->ki_filp;
+
+        BUG_ON(iocb->ki_pos != pos);
+        return(__gfs_read(filp, buf, count, &iocb->ki_pos, iocb));
+}
+
+/**
+ * grope_mapping - feel up a mapping that needs to be written
+ * @buf: the start of the memory to be written
+ * @size: the size of the memory to be written
+ *
+ * We do this after acquiring the locks on the mapping,
+ * but before starting the write transaction.  We need to make
+ * sure that we don't cause recursive transactions if blocks
+ * need to be allocated to the file backing the mapping.
+ *
+ * Returns: errno
+ */
+
+static int
+grope_mapping(char *buf, size_t size)
+{
+	unsigned long start = (unsigned long)buf;
+	unsigned long stop = start + size;
+	char c;
+
+	while (start < stop) {
+		if (copy_from_user(&c, (char *)start, 1))
+			return -EFAULT;
+
+		start += PAGE_CACHE_SIZE;
+		start &= PAGE_CACHE_MASK;
+	}
+
+	return 0;
+}
+
+/**
+ * do_write_direct_alloc - Write bytes to a file
+ * @file: The file to write to
+ * @buf: The buffer to copy from
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ *
+ * Outputs: Offset - updated according to number of bytes written
+ *
+ * Returns: The number of bytes written, errno on failure
+ */
+
+static ssize_t
+do_write_direct_alloc(struct file *file, char *buf, size_t size, loff_t *offset,
+			struct kiocb *iocb)
+{
+	struct inode *inode = file->f_mapping->host;
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = NULL;
+	struct iovec local_iov = { .iov_base = buf, .iov_len = size };
+	struct buffer_head *dibh;
+	unsigned int data_blocks, ind_blocks;
+	ssize_t count;
+	int error;
+
+	gfs_write_calc_reserv(ip, size, &data_blocks, &ind_blocks);
+
+	al = gfs_alloc_get(ip);
+
+	error = gfs_quota_lock_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto fail;
+
+	error = gfs_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid);
+	if (error)
+		goto fail_gunlock_q;
+
+	al->al_requested_meta = ind_blocks;
+	al->al_requested_data = data_blocks;
+
+	error = gfs_inplace_reserve(ip);
+	if (error)
+		goto fail_gunlock_q;
+
+	/* Trans may require:
+	   All blocks for a RG bitmap, whatever indirect blocks we
+	   need, a modified dinode, and a quota change. */
+
+	error = gfs_trans_begin(sdp,
+				1 + al->al_rgd->rd_ri.ri_length + ind_blocks,
+				1);
+	if (error)
+		goto fail_ipres;
+
+	if ((ip->i_di.di_mode & (S_ISUID | S_ISGID)) && !capable(CAP_FSETID)) {
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (error)
+			goto fail_end_trans;
+
+		ip->i_di.di_mode &= (ip->i_di.di_mode & S_IXGRP) ? (~(S_ISUID | S_ISGID)) : (~S_ISUID);
+
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	if (gfs_is_stuffed(ip)) {
+		error = gfs_unstuff_dinode(ip, gfs_unstuffer_sync, NULL);
+		if (error)
+			goto fail_end_trans;
+	}
+
+	if (!iocb)
+		count = generic_file_write_nolock(file, &local_iov, 1, offset);
+	else
+		count = generic_file_aio_write_nolock(iocb, &local_iov, 1, offset);
+
+	if (count < 0) {
+		error = count;
+		goto fail_end_trans;
+	}
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+	if (error)
+		goto fail_end_trans;
+
+	if (ip->i_di.di_size < inode->i_size)
+		ip->i_di.di_size = inode->i_size;
+	ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
+
+	gfs_trans_add_bh(ip->i_gl, dibh);
+	gfs_dinode_out(&ip->i_di, dibh->b_data);
+	brelse(dibh);
+
+	gfs_trans_end(sdp);
+
+	/* Question (wcheng)
+	 * 1. should IS_SYNC flush glock ?
+	 * 2. does gfs_log_flush_glock flush data ?
+	 */
+	if (file->f_flags & O_SYNC)
+		gfs_log_flush_glock(ip->i_gl);
+
+	gfs_inplace_release(ip);
+	gfs_quota_unlock_m(ip);
+	gfs_alloc_put(ip);
+
+	if (file->f_mapping->nrpages) {
+		error = filemap_fdatawrite(file->f_mapping);
+		if (!error)
+			error = filemap_fdatawait(file->f_mapping);
+	}
+	if (error)
+		return error;
+
+	return count;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_ipres:
+	gfs_inplace_release(ip);
+
+ fail_gunlock_q:
+	gfs_quota_unlock_m(ip);
+
+ fail:
+	gfs_alloc_put(ip);
+
+	return error;
+}
+
+/**
+ * do_write_direct - Write bytes to a file
+ * @file: The file to write to
+ * @buf: The buffer to copy from
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ * @num_gh: The number of other locks we need to do the read
+ * @gh: the locks we need plus one for our lock
+ *
+ * Outputs: Offset - updated according to number of bytes written
+ *
+ * Returns: The number of bytes written, errno on failure
+ */
+
+static ssize_t
+do_write_direct(struct file *file, char *buf, size_t size, loff_t *offset,
+		struct kiocb *iocb,
+		unsigned int num_gh, struct gfs_holder *ghs)
+{
+	struct inode *inode = file->f_mapping->host;
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_file *fp = vf2fp(file);
+	unsigned int state = LM_ST_DEFERRED;
+	int alloc_required;
+	unsigned int x;
+	size_t s;
+	ssize_t count = 0;
+	int error;
+
+	if (test_bit(GFF_DID_DIRECT_ALLOC, &fp->f_flags))
+		state = LM_ST_EXCLUSIVE;
+	else
+		for (x = 0; x < num_gh; x++)
+			if (ghs[x].gh_gl == ip->i_gl) {
+				state = LM_ST_EXCLUSIVE;
+				break;
+			}
+
+	down_write(&inode->i_alloc_sem);
+
+ restart:
+
+	gfs_holder_init(ip->i_gl, state, 0, &ghs[num_gh]);
+
+	error = gfs_glock_nq_m(num_gh + 1, ghs);
+	if (error)
+		goto out;
+
+	error = -EINVAL;
+	if (gfs_is_jdata(ip))
+		goto out_gunlock;
+
+	if (num_gh) {
+		error = grope_mapping(buf, size);
+		if (error)
+			goto out_gunlock;
+	}
+
+	if (file->f_flags & O_APPEND)
+		*offset = ip->i_di.di_size;
+
+	if (!(file->f_flags & O_LARGEFILE)) {
+		error = -EFBIG;
+		if (*offset >= 0x7FFFFFFFull)
+			goto out_gunlock;
+		if (*offset + size > 0x7FFFFFFFull)
+			size = 0x7FFFFFFFull - *offset;
+	}
+
+	if (gfs_is_stuffed(ip) ||
+	    *offset + size > ip->i_di.di_size ||
+	    ((ip->i_di.di_mode & (S_ISUID | S_ISGID)) && !capable(CAP_FSETID)))
+		alloc_required = TRUE;
+	else {
+		error = gfs_write_alloc_required(ip, *offset, size,
+						 &alloc_required);
+		if (error)
+			goto out_gunlock;
+	}
+
+	if (alloc_required && state != LM_ST_EXCLUSIVE) {
+		gfs_glock_dq_m(num_gh + 1, ghs);
+		gfs_holder_uninit(&ghs[num_gh]);
+		state = LM_ST_EXCLUSIVE;
+		goto restart;
+	}
+
+	if (alloc_required) {
+		set_bit(GFF_DID_DIRECT_ALLOC, &fp->f_flags);
+
+		/* split large writes into smaller atomic transactions */
+		while (size) {
+			s = gfs_tune_get(sdp, gt_max_atomic_write);
+			if (s > size)
+				s = size;
+
+			error = do_write_direct_alloc(file, buf, s, offset, iocb);
+			if (error < 0)
+				goto out_gunlock;
+
+			buf += error;
+			size -= error;
+			count += error;
+		}
+	} else {
+		struct iovec local_iov = { .iov_base = buf, .iov_len = size };
+		struct gfs_holder t_gh;
+
+		clear_bit(GFF_DID_DIRECT_ALLOC, &fp->f_flags);
+
+		error = gfs_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &t_gh);
+		if (error)
+			goto out_gunlock;
+
+		/* Todo: It would be nice if init_sync_kiocb is exported.
+		 *  .. wcheng
+		 */
+		if (!iocb) 
+			count = 
+			generic_file_write_nolock(file, &local_iov, 1, offset);
+		else {
+			count = 
+			generic_file_aio_write_nolock(iocb, &local_iov, 1, offset);
+		}
+		gfs_glock_dq_uninit(&t_gh);
+	}
+
+	error = 0;
+
+      out_gunlock:
+	gfs_glock_dq_m(num_gh + 1, ghs);
+
+      out:
+	up_write(&inode->i_alloc_sem);
+	gfs_holder_uninit(&ghs[num_gh]);
+
+	return (count) ? count : error;
+}
+
+/**
+ * do_do_write_buf - Write bytes to a file
+ * @file: The file to write to
+ * @buf: The buffer to copy from
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ *
+ * Outputs: Offset - updated according to number of bytes written
+ *
+ * Returns: The number of bytes written, errno on failure
+ */
+
+static ssize_t
+do_do_write_buf(struct file *file, char *buf, size_t size, loff_t *offset,
+		struct kiocb *iocb)
+{
+	struct inode *inode = file->f_mapping->host;
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = NULL;
+	struct buffer_head *dibh;
+	unsigned int data_blocks, ind_blocks;
+	int alloc_required, journaled;
+	ssize_t count;
+	int error;
+
+	journaled = gfs_is_jdata(ip);
+
+	gfs_write_calc_reserv(ip, size, &data_blocks, &ind_blocks);
+
+	error = gfs_write_alloc_required(ip, *offset, size, &alloc_required);
+	if (error)
+		return error;
+
+	if (alloc_required) {
+		al = gfs_alloc_get(ip);
+
+		error = gfs_quota_lock_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+		if (error)
+			goto fail;
+
+		error = gfs_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid);
+		if (error)
+			goto fail_gunlock_q;
+
+		if (journaled)
+			al->al_requested_meta = ind_blocks + data_blocks;
+		else {
+			al->al_requested_meta = ind_blocks;
+			al->al_requested_data = data_blocks;
+		}
+
+		error = gfs_inplace_reserve(ip);
+		if (error)
+			goto fail_gunlock_q;
+
+		/* Trans may require:
+		   All blocks for a RG bitmap, whatever indirect blocks we
+		   need, a modified dinode, and a quota change. */
+
+		error = gfs_trans_begin(sdp,
+					1 + al->al_rgd->rd_ri.ri_length +
+					ind_blocks +
+					((journaled) ? data_blocks : 0), 1);
+		if (error)
+			goto fail_ipres;
+	} else {
+		/* Trans may require:
+		   A modified dinode. */
+
+		error = gfs_trans_begin(sdp,
+					1 + ((journaled) ? data_blocks : 0), 0);
+		if (error)
+			goto fail_ipres;
+	}
+
+	if ((ip->i_di.di_mode & (S_ISUID | S_ISGID)) && !capable(CAP_FSETID)) {
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (error)
+			goto fail_end_trans;
+
+		ip->i_di.di_mode &= (ip->i_di.di_mode & S_IXGRP) ? (~(S_ISUID | S_ISGID)) : (~S_ISUID);
+
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	if (journaled ||
+	    (gfs_is_stuffed(ip) && !test_bit(GIF_PAGED, &ip->i_flags) &&
+	     *offset + size <= sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode))) {
+
+		count = gfs_writei(ip, buf, *offset, size, gfs_copy_from_user, iocb);
+		if (count < 0) {
+			error = count;
+			goto fail_end_trans;
+		}
+		if (gfs_is_stuffed(ip)){
+			struct page *page;
+			page = find_get_page(file->f_mapping, 0);
+			if (page) {
+				ClearPageUptodate(page);
+				page_cache_release(page);
+			}
+		}
+		*offset += count;
+	} else {
+		struct iovec local_iov = { .iov_base = buf, .iov_len = size };
+
+		if (!iocb) {
+			count = generic_file_write_nolock(file, &local_iov, 1, offset);
+		} else {
+			count = generic_file_aio_write_nolock(iocb, 
+				&local_iov, 1, offset);
+			if (count == -EIOCBQUEUED)
+				count = wait_on_sync_kiocb(iocb);
+		}
+		if (count < 0) {
+			error = count;
+			goto fail_end_trans;
+		}
+
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (error)
+			goto fail_end_trans;
+
+		if (ip->i_di.di_size < inode->i_size)
+			ip->i_di.di_size = inode->i_size;
+		ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
+
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	gfs_trans_end(sdp);
+
+	if (file->f_flags & O_SYNC || IS_SYNC(inode)) {
+		gfs_log_flush_glock(ip->i_gl);
+		error = filemap_fdatawrite(file->f_mapping);
+		if (error == 0)
+			error = filemap_fdatawait(file->f_mapping);
+		if (error)
+			goto fail_ipres;
+	}
+
+	if (alloc_required) {
+		gfs_assert_warn(sdp, count != size ||
+				al->al_alloced_meta ||
+				al->al_alloced_data);
+		gfs_inplace_release(ip);
+		gfs_quota_unlock_m(ip);
+		gfs_alloc_put(ip);
+	}
+
+	return count;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_ipres:
+	if (alloc_required)
+		gfs_inplace_release(ip);
+
+ fail_gunlock_q:
+	if (alloc_required)
+		gfs_quota_unlock_m(ip);
+
+ fail:
+	if (alloc_required)
+		gfs_alloc_put(ip);
+
+	return error;
+}
+
+/**
+ * do_write_buf - Write bytes to a file
+ * @file: The file to write to
+ * @buf: The buffer to copy from
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ * @num_gh: The number of other locks we need to do the read
+ * @gh: the locks we need plus one for our lock
+ *
+ * Outputs: Offset - updated according to number of bytes written
+ *
+ * Returns: The number of bytes written, errno on failure
+ */
+
+static ssize_t
+do_write_buf(struct file *file,
+		char *buf, size_t size, loff_t *offset,
+		struct kiocb *iocb,
+		unsigned int num_gh, struct gfs_holder *ghs)
+{
+	struct gfs_inode *ip = vn2ip(file->f_mapping->host);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	size_t s;
+	ssize_t count = 0;
+	int error;
+
+	gfs_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[num_gh]);
+
+	error = gfs_glock_nq_m(num_gh + 1, ghs);
+	if (error)
+		goto out;
+
+	if (num_gh) {
+		error = grope_mapping(buf, size);
+		if (error)
+			goto out_gunlock;
+	}
+
+	if (file->f_flags & O_APPEND)
+		*offset = ip->i_di.di_size;
+
+	if (!(file->f_flags & O_LARGEFILE)) {
+		error = -EFBIG;
+		if (*offset >= 0x7FFFFFFFull)
+			goto out_gunlock;
+		if (*offset + size > 0x7FFFFFFFull)
+			size = 0x7FFFFFFFull - *offset;
+	}
+
+	/* split large writes into smaller atomic transactions */
+	while (size) {
+		s = gfs_tune_get(sdp, gt_max_atomic_write);
+		if (s > size)
+			s = size;
+
+		error = do_do_write_buf(file, buf, s, offset, iocb);
+		if (error < 0)
+			goto out_gunlock;
+
+		buf += error;
+		size -= error;
+		count += error;
+	}
+
+	error = 0;
+
+ out_gunlock:
+	gfs_glock_dq_m(num_gh + 1, ghs);
+
+ out:
+	gfs_holder_uninit(&ghs[num_gh]);
+
+	return (count) ? count : error;
+}
+
+/**
+ * gfs_write - Write bytes to a file
+ * @file: The file to write to
+ * @buf: The buffer to copy from
+ * @size: The amount of data requested
+ * @offset: The current file offset
+ *
+ * Outputs: Offset - updated according to number of bytes written
+ *
+ * Returns: The number of bytes written, errno on failure
+ */
+
+static ssize_t
+__gfs_write(struct file *file, const char *buf, size_t size, loff_t *offset, struct kiocb *iocb)
+{
+	struct inode *inode = file->f_mapping->host;
+	ssize_t count;
+
+	atomic_inc(&vfs2sdp(inode->i_sb)->sd_ops_file);
+
+	if (*offset < 0)
+		return -EINVAL;
+	if (!access_ok(VERIFY_READ, buf, size))
+		return -EFAULT;
+
+	down(&inode->i_sem);
+	if (file->f_flags & O_DIRECT)
+		count = walk_vm(file, (char *)buf, size, offset, iocb, do_write_direct);
+	else
+		count = walk_vm(file, (char *)buf, size, offset, iocb, do_write_buf);
+	up(&inode->i_sem);
+
+	return count;
+}
+
+static ssize_t
+gfs_write(struct file *file, const char *buf, size_t size, loff_t *offset) 
+{
+	return(__gfs_write(file, buf, size, offset, NULL));
+}
+
+static ssize_t
+gfs_aio_write(struct kiocb *iocb, const char __user *buf, size_t size, loff_t pos)
+{
+	struct file *file = iocb->ki_filp;
+
+	BUG_ON(iocb->ki_pos != pos);
+
+	/* things to check:
+	 *   const char __user *buf (aio) and const char *buf (sio)
+	 */
+	return(__gfs_write(file, buf, size, &iocb->ki_pos, iocb));
+}
+/**
+ * filldir_reg_func - Report a directory entry to the caller of gfs_dir_read()
+ * @opaque: opaque data used by the function
+ * @name: the name of the directory entry
+ * @length: the length of the name
+ * @offset: the entry's offset in the directory
+ * @inum: the inode number the entry points to
+ * @type: the type of inode the entry points to
+ *
+ * Returns: 0 on success, 1 if buffer full
+ */
+
+static int
+filldir_reg_func(void *opaque,
+		 const char *name, unsigned int length,
+		 uint64_t offset,
+		 struct gfs_inum *inum, unsigned int type)
+{
+	struct filldir_reg *fdr = (struct filldir_reg *)opaque;
+	struct gfs_sbd *sdp = fdr->fdr_sbd;
+	unsigned int vfs_type;
+	int error;
+
+	switch (type) {
+	case GFS_FILE_NON:
+		vfs_type = DT_UNKNOWN;
+		break;
+	case GFS_FILE_REG:
+		vfs_type = DT_REG;
+		break;
+	case GFS_FILE_DIR:
+		vfs_type = DT_DIR;
+		break;
+	case GFS_FILE_LNK:
+		vfs_type = DT_LNK;
+		break;
+	case GFS_FILE_BLK:
+		vfs_type = DT_BLK;
+		break;
+	case GFS_FILE_CHR:
+		vfs_type = DT_CHR;
+		break;
+	case GFS_FILE_FIFO:
+		vfs_type = DT_FIFO;
+		break;
+	case GFS_FILE_SOCK:
+		vfs_type = DT_SOCK;
+		break;
+	default:
+		if (gfs_consist(sdp))
+			printk("GFS: fsid=%s: type = %u\n",
+			       sdp->sd_fsname, type);
+		return -EIO;
+	}
+
+	error = fdr->fdr_filldir(fdr->fdr_opaque, name, length, offset,
+				 inum->no_formal_ino, vfs_type);
+	if (error)
+		return 1;
+
+	/* Prefetch locks */
+	if (fdr->fdr_prefetch && !(length == 1 && *name == '.')) {
+		gfs_glock_prefetch_num(sdp,
+				       inum->no_formal_ino, &gfs_inode_glops,
+				       LM_ST_SHARED, LM_FLAG_TRY | LM_FLAG_ANY);
+		gfs_glock_prefetch_num(sdp,
+				       inum->no_addr, &gfs_iopen_glops,
+				       LM_ST_SHARED, LM_FLAG_TRY);
+	}
+
+	return 0;
+}
+
+/**
+ * readdir_reg - Read directory entries from a directory
+ * @file: The directory to read from
+ * @dirent: Buffer for dirents
+ * @filldir: Function used to do the copying
+ *
+ * Returns: errno
+ */
+
+static int
+readdir_reg(struct file *file, void *dirent, filldir_t filldir)
+{
+	struct gfs_inode *dip = vn2ip(file->f_mapping->host);
+	struct filldir_reg fdr;
+	struct gfs_holder d_gh;
+	uint64_t offset = file->f_pos;
+	int error;
+
+	fdr.fdr_sbd = dip->i_sbd;
+	fdr.fdr_prefetch = TRUE;
+	fdr.fdr_filldir = filldir;
+	fdr.fdr_opaque = dirent;
+
+	gfs_holder_init(dip->i_gl, LM_ST_SHARED, GL_ATIME, &d_gh);
+	error = gfs_glock_nq_atime(&d_gh);
+	if (error) {
+		gfs_holder_uninit(&d_gh);
+		return error;
+	}
+
+	error = gfs_dir_read(dip, &offset, &fdr, filldir_reg_func);
+
+	gfs_glock_dq_uninit(&d_gh);
+
+	file->f_pos = offset;
+
+	return error;
+}
+
+/**
+ * filldir_bad_func - Report a directory entry to the caller of gfs_dir_read()
+ * @opaque: opaque data used by the function
+ * @name: the name of the directory entry
+ * @length: the length of the name
+ * @offset: the entry's offset in the directory
+ * @inum: the inode number the entry points to
+ * @type: the type of inode the entry points to
+ *
+ * For supporting NFS.
+ *
+ * Returns: 0 on success, 1 if buffer full
+ */
+
+static int
+filldir_bad_func(void *opaque,
+		 const char *name, unsigned int length,
+		 uint64_t offset,
+		 struct gfs_inum *inum, unsigned int type)
+{
+	struct filldir_bad *fdb = (struct filldir_bad *)opaque;
+	struct gfs_sbd *sdp = fdb->fdb_sbd;
+	struct filldir_bad_entry *fbe;
+
+	if (fdb->fdb_entry_off == fdb->fdb_entry_num ||
+	    fdb->fdb_name_off + length > fdb->fdb_name_size)
+		return 1;
+
+	fbe = &fdb->fdb_entry[fdb->fdb_entry_off];
+	fbe->fbe_name = fdb->fdb_name + fdb->fdb_name_off;
+	memcpy(fbe->fbe_name, name, length);
+	fbe->fbe_length = length;
+	fbe->fbe_offset = offset;
+	fbe->fbe_inum = *inum;
+	fbe->fbe_type = type;
+
+	fdb->fdb_entry_off++;
+	fdb->fdb_name_off += length;
+
+	/* Prefetch locks */
+	if (!(length == 1 && *name == '.')) {
+		gfs_glock_prefetch_num(sdp,
+				       inum->no_formal_ino, &gfs_inode_glops,
+				       LM_ST_SHARED, LM_FLAG_TRY | LM_FLAG_ANY);
+		gfs_glock_prefetch_num(sdp,
+				       inum->no_addr, &gfs_iopen_glops,
+				       LM_ST_SHARED, LM_FLAG_TRY);
+	}
+
+	return 0;
+}
+
+/**
+ * readdir_bad - Read directory entries from a directory
+ * @file: The directory to read from
+ * @dirent: Buffer for dirents
+ * @filldir: Function used to do the copying
+ *
+ * For supporting NFS.
+ *
+ * Returns: errno
+ */
+
+static int
+readdir_bad(struct file *file, void *dirent, filldir_t filldir)
+{
+	struct gfs_inode *dip = vn2ip(file->f_mapping->host);
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct filldir_reg fdr;
+	unsigned int entries, size;
+	struct filldir_bad *fdb;
+	struct gfs_holder d_gh;
+	uint64_t offset = file->f_pos;
+	unsigned int x;
+	struct filldir_bad_entry *fbe;
+	int error;
+
+	entries = gfs_tune_get(sdp, gt_entries_per_readdir);
+	size = sizeof(struct filldir_bad) +
+	    entries * (sizeof(struct filldir_bad_entry) + GFS_FAST_NAME_SIZE);
+
+	fdb = kmalloc(size, GFP_KERNEL);
+	if (!fdb)
+		return -ENOMEM;
+	memset(fdb, 0, size);
+
+	fdb->fdb_sbd = sdp;
+	fdb->fdb_entry = (struct filldir_bad_entry *)(fdb + 1);
+	fdb->fdb_entry_num = entries;
+	fdb->fdb_name = ((char *)fdb) + sizeof(struct filldir_bad) +
+		entries * sizeof(struct filldir_bad_entry);
+	fdb->fdb_name_size = entries * GFS_FAST_NAME_SIZE;
+
+	gfs_holder_init(dip->i_gl, LM_ST_SHARED, GL_ATIME, &d_gh);
+	error = gfs_glock_nq_atime(&d_gh);
+	if (error) {
+		gfs_holder_uninit(&d_gh);
+		goto out;
+	}
+
+	error = gfs_dir_read(dip, &offset, fdb, filldir_bad_func);
+
+	gfs_glock_dq_uninit(&d_gh);
+
+	fdr.fdr_sbd = sdp;
+	fdr.fdr_prefetch = FALSE;
+	fdr.fdr_filldir = filldir;
+	fdr.fdr_opaque = dirent;
+
+	for (x = 0; x < fdb->fdb_entry_off; x++) {
+		fbe = &fdb->fdb_entry[x];
+
+		error = filldir_reg_func(&fdr,
+					 fbe->fbe_name, fbe->fbe_length,
+					 fbe->fbe_offset,
+					 &fbe->fbe_inum, fbe->fbe_type);
+		if (error) {
+			file->f_pos = fbe->fbe_offset;
+			error = 0;
+			goto out;
+		}
+	}
+
+	file->f_pos = offset;
+
+ out:
+	kfree(fdb);
+
+	return error;
+}
+
+/**
+ * gfs_readdir - Read directory entries from a directory
+ * @file: The directory to read from
+ * @dirent: Buffer for dirents
+ * @filldir: Function used to do the copying
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+	int error;
+
+	atomic_inc(&vfs2sdp(file->f_mapping->host->i_sb)->sd_ops_file);
+
+	/* Use "bad" one if we're called from NFS daemon */
+	if (strcmp(current->comm, "nfsd") != 0)
+		error = readdir_reg(file, dirent, filldir);
+	else
+		error = readdir_bad(file, dirent, filldir);
+
+	return error;
+}
+
+/**
+ * gfs_ioctl - do an ioctl on a file
+ * @inode: the inode
+ * @file: the file pointer
+ * @cmd: the ioctl command
+ * @arg: the argument
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_ioctl(struct inode *inode, struct file *file,
+	  unsigned int cmd, unsigned long arg)
+{
+	struct gfs_inode *ip = vn2ip(inode);
+
+	atomic_inc(&ip->i_sbd->sd_ops_file);
+
+	switch (cmd) {
+	case GFS_IOCTL_IDENTIFY: {
+                unsigned int x = GFS_MAGIC;
+                if (copy_to_user((unsigned int *)arg, &x, sizeof(unsigned int)))
+                        return -EFAULT;
+		return 0;
+        }
+
+	case GFS_IOCTL_SUPER:
+		return gfs_ioctl_i(ip, (void *)arg);
+
+	default:
+		return -ENOTTY;
+	}
+}
+
+/**
+ * gfs_mmap - We don't support shared writable mappings right now
+ * @file: The file to map
+ * @vma: The VMA which described the mapping
+ *
+ * Returns: 0 or error code
+ */
+
+static int
+gfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct gfs_inode *ip = vn2ip(file->f_mapping->host);
+	struct gfs_holder i_gh;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_file);
+
+	gfs_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &i_gh);
+	error = gfs_glock_nq_atime(&i_gh);
+	if (error) {
+		gfs_holder_uninit(&i_gh);
+		return error;
+	}
+
+	if (gfs_is_jdata(ip)) {
+		if (vma->vm_flags & VM_MAYSHARE)
+			error = -ENOSYS;
+		else
+			vma->vm_ops = &gfs_vm_ops_private;
+	} else {
+		/* This is VM_MAYWRITE instead of VM_WRITE because a call
+		   to mprotect() can turn on VM_WRITE later. */
+
+		if ((vma->vm_flags & (VM_MAYSHARE | VM_MAYWRITE)) == (VM_MAYSHARE | VM_MAYWRITE))
+			vma->vm_ops = &gfs_vm_ops_sharewrite;
+		else
+			vma->vm_ops = &gfs_vm_ops_private;
+	}
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_open - open a file
+ * @inode: the inode to open
+ * @file: the struct file for this opening
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_open(struct inode *inode, struct file *file)
+{
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_holder i_gh;
+	struct gfs_file *fp;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_file);
+
+	fp = kmalloc(sizeof(struct gfs_file), GFP_KERNEL);
+	if (!fp)
+		return -ENOMEM;
+	memset(fp, 0, sizeof(struct gfs_file));
+
+	init_MUTEX(&fp->f_fl_lock);
+
+	fp->f_inode = ip;
+	fp->f_vfile = file;
+
+	gfs_assert_warn(ip->i_sbd, !vf2fp(file));
+	vf2fp(file) = fp;
+
+	if (ip->i_di.di_type == GFS_FILE_REG) {
+		error = gfs_glock_nq_init(ip->i_gl,
+					  LM_ST_SHARED, LM_FLAG_ANY,
+					  &i_gh);
+		if (error)
+			goto fail;
+
+		if (!(file->f_flags & O_LARGEFILE) &&
+		    ip->i_di.di_size > 0x7FFFFFFFull) {
+			error = -EFBIG;
+			goto fail_gunlock;
+		}
+
+		/* Listen to the Direct I/O flag */
+
+		if (ip->i_di.di_flags & GFS_DIF_DIRECTIO)
+			file->f_flags |= O_DIRECT;
+
+		/* Don't let the user open O_DIRECT on a jdata file */
+
+		if ((file->f_flags & O_DIRECT) && gfs_is_jdata(ip)) {
+			error = -EINVAL;
+			goto fail_gunlock;
+		}
+
+		gfs_glock_dq_uninit(&i_gh);
+	}
+
+	return 0;
+
+ fail_gunlock:
+	gfs_glock_dq_uninit(&i_gh);
+
+ fail:
+	vf2fp(file) = NULL;
+	kfree(fp);
+
+	return error;
+}
+
+/**
+ * gfs_close - called to close a struct file
+ * @inode: the inode the struct file belongs to
+ * @file: the struct file being closed
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_close(struct inode *inode, struct file *file)
+{
+	struct gfs_sbd *sdp = vfs2sdp(inode->i_sb);
+	struct gfs_file *fp;
+
+	atomic_inc(&sdp->sd_ops_file);
+
+	fp = vf2fp(file);
+	vf2fp(file) = NULL;
+
+	if (!gfs_assert_warn(sdp, fp))
+		kfree(fp);
+
+	return 0;
+}
+
+/**
+ * gfs_fsync - sync the dirty data for a file (across the cluster)
+ * @file: the file that points to the dentry (we ignore this)
+ * @dentry: the dentry that points to the inode to sync
+ *
+ * Returns: errno
+ *
+ * Obtain an EXCLUSIVE lock on the file, to force other node to sync file's
+ * dirty data to disk, as it releases the EXCLUSIVE lock.
+ */
+
+static int
+gfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+	struct gfs_inode *ip = vn2ip(dentry->d_inode);
+	struct gfs_holder i_gh;
+	struct inode *inode = dentry->d_inode;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_file);
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		return error;
+
+	if (gfs_is_jdata(ip))
+		gfs_log_flush_glock(ip->i_gl);
+	else {
+		if ((!datasync) || (inode->i_state & I_DIRTY_DATASYNC)) {
+			struct writeback_control wbc = {
+				.sync_mode = WB_SYNC_ALL,
+				.nr_to_write = 0,
+			};
+			error = sync_inode(inode, &wbc);
+		}
+	}
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_lock - acquire/release a posix lock on a file
+ * @file: the file pointer
+ * @cmd: either modify or retrieve lock state, possibly wait
+ * @fl: type and range of lock
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+	struct gfs_inode *ip = vn2ip(file->f_mapping->host);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct lm_lockname name =
+		{ .ln_number = ip->i_num.no_formal_ino,
+		  .ln_type = LM_TYPE_PLOCK };
+
+	atomic_inc(&sdp->sd_ops_file);
+
+	if (!(fl->fl_flags & FL_POSIX))
+		return -ENOLCK;
+	if ((ip->i_di.di_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+		return -ENOLCK;
+
+	if (sdp->sd_args.ar_localflocks) {
+		if (IS_GETLK(cmd))
+			return LOCK_USE_CLNT;
+		return posix_lock_file_wait(file, fl);
+	}
+
+	if (IS_GETLK(cmd))
+		return gfs_lm_plock_get(sdp, &name, file, fl);
+	else if (fl->fl_type == F_UNLCK)
+		return gfs_lm_punlock(sdp, &name, file, fl);
+	else
+		return gfs_lm_plock(sdp, &name, file, cmd, fl);
+}
+
+/**
+ * gfs_sendfile - Send bytes to a file or socket
+ * @in_file: The file to read from
+ * @out_file: The file to write to
+ * @count: The amount of data
+ * @offset: The beginning file offset
+ *
+ * Outputs: offset - updated according to number of bytes read
+ *
+ * Returns: The number of bytes sent, errno on failure
+ */
+
+static ssize_t
+gfs_sendfile(struct file *in_file, loff_t *offset, size_t count, read_actor_t actor, void __user *target)
+{
+	struct gfs_inode *ip = vn2ip(in_file->f_mapping->host);
+	struct gfs_holder gh;
+	ssize_t retval;
+
+	atomic_inc(&ip->i_sbd->sd_ops_file);
+
+	gfs_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &gh);
+
+	retval = gfs_glock_nq_atime(&gh);
+	if (retval)
+		goto out;
+
+	if (gfs_is_jdata(ip))
+		retval = -ENOSYS;
+	else 
+		retval = generic_file_sendfile(in_file, offset, count, actor, target);
+
+	gfs_glock_dq(&gh);
+
+ out:
+	gfs_holder_uninit(&gh);
+
+	return retval;
+}
+
+/**
+ * do_flock - Acquire a flock on a file
+ * @file:
+ * @cmd:
+ * @fl:
+ *
+ * Returns: errno
+ */
+
+static int
+do_flock(struct file *file, int cmd, struct file_lock *fl)
+{
+	struct gfs_file *fp = vf2fp(file);
+	struct gfs_holder *fl_gh = &fp->f_fl_gh;
+	struct gfs_inode *ip = fp->f_inode;
+	struct gfs_glock *gl;
+	unsigned int state;
+	int flags;
+	int error = 0;
+
+	state = (fl->fl_type == F_WRLCK) ? LM_ST_EXCLUSIVE : LM_ST_SHARED;
+	flags = ((IS_SETLKW(cmd)) ? 0 : LM_FLAG_TRY) | GL_EXACT | GL_NOCACHE;
+
+	down(&fp->f_fl_lock);
+
+	gl = fl_gh->gh_gl;
+	if (gl) {
+		if (fl_gh->gh_state == state)
+			goto out;
+		gfs_glock_hold(gl);
+		flock_lock_file_wait(file,
+				     &(struct file_lock){.fl_type = F_UNLCK});		
+		gfs_glock_dq_uninit(fl_gh);
+	} else {
+		error = gfs_glock_get(ip->i_sbd,
+				      ip->i_num.no_formal_ino, &gfs_flock_glops,
+				      CREATE, &gl);
+		if (error)
+			goto out;
+	}
+
+	gfs_holder_init(gl, state, flags, fl_gh);
+	gfs_glock_put(gl);
+
+	error = gfs_glock_nq(fl_gh);
+	if (error) {
+		gfs_holder_uninit(fl_gh);
+		if (error == GLR_TRYFAILED)
+			error = -EAGAIN;
+	} else {
+		error = flock_lock_file_wait(file, fl);
+		gfs_assert_warn(ip->i_sbd, !error);
+	}
+
+ out:
+	up(&fp->f_fl_lock);
+
+	return error;
+}
+
+/**
+ * do_unflock - Release a flock on a file
+ * @file: the file
+ * @fl:
+ *
+ */
+
+static void
+do_unflock(struct file *file, struct file_lock *fl)
+{
+	struct gfs_file *fp = vf2fp(file);
+	struct gfs_holder *fl_gh = &fp->f_fl_gh;
+
+	down(&fp->f_fl_lock);
+	flock_lock_file_wait(file, fl);
+	if (fl_gh->gh_gl)
+		gfs_glock_dq_uninit(fl_gh);
+	up(&fp->f_fl_lock);
+}
+
+/**
+ * gfs_flock - acquire/release a flock lock on a file
+ * @file: the file pointer
+ * @cmd: either modify or retrieve lock state, possibly wait
+ * @fl: type and range of lock
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_flock(struct file *file, int cmd, struct file_lock *fl)
+{
+	struct gfs_inode *ip = vn2ip(file->f_mapping->host);
+	struct gfs_sbd *sdp = ip->i_sbd;
+
+	atomic_inc(&ip->i_sbd->sd_ops_file);
+
+	if (!(fl->fl_flags & FL_FLOCK))
+		return -ENOLCK;
+	if ((ip->i_di.di_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+		return -ENOLCK;
+
+	if (sdp->sd_args.ar_localflocks)
+		return flock_lock_file_wait(file, fl);
+
+	if (fl->fl_type == F_UNLCK) {
+		do_unflock(file, fl);
+		return 0;
+	} else
+		return do_flock(file, cmd, fl);
+}
+
+struct file_operations gfs_file_fops = {
+	.llseek = gfs_llseek,
+	.read = gfs_read,
+	.write = gfs_write,
+        .aio_read = gfs_aio_read,
+        .aio_write = gfs_aio_write,
+	.ioctl = gfs_ioctl,
+	.mmap = gfs_mmap,
+	.open = gfs_open,
+	.release = gfs_close,
+	.fsync = gfs_fsync,
+	.lock = gfs_lock,
+	.sendfile = gfs_sendfile,
+	.flock = gfs_flock,
+};
+
+struct file_operations gfs_dir_fops = {
+	.readdir = gfs_readdir,
+	.ioctl = gfs_ioctl,
+	.open = gfs_open,
+	.release = gfs_close,
+	.fsync = gfs_fsync,
+	.lock = gfs_lock,
+	.flock = gfs_flock,
+};
diff -pruN linux-2.6.9.orig/fs/gfs/ops_file.h linux-2.6.9.debug/fs/gfs/ops_file.h
--- linux-2.6.9.orig/fs/gfs/ops_file.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_file.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,20 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __OPS_FILE_DOT_H__
+#define __OPS_FILE_DOT_H__
+
+extern struct file_operations gfs_file_fops;
+extern struct file_operations gfs_dir_fops;
+
+#endif /* __OPS_FILE_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ops_fstype.c linux-2.6.9.debug/fs/gfs/ops_fstype.c
--- linux-2.6.9.orig/fs/gfs/ops_fstype.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_fstype.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,766 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+
+#include "gfs.h"
+#include "daemon.h"
+#include "diaper.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "lm.h"
+#include "mount.h"
+#include "ops_export.h"
+#include "ops_fstype.h"
+#include "ops_super.h"
+#include "proc.h"
+#include "quota.h"
+#include "recovery.h"
+#include "rgrp.h"
+#include "super.h"
+#include "unlinked.h"
+
+/**
+ * gfs_read_super - Read in superblock
+ * @sb: The VFS superblock
+ * @data: Mount options
+ * @silent: Don't complain if it's not a GFS filesystem
+ *
+ * Returns: errno
+ *
+ * After cross-linking Linux VFS incore superblock and our GFS incore superblock
+ *   (filesystem instance structures) to one another, we:
+ * -- Init some of our GFS incore superblock, including some temporary
+ *       block-size values (enough to read on-disk superblock).
+ * -- Set up some things in Linux VFS superblock.
+ * -- Mount a lock module, init glock system (incl. glock reclaim daemons),
+ *       and init some important inter-node locks (MOUNT, LIVE, SuperBlock).
+ * -- Read-in the GFS on-disk superblock (1st time, to get enough info
+ *       to do filesystem upgrade and journal replay, incl. journal index).
+ * -- Upgrade on-disk filesystem format (rarely needed).
+ * -- Replay journal(s) (always; replay *all* journals if we're first-to-mount).
+ * -- Read-in on-disk superblock and journal index special file again (2nd time,
+ *       assumed 100% valid now after journal replay).
+ * -- Read-in info on other special (hidden) files (root inode, resource index,
+ *       quota inode, license inode).
+ * -- Start other daemons (journal/log recovery, log tail, quota updates, inode
+ *       reclaim) for periodic maintenance.
+ * 
+ */
+
+static int
+fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct gfs_sbd *sdp;
+	struct gfs_holder mount_gh, sb_gh, ji_gh;
+	struct inode *inode;
+	int super = TRUE, jindex = TRUE;
+	unsigned int x;
+	int error;
+
+	sdp = vmalloc(sizeof(struct gfs_sbd));
+	if (!sdp) {
+		printk("GFS: can't alloc struct gfs_sbd\n");
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	memset(sdp, 0, sizeof(struct gfs_sbd));
+
+	vfs2sdp(sb) = sdp;
+	sdp->sd_vfs = sb;
+	gfs_diaper_register_sbd(sb->s_bdev, sdp);
+
+	/*  Init rgrp variables  */
+
+	INIT_LIST_HEAD(&sdp->sd_rglist);
+	init_MUTEX(&sdp->sd_rindex_lock);
+	INIT_LIST_HEAD(&sdp->sd_rg_mru_list);
+	spin_lock_init(&sdp->sd_rg_mru_lock);
+	INIT_LIST_HEAD(&sdp->sd_rg_recent);
+	spin_lock_init(&sdp->sd_rg_recent_lock);
+	spin_lock_init(&sdp->sd_rg_forward_lock);
+
+	for (x = 0; x < GFS_GL_HASH_SIZE; x++) {
+		sdp->sd_gl_hash[x].hb_lock = RW_LOCK_UNLOCKED;
+		INIT_LIST_HEAD(&sdp->sd_gl_hash[x].hb_list);
+	}
+
+	INIT_LIST_HEAD(&sdp->sd_reclaim_list);
+	spin_lock_init(&sdp->sd_reclaim_lock);
+	init_waitqueue_head(&sdp->sd_reclaim_wchan);
+
+	for (x = 0; x < GFS_MHC_HASH_SIZE; x++)
+		INIT_LIST_HEAD(&sdp->sd_mhc[x]);
+	INIT_LIST_HEAD(&sdp->sd_mhc_single);
+	spin_lock_init(&sdp->sd_mhc_lock);
+
+	for (x = 0; x < GFS_DEPEND_HASH_SIZE; x++)
+		INIT_LIST_HEAD(&sdp->sd_depend[x]);
+	spin_lock_init(&sdp->sd_depend_lock);
+
+	init_MUTEX(&sdp->sd_freeze_lock);
+
+	init_MUTEX(&sdp->sd_thread_lock);
+	init_completion(&sdp->sd_thread_completion);
+
+	spin_lock_init(&sdp->sd_log_seg_lock);
+	INIT_LIST_HEAD(&sdp->sd_log_seg_list);
+	init_waitqueue_head(&sdp->sd_log_seg_wait);
+	INIT_LIST_HEAD(&sdp->sd_log_ail);
+	INIT_LIST_HEAD(&sdp->sd_log_incore);
+	init_rwsem(&sdp->sd_log_lock);
+	INIT_LIST_HEAD(&sdp->sd_unlinked_list);
+	spin_lock_init(&sdp->sd_unlinked_lock);
+	INIT_LIST_HEAD(&sdp->sd_quota_list);
+	spin_lock_init(&sdp->sd_quota_lock);
+
+	INIT_LIST_HEAD(&sdp->sd_dirty_j);
+	spin_lock_init(&sdp->sd_dirty_j_lock);
+
+	spin_lock_init(&sdp->sd_ail_lock);
+	INIT_LIST_HEAD(&sdp->sd_recovery_bufs);
+
+	gfs_tune_init(&sdp->sd_tune);
+
+	error = gfs_make_args((char *)data, &sdp->sd_args);
+	if (error) {
+		printk("GFS: can't parse mount arguments\n");
+		goto fail_vfree;
+	}
+
+	/*  Copy VFS mount flags  */
+
+	if (sb->s_flags & (MS_NOATIME | MS_NODIRATIME))
+		set_bit(SDF_NOATIME, &sdp->sd_flags);
+	if (sb->s_flags & MS_RDONLY)
+		set_bit(SDF_ROFS, &sdp->sd_flags);
+
+	/*  Set up Linux Virtual (VFS) Super Block  */
+
+	sb->s_magic = GFS_MAGIC;
+	sb->s_op = &gfs_super_ops;
+	sb->s_export_op = &gfs_export_ops;
+
+	/*  Don't let the VFS update atimes.  GFS handles this itself. */
+	sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+	/*  If we were mounted with -o acl (to support POSIX access control
+	    lists), tell VFS */
+	if (sdp->sd_args.ar_posix_acls)
+		sb->s_flags |= MS_POSIXACL;
+
+	/*  Set up the buffer cache and fill in some fake block size values
+	   to allow us to read-in the on-disk superblock.  */
+
+	sdp->sd_sb.sb_bsize = sb_min_blocksize(sb, GFS_BASIC_BLOCK);
+	sdp->sd_sb.sb_bsize_shift = sb->s_blocksize_bits;
+	sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift - GFS_BASIC_BLOCK_SHIFT;
+	sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift;
+
+	if (sizeof(struct gfs_sb) > sdp->sd_sb.sb_bsize) {
+		printk("GFS: sizeof(struct gfs_sb) > sdp->sd_sb.sb_bsize\n"
+		       "GFS: %u > %u\n",
+		       (unsigned int)sizeof(struct gfs_sb), sdp->sd_sb.sb_bsize);
+		error = -EINVAL;
+		goto fail_vfree;
+	}
+
+	/*  Mount an inter-node lock module, check for local optimizations */
+
+	error = gfs_lm_mount(sdp, silent);
+	if (error)
+		goto fail_vfree;
+
+	if ((sdp->sd_lockstruct.ls_flags & LM_LSFLAG_LOCAL) &&
+	    !sdp->sd_args.ar_ignore_local_fs) {
+		/* Force local [p|f]locks */
+		sdp->sd_args.ar_localflocks = TRUE;
+
+		/* Force local read ahead and caching */
+		sdp->sd_args.ar_localcaching = TRUE;
+
+		/* Allow the machine to oops */
+		sdp->sd_args.ar_oopses_ok = TRUE;
+	}
+
+	/*  Start up the scand thread  */
+
+	error = kernel_thread(gfs_scand, sdp, 0);
+	if (error < 0) {
+		printk("GFS: fsid=%s: can't start scand thread: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_lockproto;
+	}
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Start up the glockd thread  */
+
+	for (sdp->sd_glockd_num = 0;
+	     sdp->sd_glockd_num < sdp->sd_args.ar_num_glockd;
+	     sdp->sd_glockd_num++) {
+		error = kernel_thread(gfs_glockd, sdp, 0);
+		if (error < 0) {
+			printk("GFS: fsid=%s: can't start glockd thread: %d\n",
+			       sdp->sd_fsname, error);
+			goto fail_glockd;
+		}
+		wait_for_completion(&sdp->sd_thread_completion);
+	}
+
+	/*  Only one node may mount at a time */
+	error = gfs_glock_nq_num(sdp,
+				 GFS_MOUNT_LOCK, &gfs_nondisk_glops,
+				 LM_ST_EXCLUSIVE, LM_FLAG_NOEXP | GL_NOCACHE,
+				 &mount_gh);
+	if (error) {
+		printk("GFS: fsid=%s: can't acquire mount glock: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_glockd;
+	}
+
+	/*  Show that cluster is alive */
+	error = gfs_glock_nq_num(sdp,
+				 GFS_LIVE_LOCK, &gfs_nondisk_glops,
+				 LM_ST_SHARED, LM_FLAG_NOEXP | GL_EXACT,
+				 &sdp->sd_live_gh);
+	if (error) {
+		printk("GFS: fsid=%s: can't acquire live glock: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_gunlock_mount;
+	}
+
+	sdp->sd_live_gh.gh_owner = NULL;
+
+	/*  Read the SuperBlock from disk, get enough info to enable us
+	    to read-in the journal index and replay all journals. */
+
+	error = gfs_glock_nq_num(sdp,
+				 GFS_SB_LOCK, &gfs_meta_glops,
+				 (sdp->sd_args.ar_upgrade) ? LM_ST_EXCLUSIVE : LM_ST_SHARED,
+				 0, &sb_gh);
+	if (error) {
+		printk("GFS: fsid=%s: can't acquire superblock glock: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_gunlock_live;
+	}
+
+	error = gfs_read_sb(sdp, sb_gh.gh_gl, silent);
+	if (error) {
+		printk("GFS: fsid=%s: can't read superblock: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_gunlock_sb;
+	}
+
+	/*  Set up the buffer cache and SB for real, now that we know block
+	      sizes, version #s, locations of important on-disk inodes, etc.  */
+
+	error = -EINVAL;
+	if (sdp->sd_sb.sb_bsize < bdev_hardsect_size(sb->s_bdev)) {
+		printk("GFS: fsid=%s: FS block size (%u) is too small for device block size (%u)\n",
+		       sdp->sd_fsname, sdp->sd_sb.sb_bsize, bdev_hardsect_size(sb->s_bdev));
+		goto fail_gunlock_sb;
+	}
+	if (sdp->sd_sb.sb_bsize > PAGE_SIZE) {
+		printk("GFS: fsid=%s: FS block size (%u) is too big for machine page size (%u)\n",
+		       sdp->sd_fsname, sdp->sd_sb.sb_bsize,
+		       (unsigned int)PAGE_SIZE);
+		goto fail_gunlock_sb;
+	}
+
+	/*  Get rid of buffers from the original block size  */
+	sb_gh.gh_gl->gl_ops->go_inval(sb_gh.gh_gl, DIO_METADATA | DIO_DATA);
+	sb_gh.gh_gl->gl_aspace->i_blkbits = sdp->sd_sb.sb_bsize_shift;
+
+	sb_set_blocksize(sb, sdp->sd_sb.sb_bsize);
+	set_blocksize(gfs_diaper_2real(sb->s_bdev), sdp->sd_sb.sb_bsize);
+
+	/*  Read-in journal index inode (but not the file contents, yet)  */
+
+	error = gfs_get_jiinode(sdp);
+	if (error) {
+		printk("GFS: fsid=%s: can't get journal index inode: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_gunlock_sb;
+	}
+
+	init_MUTEX(&sdp->sd_jindex_lock);
+
+	/*  Get a handle on the transaction glock; we need this for disk format
+	    upgrade and journal replays, as well as normal operation.  */
+
+	error = gfs_glock_get(sdp, GFS_TRANS_LOCK, &gfs_trans_glops,
+			      CREATE, &sdp->sd_trans_gl);
+	if (error)
+		goto fail_ji_free;
+	set_bit(GLF_STICKY, &sdp->sd_trans_gl->gl_flags);
+
+	/*  Upgrade GFS on-disk format version numbers if we need to  */
+
+	if (sdp->sd_args.ar_upgrade) {
+		error = gfs_do_upgrade(sdp, sb_gh.gh_gl);
+		if (error)
+			goto fail_trans_gl;
+	}
+
+	/*  Load in the journal index special file */
+
+	error = gfs_jindex_hold(sdp, &ji_gh);
+	if (error) {
+		printk("GFS: fsid=%s: can't read journal index: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_trans_gl;
+	}
+
+	/*  Discover this node's journal number (lock module tells us
+	    which one to use), and lock it */
+	error = -EINVAL;
+	if (sdp->sd_lockstruct.ls_jid >= sdp->sd_journals) {
+		printk("GFS: fsid=%s: can't mount journal #%u\n",
+		       sdp->sd_fsname, sdp->sd_lockstruct.ls_jid);
+		printk("GFS: fsid=%s: there are only %u journals (0 - %u)\n",
+		     sdp->sd_fsname, sdp->sd_journals, sdp->sd_journals - 1);
+		goto fail_gunlock_ji;
+	}
+	sdp->sd_jdesc = sdp->sd_jindex[sdp->sd_lockstruct.ls_jid];
+	sdp->sd_log_seg_free = sdp->sd_jdesc.ji_nsegment;
+	sdp->sd_log_seg_ail2 = 0;
+
+	error = gfs_glock_nq_num(sdp,
+				 sdp->sd_jdesc.ji_addr, &gfs_meta_glops,
+				 LM_ST_EXCLUSIVE, LM_FLAG_NOEXP,
+				 &sdp->sd_journal_gh);
+	if (error) {
+		printk("GFS: fsid=%s: can't acquire the journal glock: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_gunlock_ji;
+	}
+
+	if (sdp->sd_lockstruct.ls_first) {
+		/*  We're first node within cluster to mount this filesystem,
+		    replay ALL of the journals, then let lock module know
+		    that we're done. */
+		for (x = 0; x < sdp->sd_journals; x++) {
+			error = gfs_recover_journal(sdp,
+						    x, sdp->sd_jindex + x,
+						    TRUE);
+			if (error) {
+				printk("GFS: fsid=%s: error recovering journal %u: %d\n",
+				       sdp->sd_fsname, x, error);
+				goto fail_gunlock_journal;
+			}
+		}
+
+		gfs_lm_others_may_mount(sdp);
+	} else {
+		/*  We're not the first; replay only our own journal. */
+		error = gfs_recover_journal(sdp,
+					    sdp->sd_lockstruct.ls_jid,
+					    &sdp->sd_jdesc,
+					    TRUE);
+		if (error) {
+			printk("GFS: fsid=%s: error recovering my journal: %d\n",
+			       sdp->sd_fsname, error);
+			goto fail_gunlock_journal;
+		}
+	}
+
+	gfs_glock_dq_uninit(&ji_gh);
+	jindex = FALSE;
+
+	/*  Disown my Journal glock  */
+
+	sdp->sd_journal_gh.gh_owner = NULL;
+
+	/*  Drop our buffer cache and reread all the things we read before
+	    the journal replay, on the unlikely chance that the replay might
+	    have affected (corrected/updated) the superblock contents
+	    or journal index. */
+
+	error = gfs_read_sb(sdp, sb_gh.gh_gl, FALSE);
+	if (error) {
+		printk("GFS: fsid=%s: can't read superblock: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_gunlock_journal;
+	}
+
+	gfs_glock_force_drop(sdp->sd_jiinode->i_gl);
+
+	error = gfs_jindex_hold(sdp, &ji_gh);
+	if (error) {
+		printk("GFS: fsid=%s: can't read journal index: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_gunlock_journal;
+	}
+	gfs_glock_dq_uninit(&ji_gh);
+
+	/*  Make the FS read/write  */
+
+	if (!test_bit(SDF_ROFS, &sdp->sd_flags)) {
+		error = gfs_make_fs_rw(sdp);
+		if (error) {
+			printk("GFS: fsid=%s: can't make FS RW: %d\n",
+			       sdp->sd_fsname, error);
+			goto fail_gunlock_journal;
+		}
+	}
+
+	/*  Start up the journal recovery thread  */
+
+	error = kernel_thread(gfs_recoverd, sdp, 0);
+	if (error < 0) {
+		printk("GFS: fsid=%s: can't start recoverd thread: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_make_ro;
+	}
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Read in the resource index inode  */
+
+	error = gfs_get_riinode(sdp);
+	if (error) {
+		printk("GFS: fsid=%s: can't get resource index inode: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_recoverd;
+	}
+
+	/*  Get the root inode  */
+
+	error = gfs_get_rootinode(sdp);
+	if (error) {
+		printk("GFS: fsid=%s: can't read in root inode: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_ri_free;
+	}
+
+	/*  Read in the quota inode  */
+
+	error = gfs_get_qinode(sdp);
+	if (error) {
+		printk("GFS: fsid=%s: can't get quota file inode: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_root_free;
+	}
+
+	/*  Read in the license inode  */
+
+	error = gfs_get_linode(sdp);
+	if (error) {
+		printk("GFS: fsid=%s: can't get license file inode: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_qi_free;
+	}
+
+	/*  We're through with the superblock lock  */
+
+	gfs_glock_dq_uninit(&sb_gh);
+	super = FALSE;
+
+	/*  Get the root inode/dentry  */
+
+	inode = gfs_iget(sdp->sd_rooti, CREATE);
+	if (!inode) {
+		printk("GFS: fsid=%s: can't get root inode\n", sdp->sd_fsname);
+		error = -ENOMEM;
+		goto fail_li_free;
+	}
+
+	sb->s_root = d_alloc_root(inode);
+	if (!sb->s_root) {
+		iput(inode);
+		printk("GFS: fsid=%s: can't get root dentry\n", sdp->sd_fsname);
+		error = -ENOMEM;
+		goto fail_li_free;
+	}
+
+	/*  Start up the logd thread  */
+
+	sdp->sd_jindex_refresh_time = jiffies;
+
+	error = kernel_thread(gfs_logd, sdp, 0);
+	if (error < 0) {
+		printk("GFS: fsid=%s: can't start logd thread: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_dput;
+	}
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Start up the quotad thread  */
+
+	error = kernel_thread(gfs_quotad, sdp, 0);
+	if (error < 0) {
+		printk("GFS: fsid=%s: can't start quotad thread: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_logd;
+	}
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Start up the inoded thread  */
+
+	error = kernel_thread(gfs_inoded, sdp, 0);
+	if (error < 0) {
+		printk("GFS: fsid=%s: can't start inoded thread: %d\n",
+		       sdp->sd_fsname, error);
+		goto fail_quotad;
+	}
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Get a handle on the rename lock  */
+
+	error = gfs_glock_get(sdp, GFS_RENAME_LOCK, &gfs_nondisk_glops,
+			      CREATE, &sdp->sd_rename_gl);
+	if (error)
+		goto fail_inoded;
+
+	gfs_proc_fs_add(sdp);
+
+	gfs_glock_dq_uninit(&mount_gh);
+
+	return 0;
+
+ fail_inoded:
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_INODED_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_inoded_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+ fail_quotad:
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_QUOTAD_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_quotad_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+ fail_logd:
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_LOGD_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_logd_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+ fail_dput:
+	dput(sb->s_root);
+
+ fail_li_free:
+	gfs_inode_put(sdp->sd_linode);
+
+ fail_qi_free:
+	gfs_inode_put(sdp->sd_qinode);
+
+ fail_root_free:
+	gfs_inode_put(sdp->sd_rooti);
+
+ fail_ri_free:
+	gfs_inode_put(sdp->sd_riinode);
+	gfs_clear_rgrpd(sdp);
+
+ fail_recoverd:
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_RECOVERD_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_recoverd_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+ fail_make_ro:
+	gfs_glock_force_drop(sdp->sd_trans_gl);
+	clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
+	gfs_unlinked_cleanup(sdp);
+	gfs_quota_cleanup(sdp);
+
+ fail_gunlock_journal:
+	gfs_glock_dq_uninit(&sdp->sd_journal_gh);
+
+ fail_gunlock_ji:
+	if (jindex)
+		gfs_glock_dq_uninit(&ji_gh);
+
+ fail_trans_gl:
+	gfs_glock_put(sdp->sd_trans_gl);
+
+ fail_ji_free:
+	gfs_inode_put(sdp->sd_jiinode);
+	gfs_clear_journals(sdp);
+
+ fail_gunlock_sb:
+	if (super)
+		gfs_glock_dq_uninit(&sb_gh);
+
+ fail_gunlock_live:
+	gfs_glock_dq_uninit(&sdp->sd_live_gh);
+
+ fail_gunlock_mount:
+	gfs_glock_dq_uninit(&mount_gh);
+
+ fail_glockd:
+	clear_bit(SDF_GLOCKD_RUN, &sdp->sd_flags);
+	wake_up(&sdp->sd_reclaim_wchan);
+	while (sdp->sd_glockd_num--)
+		wait_for_completion(&sdp->sd_thread_completion);
+
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_SCAND_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_scand_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+ fail_lockproto:
+	gfs_gl_hash_clear(sdp, TRUE);
+	gfs_lm_unmount(sdp);
+	gfs_clear_dirty_j(sdp);
+	while (invalidate_inodes(sb))
+		yield();
+
+ fail_vfree:
+	vfree(sdp);
+
+ fail:
+	vfs2sdp(sb) = NULL;
+	return error;
+}
+
+/**
+ * gfs_test_bdev_super - 
+ * @sb:
+ * @data:
+ *
+ */
+
+int
+gfs_test_bdev_super(struct super_block *sb, void *data)
+{
+	return (void *)sb->s_bdev == data;	
+}
+
+/**
+ * gfs_test_bdev_super -
+ * @sb:
+ * @data:
+ *
+ */
+
+int
+gfs_set_bdev_super(struct super_block *sb, void *data)
+{
+	sb->s_bdev = data;
+	sb->s_dev = sb->s_bdev->bd_dev;
+	return 0;
+}
+
+/**
+ * gfs_get_sb - 
+ * @fs_type:
+ * @flags:
+ * @dev_name:
+ * @data:
+ *
+ * Rip off of get_sb_bdev().
+ *
+ * Returns: the new superblock
+ */
+
+struct super_block *
+gfs_get_sb(struct file_system_type *fs_type, int flags,
+	   const char *dev_name, void *data)
+{
+	struct block_device *real, *diaper;
+	struct super_block *sb;
+	int error = 0;
+
+	real = open_bdev_excl(dev_name, flags, fs_type);
+	if (IS_ERR(real))
+		return (struct super_block *)real;
+
+	diaper = gfs_diaper_get(real, flags);
+	if (IS_ERR(diaper)) {
+		close_bdev_excl(real);
+		return (struct super_block *)diaper;
+	}
+
+	down(&diaper->bd_mount_sem);
+	sb = sget(fs_type, gfs_test_bdev_super, gfs_set_bdev_super, diaper);
+	up(&diaper->bd_mount_sem);
+	if (IS_ERR(sb))
+		goto out;
+
+	if (sb->s_root) {
+		if ((flags ^ sb->s_flags) & MS_RDONLY) {
+			up_write(&sb->s_umount);
+			deactivate_super(sb);
+			sb = ERR_PTR(-EBUSY);
+		}
+		goto out;
+	} else {
+		char buf[BDEVNAME_SIZE];
+
+		sb->s_flags = flags;
+		strlcpy(sb->s_id, bdevname(real, buf), sizeof(sb->s_id));
+		sb->s_old_blocksize = block_size(real);
+		sb_set_blocksize(sb, sb->s_old_blocksize);
+		set_blocksize(real, sb->s_old_blocksize);
+		error = fill_super(sb, data, (flags & MS_VERBOSE) ? 1 : 0);
+		if (error) {
+			up_write(&sb->s_umount);
+			deactivate_super(sb);
+			sb = ERR_PTR(error);
+		} else
+			sb->s_flags |= MS_ACTIVE;
+	}
+
+	return sb;
+
+ out:
+	gfs_diaper_put(diaper);
+	close_bdev_excl(real);
+	return sb;
+}
+
+/**
+ * gfs_kill_sb - 
+ * @sb:
+ *
+ * Rip off of kill_block_super().
+ *
+ */
+
+void
+gfs_kill_sb(struct super_block *sb)
+{
+	struct block_device *diaper = sb->s_bdev;
+	struct block_device *real = gfs_diaper_2real(diaper);
+	unsigned long bsize = sb->s_old_blocksize;
+
+	generic_shutdown_super(sb);
+	set_blocksize(diaper, bsize);
+	set_blocksize(real, bsize);
+	gfs_diaper_put(diaper);
+	close_bdev_excl(real);
+}
+
+struct file_system_type gfs_fs_type = {
+	.name = "gfs",
+	.fs_flags = FS_REQUIRES_DEV,
+	.get_sb = gfs_get_sb,
+	.kill_sb = gfs_kill_sb,
+	.owner = THIS_MODULE,
+};
diff -pruN linux-2.6.9.orig/fs/gfs/ops_fstype.h linux-2.6.9.debug/fs/gfs/ops_fstype.h
--- linux-2.6.9.orig/fs/gfs/ops_fstype.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_fstype.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,22 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __OPS_FSTYPE_DOT_H__
+#define __OPS_FSTYPE_DOT_H__
+
+int gfs_test_bdev_super(struct super_block *sb, void *data);
+int gfs_set_bdev_super(struct super_block *sb, void *data);
+
+extern struct file_system_type gfs_fs_type;
+
+#endif /* __OPS_FSTYPE_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ops_inode.c linux-2.6.9.debug/fs/gfs/ops_inode.c
--- linux-2.6.9.orig/fs/gfs/ops_inode.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_inode.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1689 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/namei.h>
+#include <linux/utsname.h>
+#include <asm/uaccess.h>
+#include <linux/mm.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl.h>
+#include <cluster/cnxman.h>
+
+#include "gfs.h"
+#include "acl.h"
+#include "bmap.h"
+#include "dio.h"
+#include "dir.h"
+#include "eaops.h"
+#include "eattr.h"
+#include "glock.h"
+#include "inode.h"
+#include "ops_dentry.h"
+#include "ops_inode.h"
+#include "page.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "trans.h"
+#include "unlinked.h"
+
+/**
+ * gfs_create - Create a file
+ * @dir: The directory in which to create the file
+ * @dentry: The dentry of the new file
+ * @mode: The mode of the new file
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_create(struct inode *dir, struct dentry *dentry,
+	   int mode, struct nameidata *nd)
+{
+	struct gfs_inode *dip = vn2ip(dir), *ip;
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_holder d_gh, i_gh;
+	struct inode *inode;
+	int new = TRUE;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	gfs_unlinked_limit(sdp);
+
+	gfs_holder_init(dip->i_gl, 0, 0, &d_gh);
+
+	for (;;) {
+		error = gfs_createi(&d_gh, &dentry->d_name,
+				    GFS_FILE_REG, mode,
+				    &i_gh);
+		if (!error)
+			break;
+		else if (error != -EEXIST || (nd &&
+			 (nd->intent.open.flags & O_EXCL))) {
+			gfs_holder_uninit(&d_gh);
+			return error;
+		}
+
+		error = gfs_lookupi(&d_gh, &dentry->d_name,
+				    FALSE, &i_gh);
+		if (!error) {
+			if (i_gh.gh_gl) {
+				new = FALSE;
+				break;
+			}
+		} else {
+			gfs_holder_uninit(&d_gh);
+			return error;
+		}
+	}
+
+	ip = gl2ip(i_gh.gh_gl);
+
+	if (new) {
+		gfs_trans_end(sdp);
+		if (dip->i_alloc->al_rgd)
+			gfs_inplace_release(dip);
+		gfs_quota_unlock_m(dip);
+		gfs_unlinked_unlock(sdp, dip->i_alloc->al_ul);
+		gfs_alloc_put(dip);
+	}
+
+	gfs_glock_dq_uninit(&d_gh);
+	gfs_glock_dq_uninit(&i_gh);
+
+	inode = gfs_iget(ip, CREATE);
+	gfs_inode_put(ip);
+
+	if (!inode)
+		return -ENOMEM;
+
+	d_instantiate(dentry, inode);
+	if (new)
+		mark_inode_dirty(inode);
+
+	return 0;
+}
+
+/** get_my_node_id - Returns the node id of the local node
+ *
+ *  Returns: the nodeid as int. If we are not part of a cluster
+ *          return 0.
+ */
+static int get_my_nodeid(void) 
+{
+	/* we want to keep the information. */
+ 	static struct kcl_cluster_node *us=NULL;
+	
+	/* if we already have our information, return it.*/
+	if (us) 
+		return us->node_id;
+	
+	/* we are here, i.e. we need to collect information */
+	us = kmalloc(sizeof (struct kcl_cluster_node), GFP_KERNEL);
+	if (kcl_get_node_by_nodeid(0, us)) 
+	{
+		/* request failed. Cleaning up. Returning 0.*/
+		kfree(us);
+		us = NULL;
+		return 0;
+	}
+	/* return collected data */
+	return us->node_id;			
+}
+
+/**
+ * lookup_cdpn_sub_at - Maybe lookup a Context Dependent Pathname
+ * @sdp: the filesystem
+ * @dentry: the original dentry to lookup
+ * @new_dentry: the new dentry, if this was a substitutable path.
+ *
+ * Returns: the new dentry, a ERR_PTR, or NULL
+ */
+
+static struct dentry *
+lookup_cdpn_sub_at(struct gfs_sbd *sdp, struct dentry *dentry)
+{
+	struct dentry *parent, *new = NULL;
+	char *buf;
+
+	buf = kmalloc(2 * __NEW_UTS_LEN + 2, GFP_KERNEL);
+	if (!buf)
+		return ERR_PTR(-ENOMEM);
+
+	parent = dget_parent(dentry);
+
+	if (gfs_filecmp(&dentry->d_name, "@hostname", 9))
+		new = lookup_one_len(system_utsname.nodename,
+				     parent,
+				     strlen(system_utsname.nodename));
+	else if (gfs_filecmp(&dentry->d_name, "@nodeid", 7))
+		new = lookup_one_len(buf, 
+				     parent, 
+				     sprintf(buf, "%s%i", "node", 
+					     get_my_nodeid()));
+	else if (gfs_filecmp(&dentry->d_name, "@mach", 5))
+		new = lookup_one_len(system_utsname.machine,
+				     parent,
+				     strlen(system_utsname.machine));
+	else if (gfs_filecmp(&dentry->d_name, "@os", 3))
+		new = lookup_one_len(system_utsname.sysname,
+				     parent,
+				     strlen(system_utsname.sysname));
+	else if (gfs_filecmp(&dentry->d_name, "@uid", 4))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%u", current->fsuid));
+	else if (gfs_filecmp(&dentry->d_name, "@gid", 4))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%u", current->fsgid));
+	else if (gfs_filecmp(&dentry->d_name, "@sys", 4))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%s_%s",
+					     system_utsname.machine,
+					     system_utsname.sysname));
+	else if (gfs_filecmp(&dentry->d_name, "@jid", 4))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%u",
+					     sdp->sd_lockstruct.ls_jid));
+
+	dput(parent);
+	kfree(buf);
+
+	return new;
+}
+
+/**
+ * lookup_cdpn_sub_brace - Maybe lookup a Context Dependent Pathname
+ * @sdp: the filesystem
+ * @dentry: the original dentry to lookup
+ * @new_dentry: the new dentry, if this was a substitutable path.
+ *
+ * Returns: the new dentry, a ERR_PTR, or NULL
+ */
+
+static struct dentry *
+lookup_cdpn_sub_brace(struct gfs_sbd *sdp, struct dentry *dentry)
+{
+	struct dentry *parent, *new = NULL;
+	char *buf;
+
+	buf = kmalloc(2 * __NEW_UTS_LEN + 2, GFP_KERNEL);
+	if (!buf)
+		return ERR_PTR(-ENOMEM);
+
+	parent = dget_parent(dentry);
+
+	if (gfs_filecmp(&dentry->d_name, "{hostname}", 10))
+		new = lookup_one_len(system_utsname.nodename,
+				     parent,
+				     strlen(system_utsname.nodename));
+	else if (gfs_filecmp(&dentry->d_name, "{nodeid}", 8))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%s%i", "node",
+					     get_my_nodeid()));
+	else if (gfs_filecmp(&dentry->d_name, "{mach}", 6))
+		new = lookup_one_len(system_utsname.machine,
+				     parent,
+				     strlen(system_utsname.machine));
+	else if (gfs_filecmp(&dentry->d_name, "{os}", 4))
+		new = lookup_one_len(system_utsname.sysname,
+				     parent,
+				     strlen(system_utsname.sysname));
+	else if (gfs_filecmp(&dentry->d_name, "{uid}", 5))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%u", current->fsuid));
+	else if (gfs_filecmp(&dentry->d_name, "{gid}", 5))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%u", current->fsgid));
+	else if (gfs_filecmp(&dentry->d_name, "{sys}", 5))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%s_%s",
+					     system_utsname.machine,
+					     system_utsname.sysname));
+	else if (gfs_filecmp(&dentry->d_name, "{jid}", 5))
+		new = lookup_one_len(buf,
+				     parent,
+				     sprintf(buf, "%u",
+					     sdp->sd_lockstruct.ls_jid));
+
+	dput(parent);
+	kfree(buf);
+
+	return new;
+}
+
+/**
+ * gfs_lookup - Look up a filename in a directory and return its inode
+ * @dir: The directory inode
+ * @dentry: The dentry of the new inode
+ * @nd: passed from Linux VFS, ignored by us
+ *
+ * Called by the VFS layer. Lock dir and call gfs_lookupi()
+ *
+ * Returns: errno
+ */
+
+static struct dentry *
+gfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+{
+	struct gfs_inode *dip = vn2ip(dir), *ip;
+	struct gfs_holder d_gh, i_gh;
+	struct inode *inode = NULL;
+	int error;
+
+	atomic_inc(&dip->i_sbd->sd_ops_inode);
+
+	/*  Do Context Dependent Path Name expansion  */
+
+	if (*dentry->d_name.name == '@' && dentry->d_name.len > 1) {
+		struct dentry *new_dentry;
+		new_dentry = lookup_cdpn_sub_at(dip->i_sbd, dentry);
+		if (new_dentry)
+			return new_dentry;
+	} else if (*dentry->d_name.name == '{' && dentry->d_name.len > 2) {
+		struct dentry *new_dentry;
+		new_dentry = lookup_cdpn_sub_brace(dip->i_sbd, dentry);
+		if (new_dentry)
+			return new_dentry;
+	}
+
+	dentry->d_op = &gfs_dops;
+
+	gfs_holder_init(dip->i_gl, 0, 0, &d_gh);
+
+	error = gfs_lookupi(&d_gh, &dentry->d_name, FALSE, &i_gh);
+	if (error) {
+		gfs_holder_uninit(&d_gh);
+		return ERR_PTR(error);
+	}
+
+	if (i_gh.gh_gl) {
+		ip = gl2ip(i_gh.gh_gl);
+
+		gfs_glock_dq_uninit(&d_gh);
+		gfs_glock_dq_uninit(&i_gh);
+
+		inode = gfs_iget(ip, CREATE);
+		gfs_inode_put(ip);
+
+		if (!inode)
+			return ERR_PTR(-ENOMEM);
+	} else
+		gfs_holder_uninit(&d_gh);
+
+	if (inode)
+		return d_splice_alias(inode, dentry);
+	d_add(dentry, inode);
+
+	return NULL;
+}
+
+/**
+ * gfs_link - Link to a file
+ * @old_dentry: The inode to link
+ * @dir: Add link to this directory
+ * @dentry: The name of the link
+ *
+ * Link the inode in "old_dentry" into the directory "dir" with the
+ * name in "dentry".
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+{
+	struct gfs_inode *dip = vn2ip(dir);
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct inode *inode = old_dentry->d_inode;
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_alloc *al = NULL;
+	struct gfs_holder ghs[2];
+	int alloc_required;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	if (ip->i_di.di_type == GFS_FILE_DIR)
+		return -EPERM;
+
+	gfs_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[0]);
+	gfs_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[1]);
+
+	error = gfs_glock_nq_m(2, ghs);
+	if (error)
+		goto fail;
+
+	error = permission(dir, MAY_WRITE | MAY_EXEC, NULL);
+	if (error)
+		goto fail_gunlock;
+
+	error = gfs_dir_search(dip, &dentry->d_name, NULL, NULL);
+	switch (error) {
+	case -ENOENT:
+		break;
+	case 0:
+		error = -EEXIST;
+	default:
+		goto fail_gunlock;
+	}
+
+	if (!dip->i_di.di_nlink) {
+		error = -EINVAL;
+		goto fail_gunlock;
+	}
+	if (dip->i_di.di_entries == (uint32_t)-1) {
+		error = -EFBIG;
+		goto fail_gunlock;
+	}
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+		error = -EPERM;
+		goto fail_gunlock;
+	}
+	if (!ip->i_di.di_nlink) {
+		error = -EINVAL;
+		goto fail_gunlock;
+	}
+	if (ip->i_di.di_nlink == (uint32_t)-1) {
+		error = -EMLINK;
+		goto fail_gunlock;
+	}
+
+	error = gfs_diradd_alloc_required(dip, &dentry->d_name, &alloc_required);
+	if (error)
+		goto fail_gunlock;
+
+	if (alloc_required) {
+		al = gfs_alloc_get(dip);
+
+		error = gfs_quota_lock_m(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+		if (error)
+			goto fail_alloc;
+
+		error = gfs_quota_check(dip, dip->i_di.di_uid, dip->i_di.di_gid);
+		if (error)
+			goto fail_gunlock_q;
+
+		al->al_requested_meta = sdp->sd_max_dirres;
+
+		error = gfs_inplace_reserve(dip);
+		if (error)
+			goto fail_gunlock_q;
+
+		/* Trans may require:
+		   two dinode blocks, directory modifications to add an entry,
+		   RG bitmap blocks to allocate from, and quota change */
+
+		error = gfs_trans_begin(sdp,
+					2 + sdp->sd_max_dirres +
+					al->al_rgd->rd_ri.ri_length,
+					1);
+		if (error)
+			goto fail_ipres;
+	} else {
+		/*  Trans may require:
+		    Two dinode blocks and a leaf block.  */
+
+		error = gfs_trans_begin(sdp, 3, 0);
+		if (error)
+			goto fail_ipres;
+	}
+
+	error = gfs_dir_add(dip, &dentry->d_name, &ip->i_num, ip->i_di.di_type);
+	if (error)
+		goto fail_end_trans;
+
+	error = gfs_change_nlink(ip, +1);
+	if (error)
+		goto fail_end_trans;
+
+	gfs_trans_end(sdp);
+
+	if (alloc_required) {
+		gfs_assert_warn(sdp, al->al_alloced_meta);
+		gfs_inplace_release(dip);
+		gfs_quota_unlock_m(dip);
+		gfs_alloc_put(dip);
+	}
+
+	gfs_glock_dq_m(2, ghs);
+
+	gfs_holder_uninit(&ghs[0]);
+	gfs_holder_uninit(&ghs[1]);
+
+	atomic_inc(&inode->i_count);
+
+	d_instantiate(dentry, inode);
+	mark_inode_dirty(inode);
+
+	return 0;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_ipres:
+	if (alloc_required)
+		gfs_inplace_release(dip);
+
+ fail_gunlock_q:
+	if (alloc_required)
+		gfs_quota_unlock_m(dip);
+
+ fail_alloc:
+	if (alloc_required)
+		gfs_alloc_put(dip);
+
+ fail_gunlock:
+	gfs_glock_dq_m(2, ghs);
+
+ fail:
+	gfs_holder_uninit(&ghs[0]);
+	gfs_holder_uninit(&ghs[1]);
+
+	return error;
+}
+
+/**
+ * gfs_unlink - Unlink a file
+ * @dir: The inode of the directory containing the file to unlink
+ * @dentry: The file itself
+ *
+ * Unlink a file.  Call gfs_unlinki()
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct gfs_inode *dip = vn2ip(dir);
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_inode *ip = vn2ip(dentry->d_inode);
+	struct gfs_holder ghs[2];
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	gfs_unlinked_limit(sdp);
+
+	gfs_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[0]);
+	gfs_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[1]);
+
+	error = gfs_glock_nq_m(2, ghs);
+	if (error)
+		goto fail;
+
+	error = gfs_unlink_ok(dip, &dentry->d_name, ip);
+	if (error)
+		goto fail_gunlock;
+
+	/*  Trans may require:
+	    Two dinode blocks and one modified directory leaf block
+	    and one unlinked tag.  */
+
+	error = gfs_trans_begin(sdp, 3, 1);
+	if (error)
+		goto fail_gunlock;
+
+	error = gfs_unlinki(dip, &dentry->d_name, ip);
+	if (error)
+		goto fail_end_trans;
+
+	gfs_trans_end(sdp);
+
+	gfs_glock_dq_m(2, ghs);
+
+	gfs_holder_uninit(&ghs[0]);
+	gfs_holder_uninit(&ghs[1]);
+
+	return 0;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_gunlock:
+	gfs_glock_dq_m(2, ghs);
+
+ fail:
+	gfs_holder_uninit(&ghs[0]);
+	gfs_holder_uninit(&ghs[1]);
+
+	return error;
+}
+
+/**
+ * gfs_symlink - Create a symlink
+ * @dir: The directory to create the symlink in
+ * @dentry: The dentry to put the symlink in
+ * @symname: The thing which the link points to
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+	struct gfs_inode *dip = vn2ip(dir), *ip;
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_holder d_gh, i_gh;
+	struct inode *inode;
+	struct buffer_head *dibh;
+	int size;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	gfs_unlinked_limit(sdp);
+
+	/* Must be stuffed with a null terminator for gfs_follow_link() */
+	size = strlen(symname);
+	if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode) - 1)
+	        return -ENAMETOOLONG;
+
+	gfs_holder_init(dip->i_gl, 0, 0, &d_gh);
+
+	error = gfs_createi(&d_gh, &dentry->d_name,
+			    GFS_FILE_LNK, S_IFLNK | S_IRWXUGO,
+			    &i_gh);
+	if (error) {
+		gfs_holder_uninit(&d_gh);
+		return error;
+	}
+
+	ip = gl2ip(i_gh.gh_gl);
+
+	ip->i_di.di_size = size;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+
+	if (!gfs_assert_withdraw(sdp, !error)) {
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		memcpy(dibh->b_data + sizeof(struct gfs_dinode), symname, size);
+		brelse(dibh);
+	}
+
+	gfs_trans_end(sdp);
+	if (dip->i_alloc->al_rgd)
+		gfs_inplace_release(dip);
+	gfs_quota_unlock_m(dip);
+	gfs_unlinked_unlock(sdp, dip->i_alloc->al_ul);
+	gfs_alloc_put(dip);
+
+	gfs_glock_dq_uninit(&d_gh);
+	gfs_glock_dq_uninit(&i_gh);
+
+	inode = gfs_iget(ip, CREATE);
+	gfs_inode_put(ip);
+
+	if (!inode)
+		return -ENOMEM;
+
+	d_instantiate(dentry, inode);
+	mark_inode_dirty(inode);
+
+	return 0;
+}
+
+/**
+ * gfs_mkdir - Make a directory
+ * @dir: The parent directory of the new one
+ * @dentry: The dentry of the new directory
+ * @mode: The mode of the new directory
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct gfs_inode *dip = vn2ip(dir), *ip;
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_holder d_gh, i_gh;
+	struct inode *inode;
+	struct buffer_head *dibh;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	gfs_unlinked_limit(sdp);
+
+	gfs_holder_init(dip->i_gl, 0, 0, &d_gh);
+
+	error = gfs_createi(&d_gh, &dentry->d_name,
+			    GFS_FILE_DIR, S_IFDIR | mode,
+			    &i_gh);
+	if (error) {
+		gfs_holder_uninit(&d_gh);
+		return error;
+	}
+
+	ip = gl2ip(i_gh.gh_gl);
+
+	ip->i_di.di_nlink = 2;
+	ip->i_di.di_size = sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode);
+	ip->i_di.di_flags |= GFS_DIF_JDATA;
+	ip->i_di.di_payload_format = GFS_FORMAT_DE;
+	ip->i_di.di_entries = 2;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+
+	if (!gfs_assert_withdraw(sdp, !error)) {
+		struct gfs_dinode *di = (struct gfs_dinode *)dibh->b_data;
+		struct gfs_dirent *dent;
+
+		gfs_dirent_alloc(ip, dibh, 1, &dent);
+
+		dent->de_inum = di->di_num; /* already GFS endian */
+		dent->de_hash = gfs_dir_hash(".", 1);
+		dent->de_hash = cpu_to_gfs32(dent->de_hash);
+		dent->de_type = cpu_to_gfs16(GFS_FILE_DIR);
+		memcpy((char *) (dent + 1), ".", 1);
+		di->di_entries = cpu_to_gfs32(1);
+
+		gfs_dirent_alloc(ip, dibh, 2, &dent);
+
+		gfs_inum_out(&dip->i_num, (char *) &dent->de_inum);
+		dent->de_hash = gfs_dir_hash("..", 2);
+		dent->de_hash = cpu_to_gfs32(dent->de_hash);
+		dent->de_type = cpu_to_gfs16(GFS_FILE_DIR);
+		memcpy((char *) (dent + 1), "..", 2);
+
+		gfs_dinode_out(&ip->i_di, (char *)di);
+
+		brelse(dibh);
+	}
+
+	error = gfs_change_nlink(dip, +1);
+	gfs_assert_withdraw(sdp, !error); /* dip already pinned */
+
+	gfs_trans_end(sdp);
+	if (dip->i_alloc->al_rgd)
+		gfs_inplace_release(dip);
+	gfs_quota_unlock_m(dip);
+	gfs_unlinked_unlock(sdp, dip->i_alloc->al_ul);
+	gfs_alloc_put(dip);
+
+	gfs_glock_dq_uninit(&d_gh);
+	gfs_glock_dq_uninit(&i_gh);
+
+	inode = gfs_iget(ip, CREATE);
+	gfs_inode_put(ip);
+
+	if (!inode)
+		return -ENOMEM;
+
+	d_instantiate(dentry, inode);
+	mark_inode_dirty(inode);
+
+	return 0;
+}
+
+/**
+ * gfs_rmdir - Remove a directory
+ * @dir: The parent directory of the directory to be removed
+ * @dentry: The dentry of the directory to remove
+ *
+ * Remove a directory. Call gfs_rmdiri()
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct gfs_inode *dip = vn2ip(dir);
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_inode *ip = vn2ip(dentry->d_inode);
+	struct gfs_holder ghs[2];
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	gfs_unlinked_limit(sdp);
+
+	gfs_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[0]);
+	gfs_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[1]);
+
+	error = gfs_glock_nq_m(2, ghs);
+	if (error)
+		goto fail;
+
+	error = gfs_unlink_ok(dip, &dentry->d_name, ip);
+	if (error)
+		goto fail_gunlock;
+
+	if (ip->i_di.di_entries < 2) {
+		if (gfs_consist_inode(ip))
+			gfs_dinode_print(&ip->i_di);
+		error = -EIO;
+		goto fail_gunlock;
+	}
+	if (ip->i_di.di_entries > 2) {
+		error = -ENOTEMPTY;
+		goto fail_gunlock;
+	}
+
+	/* Trans may require:
+	   Two dinode blocks, one directory leaf block containing the
+	   entry to be rmdired, two leaf blocks containing . and .. of
+	   the directory being rmdired, and one unlinked tag */
+
+	error = gfs_trans_begin(sdp, 5, 1);
+	if (error)
+		goto fail_gunlock;
+
+	error = gfs_rmdiri(dip, &dentry->d_name, ip);
+	if (error)
+		goto fail_end_trans;
+
+	gfs_trans_end(sdp);
+
+	gfs_glock_dq_m(2, ghs);
+
+	gfs_holder_uninit(&ghs[0]);
+	gfs_holder_uninit(&ghs[1]);
+
+	return 0;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_gunlock:
+	gfs_glock_dq_m(2, ghs);
+
+ fail:
+	gfs_holder_uninit(&ghs[0]);
+	gfs_holder_uninit(&ghs[1]);
+
+	return error;
+}
+
+/**
+ * gfs_mknod - Make a special file
+ * @dir: The directory in which the special file will reside
+ * @dentry: The dentry of the special file
+ * @mode: The mode of the special file
+ * @rdev: The device specification of the special file
+ *
+ */
+
+static int
+gfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+	struct gfs_inode *dip = vn2ip(dir), *ip;
+	struct gfs_sbd *sdp = dip->i_sbd;
+	struct gfs_holder d_gh, i_gh;
+	struct inode *inode;
+	struct buffer_head *dibh;
+	uint16_t type = 0;
+	uint32_t major = 0, minor = 0;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	gfs_unlinked_limit(sdp);
+
+	switch (mode & S_IFMT) {
+	case S_IFBLK:
+		type = GFS_FILE_BLK;
+		major = MAJOR(dev);
+		minor = MINOR(dev);
+		break;
+	case S_IFCHR:
+		type = GFS_FILE_CHR;
+		major = MAJOR(dev);
+		minor = MINOR(dev);
+		break;
+	case S_IFIFO:
+		type = GFS_FILE_FIFO;
+		break;
+	case S_IFSOCK:
+		type = GFS_FILE_SOCK;
+		break;
+	default:
+		printk("GFS: fsid=%s: mknod() with invalid type (%d)\n",
+		       sdp->sd_fsname, mode);
+		return -EINVAL;
+	};
+
+	gfs_holder_init(dip->i_gl, 0, 0, &d_gh);
+
+	error = gfs_createi(&d_gh, &dentry->d_name,
+			    type, mode,
+			    &i_gh);
+	if (error) {
+		gfs_holder_uninit(&d_gh);
+		return error;
+	}
+
+	ip = gl2ip(i_gh.gh_gl);
+
+	ip->i_di.di_major = major;
+	ip->i_di.di_minor = minor;
+
+	error = gfs_get_inode_buffer(ip, &dibh);
+
+	if (!gfs_assert_withdraw(sdp, !error)) {
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+	}
+
+	gfs_trans_end(sdp);
+	if (dip->i_alloc->al_rgd)
+		gfs_inplace_release(dip);
+	gfs_quota_unlock_m(dip);
+	gfs_unlinked_unlock(sdp, dip->i_alloc->al_ul);
+	gfs_alloc_put(dip);
+
+	gfs_glock_dq_uninit(&d_gh);
+	gfs_glock_dq_uninit(&i_gh);
+
+	inode = gfs_iget(ip, CREATE);
+	gfs_inode_put(ip);
+
+	if (!inode)
+		return -ENOMEM;
+
+	d_instantiate(dentry, inode);
+	mark_inode_dirty(inode);
+
+	return 0;
+}
+
+/**
+ * gfs_rename - Rename a file
+ * @odir: Parent directory of old file name
+ * @odentry: The old dentry of the file
+ * @ndir: Parent directory of new file name
+ * @ndentry: The new dentry of the file
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_rename(struct inode *odir, struct dentry *odentry,
+	   struct inode *ndir, struct dentry *ndentry)
+{
+	struct gfs_inode *odip = vn2ip(odir);
+	struct gfs_inode *ndip = vn2ip(ndir);
+	struct gfs_inode *ip = vn2ip(odentry->d_inode);
+	struct gfs_inode *nip = NULL;
+	struct gfs_sbd *sdp = odip->i_sbd;
+	struct qstr name;
+	struct gfs_alloc *al;
+	struct gfs_holder ghs[4], r_gh;
+	unsigned int num_gh;
+	int dir_rename = FALSE;
+	int alloc_required;
+	unsigned int x;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	gfs_unlinked_limit(sdp);
+
+	if (ndentry->d_inode) {
+		nip = vn2ip(ndentry->d_inode);
+		if (ip == nip)
+			return 0;
+	}
+
+	/*  Make sure we aren't trying to move a dirctory into it's subdir  */
+
+	if (ip->i_di.di_type == GFS_FILE_DIR && odip != ndip) {
+		dir_rename = TRUE;
+
+		error = gfs_glock_nq_init(sdp->sd_rename_gl,
+					  LM_ST_EXCLUSIVE, 0,
+					  &r_gh);
+		if (error)
+			return error;
+
+		error = gfs_ok_to_move(ip, ndip);
+		if (error)
+			goto fail;
+	}
+
+	gfs_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[0]);
+	gfs_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[1]);
+	num_gh = 2;
+
+	if (nip)
+		gfs_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[num_gh++]);
+
+	if (dir_rename)
+		gfs_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ghs[num_gh++]);
+
+	error = gfs_glock_nq_m(num_gh, ghs);
+	if (error)
+		goto fail_uninit;
+
+	/*  Check out the old directory  */
+
+	error = gfs_unlink_ok(odip, &odentry->d_name, ip);
+	if (error)
+		goto fail_gunlock;
+
+	/*  Check out the new directory  */
+
+	if (nip) {
+		error = gfs_unlink_ok(ndip, &ndentry->d_name, nip);
+		if (error)
+			goto fail_gunlock;
+
+		if (nip->i_di.di_type == GFS_FILE_DIR) {
+			if (nip->i_di.di_entries < 2) {
+				if (gfs_consist_inode(nip))
+					gfs_dinode_print(&nip->i_di);
+				error = -EIO;
+				goto fail_gunlock;
+			}
+			if (nip->i_di.di_entries > 2) {
+				error = -ENOTEMPTY;
+				goto fail_gunlock;
+			}
+		}
+	} else {
+		error = permission(ndir, MAY_WRITE | MAY_EXEC, NULL);
+		if (error)
+			goto fail_gunlock;
+
+		error = gfs_dir_search(ndip, &ndentry->d_name, NULL, NULL);
+		switch (error) {
+		case -ENOENT:
+			error = 0;
+			break;
+		case 0:
+			error = -EEXIST;
+		default:
+			goto fail_gunlock;
+		};
+
+		if (odip != ndip) {
+			if (!ndip->i_di.di_nlink) {
+				error = -EINVAL;
+				goto fail_gunlock;
+			}
+			if (ndip->i_di.di_entries == (uint32_t)-1) {
+				error = -EFBIG;
+				goto fail_gunlock;
+			}
+			if (ip->i_di.di_type == GFS_FILE_DIR &&
+			    ndip->i_di.di_nlink == (uint32_t)-1) {
+				error = -EMLINK;
+				goto fail_gunlock;
+			}
+		}
+	}
+
+	error = gfs_diradd_alloc_required(ndip, &ndentry->d_name, &alloc_required);
+	if (error)
+		goto fail_gunlock;
+
+	if (alloc_required) {
+		al = gfs_alloc_get(ndip);
+
+		error = gfs_quota_lock_m(ndip,
+					    NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+		if (error)
+			goto fail_alloc;
+
+		error = gfs_quota_check(ndip, ndip->i_di.di_uid, ndip->i_di.di_gid);
+		if (error)
+			goto fail_gunlock_q;
+
+		al->al_requested_meta = sdp->sd_max_dirres;
+
+		error = gfs_inplace_reserve(ndip);
+		if (error)
+			goto fail_gunlock_q;
+
+		/* Trans may require:
+		   Dinodes for the srcdir, srcino, dstdir, dstino.  Blocks for
+		   adding the entry to dstdir.  RG bitmaps for that allocation.
+		   One leaf block in the srcdir for removal of the entry.
+		   One leaf block for changing .. in srcino (if it's a directory).
+		   Two leaf blocks for removing . and .. from dstino (if it exists
+		   and it's a directory), one unlinked tag, and one quota block. */
+
+		error = gfs_trans_begin(sdp,
+					8 + sdp->sd_max_dirres +
+					al->al_rgd->rd_ri.ri_length,
+					2);
+		if (error)
+			goto fail_ipres;
+	} else {
+		/* Trans may require:
+		   Dinodes for the srcdir, srcino, dstdir, dstino.  One block for
+		   adding the entry to dstdir.
+		   One leaf block in the srcdir for removal of the entry.
+		   One leaf block for changing .. in srcino (if it's a directory).
+		   Two leaf blocks for removing . and .. from dstino (if it exists
+		   and it's a directory), and one unlinked tag. */
+
+		error = gfs_trans_begin(sdp, 9, 1);
+		if (error)
+			goto fail_ipres;
+	}
+
+	/*  Remove the target file, if it exists  */
+
+	if (nip) {
+		if (nip->i_di.di_type == GFS_FILE_DIR)
+			error = gfs_rmdiri(ndip, &ndentry->d_name, nip);
+		else
+			error = gfs_unlinki(ndip, &ndentry->d_name, nip);
+
+		if (error)
+			goto fail_end_trans;
+	}
+
+	if (dir_rename) {
+		error = gfs_change_nlink(ndip, +1);
+		if (error)
+			goto fail_end_trans;
+		error = gfs_change_nlink(odip, -1);
+		if (error)
+			goto fail_end_trans;
+
+		name.len = 2;
+		name.name = "..";
+
+		error = gfs_dir_mvino(ip, &name, &ndip->i_num, GFS_FILE_DIR);
+		if (error)
+			goto fail_end_trans;
+	}
+
+	error = gfs_dir_del(odip, &odentry->d_name);
+	if (error)
+		goto fail_end_trans;
+
+	error = gfs_dir_add(ndip, &ndentry->d_name, &ip->i_num, ip->i_di.di_type);
+	if (error)
+		goto fail_end_trans;
+
+	if (dir_rename)
+		gfs_trans_add_gl(sdp->sd_rename_gl);
+
+	gfs_trans_end(sdp);
+
+	if (alloc_required) {
+		/*  Don't check al->al_alloced_meta and friends.  */
+		gfs_inplace_release(ndip);
+		gfs_quota_unlock_m(ndip);
+		gfs_alloc_put(ndip);
+	}
+
+	gfs_glock_dq_m(num_gh, ghs);
+
+	for (x = 0; x < num_gh; x++)
+		gfs_holder_uninit(&ghs[x]);
+
+	if (dir_rename)
+		gfs_glock_dq_uninit(&r_gh);
+
+	return 0;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_ipres:
+	if (alloc_required)
+		gfs_inplace_release(ndip);
+
+ fail_gunlock_q:
+	if (alloc_required)
+		gfs_quota_unlock_m(ndip);
+
+ fail_alloc:
+	if (alloc_required)
+		gfs_alloc_put(ndip);
+
+ fail_gunlock:
+	gfs_glock_dq_m(num_gh, ghs);
+
+ fail_uninit:
+	for (x = 0; x < num_gh; x++)
+		gfs_holder_uninit(&ghs[x]);
+
+ fail:
+	if (dir_rename)
+		gfs_glock_dq_uninit(&r_gh);
+
+	return error;
+}
+
+/**
+ * gfs_readlink - Read the value of a symlink
+ * @dentry: the symlink
+ * @buf: the buffer to read the symlink data into
+ * @size: the size of the buffer
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_readlink(struct dentry *dentry, char *user_buf, int user_size)
+{
+	struct gfs_inode *ip = vn2ip(dentry->d_inode);
+	char array[GFS_FAST_NAME_SIZE], *buf = array;
+	unsigned int len = GFS_FAST_NAME_SIZE;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_inode);
+
+	error = gfs_readlinki(ip, &buf, &len);
+	if (error)
+		return error;
+
+	if (user_size > len - 1)
+		user_size = len - 1;
+
+	if (copy_to_user(user_buf, buf, user_size))
+		error = -EFAULT;
+	else
+		error = user_size;
+
+	if (buf != array)
+		kfree(buf);
+
+	return error;
+}
+
+/**
+ * gfs_follow_link - Follow a symbolic link
+ * @dentry: The dentry of the link
+ * @nd: Data that we pass to vfs_follow_link()
+ *
+ * This can handle symlinks of any size. It is optimised for symlinks
+ * under GFS_FAST_NAME_SIZE.
+ *
+ * Returns: 0 on success or error code
+ */
+
+static int
+gfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	struct gfs_inode *ip = vn2ip(dentry->d_inode);
+	char array[GFS_FAST_NAME_SIZE], *buf = array;
+	unsigned int len = GFS_FAST_NAME_SIZE;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_inode);
+
+	error = gfs_readlinki(ip, &buf, &len);
+	if (!error) {
+		error = vfs_follow_link(nd, buf);
+		if (buf != array)
+			kfree(buf);
+	}
+
+	return error;
+}
+
+/**
+ * gfs_permission_i -
+ * @inode:
+ * @mask:
+ * @nd: ignored
+ *
+ * Shamelessly ripped from ext3
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_permission_i(struct inode *inode, int mask, struct nameidata *nd)
+{
+	int mode = inode->i_mode;
+      
+	/* Nobody gets write access to a read-only fs */
+	if ((mask & MAY_WRITE) &&
+	    IS_RDONLY(inode) &&
+	    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+		return -EROFS;
+
+	/* Nobody gets write access to an immutable file */
+	if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
+		return -EACCES;
+
+	if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else if (IS_POSIXACL(inode)) {
+		struct posix_acl *acl = NULL;
+		int error;
+
+		/* The access ACL cannot grant access if the group class
+		   permission bits don't contain all requested permissions. */
+		if (((mode >> 3) & mask & S_IRWXO) != mask)
+			goto check_groups;
+
+		error = gfs_acl_get(vn2ip(inode), TRUE, &acl);
+		if (error)
+			return error;
+
+		if (acl) {
+			int error = posix_acl_permission(inode, acl, mask);
+			posix_acl_release(acl);
+			if (error == -EACCES)
+				goto check_capabilities;
+			return error;
+		} else
+			goto check_groups;
+	} else {
+	check_groups:
+		if (in_group_p(inode->i_gid))
+			mode >>= 3;
+	}
+
+	if ((mode & mask & S_IRWXO) == mask)
+		return 0;
+      
+ check_capabilities:
+	/* Allowed to override Discretionary Access Control? */
+	if (!(mask & MAY_EXEC) ||
+	    (inode->i_mode & S_IXUGO) ||
+	    S_ISDIR(inode->i_mode))
+		if (capable(CAP_DAC_OVERRIDE))
+			return 0;
+
+	/* Read and search granted if capable(CAP_DAC_READ_SEARCH) */
+	if (capable(CAP_DAC_READ_SEARCH) &&
+	    ((mask == MAY_READ) ||
+	     (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))))
+		return 0;
+
+	return -EACCES;
+}
+
+/**
+ * gfs_permission -
+ * @inode:
+ * @mask:
+ * @nd: passed from Linux VFS, ignored by us
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_holder i_gh;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_inode);
+
+	error = gfs_glock_nq_init(ip->i_gl,
+				  LM_ST_SHARED, LM_FLAG_ANY,
+				  &i_gh);
+	if (error)
+		return error;
+
+	error = gfs_permission_i(inode, mask, nd);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_setattr - Change attributes on an inode
+ * @dentry: The dentry which is changing
+ * @attr: The structure describing the change
+ *
+ * The VFS layer wants to change one or more of an inodes attributes.  Write
+ * that change out to disk.
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_holder i_gh;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_inode);
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+		error = -EPERM;
+		goto fail;
+	}
+
+	error = inode_change_ok(inode, attr);
+	if (error)
+		goto fail;
+
+	if (attr->ia_valid & ATTR_SIZE) {
+		/* Is there a reason for this??
+		error = permission(inode, MAY_WRITE, NULL);
+		if (error)
+			goto fail;
+		*/
+
+		if (attr->ia_size != ip->i_di.di_size) {
+			error = vmtruncate(inode, attr->ia_size);
+			if (error)
+				goto fail;
+		}
+
+		error = gfs_truncatei(ip, attr->ia_size, gfs_truncator_page);
+		if (error)
+			goto fail;
+
+		if ((sdp->sd_vfs->s_flags & MS_SYNCHRONOUS) &&
+		    !gfs_is_jdata(ip))
+			i_gh.gh_flags |= GL_SYNC;
+	}
+
+	else if (attr->ia_valid & (ATTR_UID | ATTR_GID)) {
+		struct gfs_alloc *al;
+		struct buffer_head *dibh;
+		uint32_t ouid, ogid, nuid, ngid;
+
+		ouid = ip->i_di.di_uid;
+		ogid = ip->i_di.di_gid;
+		nuid = attr->ia_uid;
+		ngid = attr->ia_gid;
+
+		if (!(attr->ia_valid & ATTR_UID) || ouid == nuid)
+			ouid = nuid = NO_QUOTA_CHANGE;
+		if (!(attr->ia_valid & ATTR_GID) || ogid == ngid)
+			ogid = ngid = NO_QUOTA_CHANGE;
+
+		al = gfs_alloc_get(ip);
+
+		error = gfs_quota_lock_m(ip, nuid, ngid);
+		if (error)
+			goto fail_alloc;
+
+		if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) {
+			error = gfs_quota_check(ip, nuid, ngid);
+			if (error)
+				goto fail_gunlock_q;
+		}
+
+		/* Trans may require:
+		   one dinode block and one quota change block */
+
+		error = gfs_trans_begin(sdp, 1, 1);
+		if (error)
+			goto fail_gunlock_q;
+
+		error = gfs_get_inode_buffer(ip, &dibh);
+		if (error)
+			goto fail_end_trans;
+
+		if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) {
+			gfs_trans_add_quota(sdp, -ip->i_di.di_blocks,
+					    ouid, ogid);
+			gfs_trans_add_quota(sdp, ip->i_di.di_blocks,
+					    nuid, ngid);
+		}
+
+		inode_setattr(inode, attr);
+		gfs_inode_attr_out(ip);
+
+		gfs_trans_add_bh(ip->i_gl, dibh);
+		gfs_dinode_out(&ip->i_di, dibh->b_data);
+		brelse(dibh);
+
+		gfs_trans_end(sdp);
+
+		gfs_quota_unlock_m(ip);
+		gfs_alloc_put(ip);
+	}
+
+	else if ((attr->ia_valid & ATTR_MODE) && IS_POSIXACL(inode)) {
+		error = gfs_acl_chmod(ip, attr);
+		if (error)
+			goto fail;
+	}
+
+	else {
+		error = gfs_setattr_simple(ip, attr);
+		if (error)
+			goto fail;
+	}
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	mark_inode_dirty(inode);
+
+	return error;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_gunlock_q:
+	gfs_quota_unlock_m(ip);
+
+ fail_alloc:
+	gfs_alloc_put(ip);
+
+ fail:
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_getattr - Read out an inode's attributes
+ * @mnt: ?
+ * @dentry: The dentry to stat
+ * @stat: The inode's stats
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+	struct inode *inode = dentry->d_inode;
+	struct gfs_inode *ip = vn2ip(inode);
+	struct gfs_holder gh;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_inode);
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
+	if (!error)
+	{
+		generic_fillattr(inode, stat);
+		gfs_glock_dq_uninit(&gh);
+	}
+
+	return error;
+}
+
+/**
+ * gfs_setxattr - Set (or create or replace) an inode's extended attribute
+ * @dentry: 
+ * @name: 
+ * @data: 
+ * @size: 
+ * @flags: 
+ *
+ * Returns: errno
+ */
+
+int
+gfs_setxattr(struct dentry *dentry, const char *name,
+	     const void *data, size_t size,
+	     int flags)
+{
+	struct gfs_ea_request er;
+
+	atomic_inc(&vfs2sdp(dentry->d_inode->i_sb)->sd_ops_inode);
+
+	memset(&er, 0, sizeof(struct gfs_ea_request));
+	er.er_type = gfs_ea_name2type(name, &er.er_name);
+	if (er.er_type == GFS_EATYPE_UNUSED)
+	        return -EOPNOTSUPP;
+	er.er_data = (char *)data;
+	er.er_name_len = strlen(er.er_name);
+	er.er_data_len = size;
+	er.er_flags = flags;
+
+	return gfs_ea_set(vn2ip(dentry->d_inode), &er);
+}
+
+/**
+ * gfs_getxattr -
+ * @dentry:
+ * @name:
+ * @data:
+ * @size:
+ *
+ * Returns: The number of bytes put into data, or -errno
+ */
+
+ssize_t
+gfs_getxattr(struct dentry *dentry, const char *name,
+	     void *data, size_t size)
+{
+	struct gfs_ea_request er;
+
+	atomic_inc(&vfs2sdp(dentry->d_inode->i_sb)->sd_ops_inode);
+
+	memset(&er, 0, sizeof(struct gfs_ea_request));
+	er.er_type = gfs_ea_name2type(name, &er.er_name);
+	if (er.er_type == GFS_EATYPE_UNUSED)
+	        return -EOPNOTSUPP;
+	er.er_data = data;
+	er.er_name_len = strlen(er.er_name);
+	er.er_data_len = size;
+
+	return gfs_ea_get(vn2ip(dentry->d_inode), &er);
+}
+
+/**
+ * gfs_listxattr - 
+ * @dentry:
+ * @buffer:
+ * @size:
+ *
+ * Returns: The number of bytes put into data, or -errno
+ */
+
+ssize_t
+gfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+	struct gfs_ea_request er;
+
+	atomic_inc(&vfs2sdp(dentry->d_inode->i_sb)->sd_ops_inode);
+
+	memset(&er, 0, sizeof(struct gfs_ea_request));
+	er.er_data = (size) ? buffer : NULL;
+	er.er_data_len = size;
+
+	return gfs_ea_list(vn2ip(dentry->d_inode), &er);
+}
+
+/**
+ * gfs_removexattr -
+ * @dentry:
+ * @name:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_removexattr(struct dentry *dentry, const char *name)
+{
+	struct gfs_ea_request er;
+
+	atomic_inc(&vfs2sdp(dentry->d_inode->i_sb)->sd_ops_inode);
+
+	memset(&er, 0, sizeof(struct gfs_ea_request));
+	er.er_type = gfs_ea_name2type(name, &er.er_name);
+	if (er.er_type == GFS_EATYPE_UNUSED)
+	        return -EOPNOTSUPP;
+	er.er_name_len = strlen(er.er_name);
+
+	return gfs_ea_remove(vn2ip(dentry->d_inode), &er);
+}
+
+struct inode_operations gfs_file_iops = {
+	.permission = gfs_permission,
+	.setattr = gfs_setattr,
+	.getattr = gfs_getattr,
+	.setxattr = gfs_setxattr,
+	.getxattr = gfs_getxattr,
+	.listxattr = gfs_listxattr,
+	.removexattr = gfs_removexattr,
+};
+
+struct inode_operations gfs_dev_iops = {
+	.permission = gfs_permission,
+	.setattr = gfs_setattr,
+	.getattr = gfs_getattr,
+	.setxattr = gfs_setxattr,
+	.getxattr = gfs_getxattr,
+	.listxattr = gfs_listxattr,
+	.removexattr = gfs_removexattr,
+};
+
+struct inode_operations gfs_dir_iops = {
+	.create = gfs_create,
+	.lookup = gfs_lookup,
+	.link = gfs_link,
+	.unlink = gfs_unlink,
+	.symlink = gfs_symlink,
+	.mkdir = gfs_mkdir,
+	.rmdir = gfs_rmdir,
+	.mknod = gfs_mknod,
+	.rename = gfs_rename,
+	.permission = gfs_permission,
+	.setattr = gfs_setattr,
+	.getattr = gfs_getattr,
+	.setxattr = gfs_setxattr,
+	.getxattr = gfs_getxattr,
+	.listxattr = gfs_listxattr,
+	.removexattr = gfs_removexattr,
+};
+
+struct inode_operations gfs_symlink_iops = {
+	.readlink = gfs_readlink,
+	.follow_link = gfs_follow_link,
+	.permission = gfs_permission,
+	.setattr = gfs_setattr,
+	.getattr = gfs_getattr,
+	.setxattr = gfs_setxattr,
+	.getxattr = gfs_getxattr,
+	.listxattr = gfs_listxattr,
+	.removexattr = gfs_removexattr,
+};
+
diff -pruN linux-2.6.9.orig/fs/gfs/ops_inode.h linux-2.6.9.debug/fs/gfs/ops_inode.h
--- linux-2.6.9.orig/fs/gfs/ops_inode.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_inode.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,22 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __OPS_INODE_DOT_H__
+#define __OPS_INODE_DOT_H__
+
+extern struct inode_operations gfs_file_iops;
+extern struct inode_operations gfs_dir_iops;
+extern struct inode_operations gfs_symlink_iops;
+extern struct inode_operations gfs_dev_iops;
+
+#endif /* __OPS_INODE_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ops_super.c linux-2.6.9.debug/fs/gfs/ops_super.c
--- linux-2.6.9.orig/fs/gfs/ops_super.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_super.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,453 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/vmalloc.h>
+#include <linux/statfs.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "glock.h"
+#include "inode.h"
+#include "lm.h"
+#include "log.h"
+#include "ops_super.h"
+#include "page.h"
+#include "proc.h"
+#include "quota.h"
+#include "recovery.h"
+#include "rgrp.h"
+#include "super.h"
+#include "mount.h"
+
+/**
+ * gfs_write_inode - Make sure the inode is stable on the disk
+ * @inode: The inode
+ * @sync: synchronous write flag
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_write_inode(struct inode *inode, int sync)
+{
+	struct gfs_inode *ip = vn2ip(inode);
+
+	atomic_inc(&ip->i_sbd->sd_ops_super);
+
+	if (ip && sync)
+		gfs_log_flush_glock(ip->i_gl);
+
+	return 0;
+}
+
+/**
+ * gfs_put_inode - put an inode
+ * @inode: The inode
+ *
+ * If i_nlink is zero, any dirty data for the inode is thrown away.
+ * If a process on another machine has the file open, it may need that
+ * data.  So, sync it out.
+ */
+
+static void
+gfs_put_inode(struct inode *inode)
+{
+	struct gfs_sbd *sdp = vfs2sdp(inode->i_sb);
+	struct gfs_inode *ip = vn2ip(inode);
+
+	atomic_inc(&sdp->sd_ops_super);
+
+	if (ip &&
+	    !inode->i_nlink &&
+	    S_ISREG(inode->i_mode) &&
+	    !sdp->sd_args.ar_localcaching)
+		gfs_sync_page_i(inode, DIO_START | DIO_WAIT);
+}
+
+/**
+ * gfs_put_super - Unmount the filesystem
+ * @sb: The VFS superblock
+ *
+ */
+
+static void
+gfs_put_super(struct super_block *sb)
+{
+	struct gfs_sbd *sdp = vfs2sdp(sb);
+	int error;
+
+        if (!sdp)
+                return;
+
+	atomic_inc(&sdp->sd_ops_super);
+
+	gfs_proc_fs_del(sdp);
+
+	/*  Unfreeze the filesystem, if we need to  */
+
+	down(&sdp->sd_freeze_lock);
+	if (sdp->sd_freeze_count)
+		gfs_glock_dq_uninit(&sdp->sd_freeze_gh);
+	up(&sdp->sd_freeze_lock);
+
+	/*  Kill off the inode thread  */
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_INODED_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_inoded_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Kill off the quota thread  */
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_QUOTAD_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_quotad_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Kill off the log thread  */
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_LOGD_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_logd_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Kill off the recoverd thread  */
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_RECOVERD_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_recoverd_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Kill off the glockd threads  */
+	clear_bit(SDF_GLOCKD_RUN, &sdp->sd_flags);
+	wake_up(&sdp->sd_reclaim_wchan);
+	while (sdp->sd_glockd_num--)
+		wait_for_completion(&sdp->sd_thread_completion);
+
+	/*  Kill off the scand thread  */
+	down(&sdp->sd_thread_lock);
+	clear_bit(SDF_SCAND_RUN, &sdp->sd_flags);
+	wake_up_process(sdp->sd_scand_process);
+	up(&sdp->sd_thread_lock);
+	wait_for_completion(&sdp->sd_thread_completion);
+
+	if (!test_bit(SDF_ROFS, &sdp->sd_flags)) {
+		error = gfs_make_fs_ro(sdp);
+		if (error)
+			gfs_io_error(sdp);
+	}
+
+	/*  At this point, we're through modifying the disk  */
+
+	/*  Release stuff  */
+
+	gfs_inode_put(sdp->sd_riinode);
+	gfs_inode_put(sdp->sd_jiinode);
+	gfs_inode_put(sdp->sd_rooti);
+	gfs_inode_put(sdp->sd_qinode);
+	gfs_inode_put(sdp->sd_linode);
+
+	gfs_glock_put(sdp->sd_trans_gl);
+	gfs_glock_put(sdp->sd_rename_gl);
+
+	gfs_glock_dq_uninit(&sdp->sd_journal_gh);
+
+	gfs_glock_dq_uninit(&sdp->sd_live_gh);
+
+	/*  Get rid of rgrp bitmap structures  */
+	gfs_clear_rgrpd(sdp);
+	gfs_clear_journals(sdp);
+
+	/*  Take apart glock structures and buffer lists  */
+	gfs_gl_hash_clear(sdp, TRUE);
+
+	/*  Unmount the locking protocol  */
+	gfs_lm_unmount(sdp);
+
+	/*  At this point, we're through participating in the lockspace  */
+
+	gfs_clear_dirty_j(sdp);
+
+	/*  Get rid of any extra inodes  */
+	while (invalidate_inodes(sb))
+		yield();
+
+	vfree(sdp);
+
+	vfs2sdp(sb) = NULL;
+}
+
+/**
+ * gfs_write_super - disk commit all incore transactions
+ * @sb: the filesystem
+ *
+ * This function is called every time sync(2) is called.
+ * After this exits, all dirty buffers and synced.
+ */
+
+static void
+gfs_write_super(struct super_block *sb)
+{
+	struct gfs_sbd *sdp = vfs2sdp(sb);
+	atomic_inc(&sdp->sd_ops_super);
+	gfs_log_flush(sdp);
+}
+
+/**
+ * gfs_write_super_lockfs - prevent further writes to the filesystem
+ * @sb: the VFS structure for the filesystem
+ *
+ */
+
+static void
+gfs_write_super_lockfs(struct super_block *sb)
+{
+	struct gfs_sbd *sdp = vfs2sdp(sb);
+	int error;
+
+	atomic_inc(&sdp->sd_ops_super);
+
+	for (;;) {
+		error = gfs_freeze_fs(sdp);
+		if (!error)
+			break;
+
+		switch (error) {
+		case -EBUSY:
+			printk("GFS: fsid=%s: waiting for recovery before freeze\n",
+			       sdp->sd_fsname);
+			break;
+
+		default:
+			printk("GFS: fsid=%s: error freezing FS: %d\n",
+			       sdp->sd_fsname, error);
+			break;
+		}
+
+		printk("GFS: fsid=%s: retrying...\n", sdp->sd_fsname);
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ);
+	}
+}
+
+/**
+ * gfs_unlockfs - reallow writes to the filesystem
+ * @sb: the VFS structure for the filesystem
+ *
+ */
+
+static void
+gfs_unlockfs(struct super_block *sb)
+{
+	struct gfs_sbd *sdp = vfs2sdp(sb);
+
+	atomic_inc(&sdp->sd_ops_super);
+
+	gfs_unfreeze_fs(sdp);
+}
+
+/**
+ * gfs_statfs - Gather and return stats about the filesystem
+ * @sb: The superblock
+ * @statfsbuf: The buffer
+ *
+ * Returns: 0 on success or error code
+ */
+
+static int
+gfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	struct gfs_sbd *sdp = vfs2sdp(sb);
+	struct gfs_stat_gfs sg;
+	int error;
+
+	atomic_inc(&sdp->sd_ops_super);
+
+	error = gfs_stat_gfs(sdp, &sg, TRUE);
+	if (error)
+		return error;
+
+	memset(buf, 0, sizeof(struct kstatfs));
+
+	buf->f_type = GFS_MAGIC;
+	buf->f_bsize = sdp->sd_sb.sb_bsize;
+	buf->f_blocks = sg.sg_total_blocks;
+	buf->f_bfree = sg.sg_free + sg.sg_free_dinode + sg.sg_free_meta;
+	buf->f_bavail = sg.sg_free + sg.sg_free_dinode + sg.sg_free_meta;
+	buf->f_files = sg.sg_used_dinode + sg.sg_free_dinode +
+		sg.sg_free_meta + sg.sg_free;
+	buf->f_ffree = sg.sg_free_dinode + sg.sg_free_meta + sg.sg_free;
+	buf->f_namelen = GFS_FNAMESIZE;
+
+	return 0;
+}
+
+/**
+ * gfs_remount_fs - called when the FS is remounted
+ * @sb:  the filesystem
+ * @flags:  the remount flags
+ * @data:  extra data passed in (not used right now)
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+	struct gfs_sbd *sdp = vfs2sdp(sb);
+	int error = 0;
+	struct gfs_args *args;
+
+	atomic_inc(&sdp->sd_ops_super);
+
+	args = kmalloc(sizeof(struct gfs_args), GFP_KERNEL);
+	if (!args)
+		return -ENOMEM;
+
+	error = gfs_make_args(data, args);
+	if (error) {
+		printk("GFS: can't parse remount arguments\n");
+		goto out;
+	}
+	if (args->ar_posix_acls) {
+		sdp->sd_args.ar_posix_acls = TRUE;
+		sb->s_flags |= MS_POSIXACL;
+	}
+	else {
+		if (sdp->sd_args.ar_posix_acls == TRUE)
+			printk("GFS: remounting without ACLs\n");
+		sdp->sd_args.ar_posix_acls = FALSE;
+		sb->s_flags &= ~MS_POSIXACL;
+	}
+
+	if (*flags & (MS_NOATIME | MS_NODIRATIME))
+		set_bit(SDF_NOATIME, &sdp->sd_flags);
+	else
+		clear_bit(SDF_NOATIME, &sdp->sd_flags);
+
+	if (*flags & MS_RDONLY) {
+		if (!test_bit(SDF_ROFS, &sdp->sd_flags))
+			error = gfs_make_fs_ro(sdp);
+	} else if (!(*flags & MS_RDONLY) &&
+		   test_bit(SDF_ROFS, &sdp->sd_flags)) {
+		error = gfs_make_fs_rw(sdp);
+	}
+
+	/*  Don't let the VFS update atimes.  GFS handles this itself. */
+	*flags |= MS_NOATIME | MS_NODIRATIME;
+
+out:
+	kfree(args);
+	return error;
+}
+
+/**
+ * gfs_clear_inode - Deallocate an inode when VFS is done with it
+ * @inode: The VFS inode
+ *
+ * If there's a GFS incore inode structure attached to the VFS inode:
+ * --  Detach them from one another.
+ * --  Schedule reclaim of GFS inode struct, the glock protecting it, and
+ *     the associated iopen glock.
+ */
+
+static void
+gfs_clear_inode(struct inode *inode)
+{
+	struct gfs_inode *ip = vn2ip(inode);
+
+	atomic_inc(&vfs2sdp(inode->i_sb)->sd_ops_super);
+
+	if (ip) {
+		spin_lock(&ip->i_spin);
+		ip->i_vnode = NULL;
+		vn2ip(inode) = NULL;
+		spin_unlock(&ip->i_spin);
+
+		gfs_glock_schedule_for_reclaim(ip->i_gl);
+		gfs_inode_put(ip);
+	}
+}
+
+/**
+ * gfs_show_options - Show mount options for /proc/mounts
+ * @s: seq_file structure
+ * @mnt: vfsmount
+ *
+ * Returns: 0 on success or error code
+ */
+
+static int
+gfs_show_options(struct seq_file *s, struct vfsmount *mnt)
+{
+	struct gfs_sbd *sdp = vfs2sdp(mnt->mnt_sb);
+	struct gfs_args *args = &sdp->sd_args;
+
+	atomic_inc(&sdp->sd_ops_super);
+
+	if (args->ar_lockproto[0]) {
+		seq_printf(s, ",lockproto=");
+		seq_puts(s, args->ar_lockproto);
+	}
+	if (args->ar_locktable[0]) {
+		seq_printf(s, ",locktable=");
+		seq_puts(s, args->ar_locktable);
+	}
+	if (args->ar_hostdata[0]) {
+		seq_printf(s, ",hostdata=");
+		seq_puts(s, args->ar_hostdata);
+	}
+	if (args->ar_ignore_local_fs)
+		seq_printf(s, ",ignore_local_fs");
+	if (args->ar_localflocks)
+		seq_printf(s, ",localflocks");
+	if (args->ar_localcaching)
+		seq_printf(s, ",localcaching");
+	if (args->ar_oopses_ok)
+		seq_printf(s, ",oopses_ok");
+	if (args->ar_debug)
+		seq_printf(s, ",debug");
+	if (args->ar_upgrade)
+		seq_printf(s, ",upgrade");
+	if (args->ar_num_glockd != GFS_GLOCKD_DEFAULT)
+		seq_printf(s, ",num_glockd=%u", args->ar_num_glockd);
+	if (args->ar_posix_acls)
+		seq_printf(s, ",acl");
+	if (args->ar_suiddir)
+		seq_printf(s, ",suiddir");
+
+	return 0;
+}
+
+struct super_operations gfs_super_ops = {
+	.write_inode = gfs_write_inode,
+	.put_inode = gfs_put_inode,
+	.put_super = gfs_put_super,
+	.write_super = gfs_write_super,
+	.write_super_lockfs = gfs_write_super_lockfs,
+	.unlockfs = gfs_unlockfs,
+	.statfs = gfs_statfs,
+	.remount_fs = gfs_remount_fs,
+	.clear_inode = gfs_clear_inode,
+	.show_options = gfs_show_options,
+};
diff -pruN linux-2.6.9.orig/fs/gfs/ops_super.h linux-2.6.9.debug/fs/gfs/ops_super.h
--- linux-2.6.9.orig/fs/gfs/ops_super.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_super.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,19 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __OPS_SUPER_DOT_H__
+#define __OPS_SUPER_DOT_H__
+
+extern struct super_operations gfs_super_ops;
+
+#endif /* __OPS_SUPER_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/ops_vm.c linux-2.6.9.debug/fs/gfs/ops_vm.c
--- linux-2.6.9.orig/fs/gfs/ops_vm.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_vm.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,238 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#include "gfs.h"
+#include "bmap.h"
+#include "glock.h"
+#include "inode.h"
+#include "ops_vm.h"
+#include "page.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "trans.h"
+
+/**
+ * pfault_be_greedy -
+ * @ip:
+ *
+ */
+
+static void
+pfault_be_greedy(struct gfs_inode *ip)
+{
+	unsigned int time;
+
+	spin_lock(&ip->i_spin);
+	time = ip->i_greedy;
+	ip->i_last_pfault = jiffies;
+	spin_unlock(&ip->i_spin);
+
+	gfs_inode_hold(ip);
+	if (gfs_glock_be_greedy(ip->i_gl, time))
+		gfs_inode_put(ip);
+}
+
+/**
+ * gfs_private_nopage -
+ * @area:
+ * @address:
+ * @type:
+ *
+ * Returns: the page
+ */
+
+static struct page *
+gfs_private_nopage(struct vm_area_struct *area,
+		   unsigned long address, int *type)
+{
+	struct gfs_inode *ip = vn2ip(area->vm_file->f_mapping->host);
+	struct gfs_holder i_gh;
+	struct page *result;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_vm);
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
+	if (error)
+		return NULL;
+
+	set_bit(GIF_PAGED, &ip->i_flags);
+
+	result = filemap_nopage(area, address, type);
+
+	if (result && result != NOPAGE_OOM)
+		pfault_be_greedy(ip);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return result;
+}
+
+/**
+ * alloc_page_backing -
+ * @ip:
+ * @index:
+ *
+ * Returns: errno
+ */
+
+static int
+alloc_page_backing(struct gfs_inode *ip, unsigned long index)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	uint64_t lblock = index << (PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift);
+	unsigned int blocks = PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift;
+	struct gfs_alloc *al;
+	unsigned int x;
+	int error;
+
+	al = gfs_alloc_get(ip);
+
+	error = gfs_quota_lock_m(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto out;
+
+	error = gfs_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid);
+	if (error)
+		goto out_gunlock_q;
+
+	gfs_write_calc_reserv(ip, PAGE_CACHE_SIZE,
+			      &al->al_requested_data, &al->al_requested_meta);
+
+	error = gfs_inplace_reserve(ip);
+	if (error)
+		goto out_gunlock_q;
+
+	/* Trans may require:
+	   a dinode block, RG bitmaps to allocate from,
+	   indirect blocks, and a quota block */
+
+	error = gfs_trans_begin(sdp,
+				1 + al->al_rgd->rd_ri.ri_length +
+				al->al_requested_meta, 1);
+	if (error)
+		goto out_ipres;
+
+	if (gfs_is_stuffed(ip)) {
+		error = gfs_unstuff_dinode(ip, gfs_unstuffer_page, NULL);
+		if (error)
+			goto out_trans;
+	}
+
+	for (x = 0; x < blocks; ) {
+		uint64_t dblock;
+		unsigned int extlen;
+		int new = TRUE;
+
+		error = gfs_block_map(ip, lblock, &new, &dblock, &extlen);
+		if (error)
+			goto out_trans;
+
+		lblock += extlen;
+		x += extlen;
+	}
+
+	gfs_assert_warn(sdp, al->al_alloced_meta || al->al_alloced_data);
+
+ out_trans:
+	gfs_trans_end(sdp);
+
+ out_ipres:
+	gfs_inplace_release(ip);
+
+ out_gunlock_q:
+	gfs_quota_unlock_m(ip);
+
+ out:
+	gfs_alloc_put(ip);
+
+	return error;
+}
+
+/**
+ * gfs_sharewrite_nopage -
+ * @area:
+ * @address:
+ * @type:
+ *
+ * Returns: the page
+ */
+
+static struct page *
+gfs_sharewrite_nopage(struct vm_area_struct *area,
+		      unsigned long address, int *type)
+{
+	struct gfs_inode *ip = vn2ip(area->vm_file->f_mapping->host);
+	struct gfs_holder i_gh;
+	struct page *result = NULL;
+	unsigned long index = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
+	int alloc_required;
+	int error;
+
+	atomic_inc(&ip->i_sbd->sd_ops_vm);
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return NULL;
+
+	if (gfs_is_jdata(ip))
+		goto out;
+
+	set_bit(GIF_PAGED, &ip->i_flags);
+	set_bit(GIF_SW_PAGED, &ip->i_flags);
+
+	error = gfs_write_alloc_required(ip, (uint64_t)index << PAGE_CACHE_SHIFT,
+					 PAGE_CACHE_SIZE, &alloc_required);
+	if (error)
+		goto out;
+
+	result = filemap_nopage(area, address, type);
+	if (!result || result == NOPAGE_OOM)
+		goto out;
+
+	if (alloc_required) {
+		error = alloc_page_backing(ip, index);
+		if (error) {
+			page_cache_release(result);
+			result = NULL;
+			goto out;
+		}
+		set_page_dirty(result);
+	}
+
+	pfault_be_greedy(ip);
+
+ out:
+	gfs_glock_dq_uninit(&i_gh);
+
+	return result;
+}
+
+struct vm_operations_struct gfs_vm_ops_private = {
+	.nopage = gfs_private_nopage,
+};
+
+struct vm_operations_struct gfs_vm_ops_sharewrite = {
+	.nopage = gfs_sharewrite_nopage,
+};
+
diff -pruN linux-2.6.9.orig/fs/gfs/ops_vm.h linux-2.6.9.debug/fs/gfs/ops_vm.h
--- linux-2.6.9.orig/fs/gfs/ops_vm.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/ops_vm.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,20 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __OPS_VM_DOT_H__
+#define __OPS_VM_DOT_H__
+
+extern struct vm_operations_struct gfs_vm_ops_private;
+extern struct vm_operations_struct gfs_vm_ops_sharewrite;
+
+#endif /* __OPS_VM_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/page.c linux-2.6.9.debug/fs/gfs/page.c
--- linux-2.6.9.orig/fs/gfs/page.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/page.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,279 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/pagemap.h>
+#include <linux/mm.h>
+
+#include "gfs.h"
+#include "bmap.h"
+#include "inode.h"
+#include "page.h"
+
+/**
+ * gfs_inval_pte - Sync and invalidate all PTEs associated with a glock
+ * @gl: the glock
+ *
+ */
+
+void
+gfs_inval_pte(struct gfs_glock *gl)
+{
+	struct gfs_inode *ip;
+	struct inode *inode;
+
+	ip = gl2ip(gl);
+	if (!ip ||
+	    ip->i_di.di_type != GFS_FILE_REG)
+		return;
+
+	if (!test_bit(GIF_PAGED, &ip->i_flags))
+		return;
+
+	inode = gfs_iget(ip, NO_CREATE);
+	if (inode) {
+		unmap_shared_mapping_range(inode->i_mapping, 0, 0);
+		iput(inode);
+
+		if (test_bit(GIF_SW_PAGED, &ip->i_flags))
+			set_bit(GLF_DIRTY, &gl->gl_flags);
+	}
+
+	clear_bit(GIF_SW_PAGED, &ip->i_flags);
+}
+
+/**
+ * gfs_inval_page - Invalidate all pages associated with a glock
+ * @gl: the glock
+ *
+ */
+
+void
+gfs_inval_page(struct gfs_glock *gl)
+{
+	struct gfs_inode *ip;
+	struct inode *inode;
+
+	ip = gl2ip(gl);
+	if (!ip ||
+	    ip->i_di.di_type != GFS_FILE_REG)
+		return;
+
+	inode = gfs_iget(ip, NO_CREATE);
+	if (inode) {
+		struct address_space *mapping = inode->i_mapping;
+
+		truncate_inode_pages(mapping, 0);
+		gfs_assert_withdraw(ip->i_sbd, !mapping->nrpages);
+
+		iput(inode);
+	}
+
+	clear_bit(GIF_PAGED, &ip->i_flags);
+}
+
+/**
+ * gfs_sync_page_i - Sync the data pages (not metadata) for a struct inode
+ * @inode: the inode
+ * @flags: DIO_START | DIO_WAIT
+ *
+ */
+
+void
+gfs_sync_page_i(struct inode *inode, int flags)
+{
+	struct address_space *mapping = inode->i_mapping;
+	int error = 0;
+
+	if (flags & DIO_START)
+		error = filemap_fdatawrite(mapping);
+	if (!error && (flags & DIO_WAIT))
+		error = filemap_fdatawait(mapping);
+
+	/* Find a better way to report this to the user. */
+	if (error)
+		gfs_io_error_inode(vn2ip(inode));
+}
+
+/**
+ * gfs_sync_page - Sync the data pages (not metadata) associated with a glock
+ * @gl: the glock
+ * @flags: DIO_START | DIO_WAIT
+ *
+ * Syncs data (not metadata) for a regular file.
+ * No-op for all other types.
+ */
+
+void
+gfs_sync_page(struct gfs_glock *gl, int flags)
+{
+	struct gfs_inode *ip;
+	struct inode *inode;
+
+	ip = gl2ip(gl);
+	if (!ip ||
+	    ip->i_di.di_type != GFS_FILE_REG)
+		return;
+
+	inode = gfs_iget(ip, NO_CREATE);
+	if (inode) {
+		gfs_sync_page_i(inode, flags);
+		iput(inode);
+	}
+}
+
+/**
+ * gfs_unstuffer_page - unstuff a stuffed inode into a block cached by a page
+ * @ip: the inode
+ * @dibh: the dinode buffer
+ * @block: the block number that was allocated
+ * @private: any locked page held by the caller process
+ *
+ * Returns: errno
+ */
+
+int
+gfs_unstuffer_page(struct gfs_inode *ip, struct buffer_head *dibh,
+		   uint64_t block, void *private)
+{
+	struct inode *inode = ip->i_vnode;
+	struct page *page = (struct page *)private;
+	struct buffer_head *bh;
+	int release = FALSE;
+
+	if (!page || page->index) {
+		page = grab_cache_page(inode->i_mapping, 0);
+		if (!page)
+			return -ENOMEM;
+		release = TRUE;
+	}
+
+	if (!PageUptodate(page)) {
+		void *kaddr = kmap(page);
+
+		memcpy(kaddr,
+		       dibh->b_data + sizeof(struct gfs_dinode),
+		       ip->i_di.di_size);
+		memset(kaddr + ip->i_di.di_size,
+		       0,
+		       PAGE_CACHE_SIZE - ip->i_di.di_size);
+		kunmap(page);
+
+		SetPageUptodate(page);
+	}
+
+	if (!page_has_buffers(page))
+		create_empty_buffers(page, 1 << inode->i_blkbits,
+				     (1 << BH_Uptodate));
+
+	bh = page_buffers(page);
+
+	if (!buffer_mapped(bh))
+		map_bh(bh, inode->i_sb, block);
+	else if (gfs_assert_warn(ip->i_sbd,
+				 bh->b_bdev == inode->i_sb->s_bdev &&
+				 bh->b_blocknr == block))
+                map_bh(bh, inode->i_sb, block);
+
+	set_buffer_uptodate(bh);
+	mark_buffer_dirty(bh);
+
+	if (release) {
+		unlock_page(page);
+		page_cache_release(page);
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_truncator_page - truncate a partial data block in the page cache
+ * @ip: the inode
+ * @size: the size the file should be
+ *
+ * Returns: errno
+ */
+
+int
+gfs_truncator_page(struct gfs_inode *ip, uint64_t size)
+{
+	struct inode *inode = ip->i_vnode;
+	struct page *page;
+	struct buffer_head *bh;
+	void *kaddr;
+	uint64_t lbn, dbn;
+	unsigned long index;
+	unsigned int offset;
+	unsigned int bufnum;
+	int not_new = 0;
+	int error;
+
+	lbn = size >> inode->i_blkbits;
+	error = gfs_block_map(ip,
+			      lbn, &not_new,
+			      &dbn, NULL);
+	if (error || !dbn)
+		return error;
+
+	index = size >> PAGE_CACHE_SHIFT;
+	offset = size & (PAGE_CACHE_SIZE - 1);
+	bufnum = lbn - (index << (PAGE_CACHE_SHIFT - inode->i_blkbits));
+
+	/* Not in a transaction here -- a non-disk-I/O error is ok. */
+
+	page = read_cache_page(inode->i_mapping, index,
+			       (filler_t *)inode->i_mapping->a_ops->readpage,
+			       NULL);
+	if (IS_ERR(page))
+		return PTR_ERR(page);
+
+	lock_page(page);
+
+	if (!PageUptodate(page) || PageError(page)) {
+		error = -EIO;
+		goto out;
+	}
+
+	kaddr = kmap(page);
+	memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
+	kunmap(page);
+
+	if (!page_has_buffers(page))
+		create_empty_buffers(page, 1 << inode->i_blkbits,
+				     (1 << BH_Uptodate));
+
+	for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
+		/* Do nothing */;
+
+	if (!buffer_mapped(bh))
+		map_bh(bh, inode->i_sb, dbn);
+	else if (gfs_assert_warn(ip->i_sbd,
+				 bh->b_bdev == inode->i_sb->s_bdev &&
+				 bh->b_blocknr == dbn))
+		map_bh(bh, inode->i_sb, dbn);
+
+	set_buffer_uptodate(bh);
+	mark_buffer_dirty(bh);
+
+ out:
+	unlock_page(page);
+	page_cache_release(page);
+
+	return error;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/page.h linux-2.6.9.debug/fs/gfs/page.h
--- linux-2.6.9.orig/fs/gfs/page.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/page.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,26 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __PAGE_DOT_H__
+#define __PAGE_DOT_H__
+
+void gfs_inval_pte(struct gfs_glock *gl);
+void gfs_inval_page(struct gfs_glock *gl);
+void gfs_sync_page_i(struct inode *inode, int flags);
+void gfs_sync_page(struct gfs_glock *gl, int flags);
+
+int gfs_unstuffer_page(struct gfs_inode *ip, struct buffer_head *dibh,
+		       uint64_t block, void *private);
+int gfs_truncator_page(struct gfs_inode *ip, uint64_t size);
+
+#endif /* __PAGE_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/proc.c linux-2.6.9.debug/fs/gfs/proc.c
--- linux-2.6.9.orig/fs/gfs/proc.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/proc.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,496 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+#include "gfs.h"
+#include "glock.h"
+#include "lm.h"
+#include "proc.h"
+#include "super.h"
+
+struct list_head gfs_fs_list;
+struct semaphore gfs_fs_lock;
+char *gfs_proc_margs;
+spinlock_t gfs_proc_margs_lock;
+spinlock_t req_lock;
+
+/**
+ * gfs_proc_fs_add - Add a FS to the list of mounted FSs
+ * @sdp:
+ *
+ */
+
+void
+gfs_proc_fs_add(struct gfs_sbd *sdp)
+{
+	down(&gfs_fs_lock);
+	list_add(&sdp->sd_list, &gfs_fs_list);
+	up(&gfs_fs_lock);
+}
+
+/**
+ * gfs_proc_fs_del - Remove a FS from the list of mounted FSs
+ * @sdp:
+ *
+ */
+
+void
+gfs_proc_fs_del(struct gfs_sbd *sdp)
+{
+	down(&gfs_fs_lock);
+	list_del(&sdp->sd_list);
+	up(&gfs_fs_lock);
+}
+
+/**
+ * do_list - Copy the list of mountes FSs to userspace
+ * @user_buf:
+ * @size:
+ *
+ * @Returns: -errno, or the number of bytes copied to userspace
+ */
+
+static ssize_t
+do_list(char *user_buf, size_t size)
+{
+	struct list_head *tmp;
+	struct gfs_sbd *sdp = NULL;
+	unsigned int x;
+	char num[21];
+	char *buf;
+	int error = 0;
+
+	down(&gfs_fs_lock);
+
+	x = 0;
+	for (tmp = gfs_fs_list.next; tmp != &gfs_fs_list; tmp = tmp->next) {
+		sdp = list_entry(tmp, struct gfs_sbd, sd_list);
+		x += sprintf(num, "%lu", (unsigned long)sdp) +
+			strlen(sdp->sd_vfs->s_id) +
+			strlen(sdp->sd_fsname) + 3;
+	}
+
+	if (!x)
+		goto out;
+
+	error = -EFBIG;
+	if (x > size)
+		goto out;
+
+	error = -ENOMEM;
+	buf = kmalloc(x + 1, GFP_KERNEL);
+	if (!buf)
+		goto out;
+
+	x = 0;
+	for (tmp = gfs_fs_list.next; tmp != &gfs_fs_list; tmp = tmp->next) {
+		sdp = list_entry(tmp, struct gfs_sbd, sd_list);
+		x += sprintf(buf + x, "%lu %s %s\n",
+			     (unsigned long)sdp, sdp->sd_vfs->s_id, sdp->sd_fsname);
+	}
+
+	if (copy_to_user(user_buf, buf, x))
+		error = -EFAULT;
+	else
+		error = x;
+
+	kfree(buf);
+
+ out:
+	up(&gfs_fs_lock);
+
+	return error;
+}
+
+/**
+ * find_argument - 
+ * @p:
+ *
+ * Returns:
+ */
+
+static char *
+find_argument(char *p)
+{
+	char *p2;
+
+	while (*p == ' ' || *p == '\n')
+		p++;
+	if (!*p)
+		return NULL;
+	for (p2 = p; *p2; p2++) /* do nothing */;
+	p2--;
+	while (*p2 == ' ' || *p2 == '\n')
+		*p2-- = 0;
+
+	return p;
+}
+
+/**
+ * do_freeze - freeze a filesystem
+ * @p: the freeze command
+ *
+ * Returns: errno
+ */
+
+static int
+do_freeze(char *p)
+{
+	struct list_head *tmp;
+	struct gfs_sbd *sdp;
+	char num[21];
+	int error = 0;
+
+	p = find_argument(p + 6);
+	if (!p)
+		return -ENOENT;
+
+	down(&gfs_fs_lock);
+
+	for (tmp = gfs_fs_list.next; tmp != &gfs_fs_list; tmp = tmp->next) {
+		sdp = list_entry(tmp, struct gfs_sbd, sd_list);
+		sprintf(num, "%lu", (unsigned long)sdp);
+		if (strcmp(num, p) == 0)
+			break;
+	}
+
+	if (tmp == &gfs_fs_list)
+		error = -ENOENT;
+	else
+		error = gfs_freeze_fs(sdp);
+
+	up(&gfs_fs_lock);
+
+	return error;
+}
+
+/**
+ * do_unfreeze - unfreeze a filesystem
+ * @p: the unfreeze command
+ *
+ * Returns: errno
+ */
+
+static int
+do_unfreeze(char *p)
+{
+	struct list_head *tmp;
+	struct gfs_sbd *sdp;
+	char num[21];
+	int error = 0;
+
+	p = find_argument(p + 8);
+	if (!p)
+		return -ENOENT;
+
+	down(&gfs_fs_lock);
+
+	for (tmp = gfs_fs_list.next; tmp != &gfs_fs_list; tmp = tmp->next) {
+		sdp = list_entry(tmp, struct gfs_sbd, sd_list);
+		sprintf(num, "%lu", (unsigned long)sdp);
+		if (strcmp(num, p) == 0)
+			break;
+	}
+
+	if (tmp == &gfs_fs_list)
+		error = -ENOENT;
+	else
+		gfs_unfreeze_fs(sdp);
+
+	up(&gfs_fs_lock);
+
+	return error;
+}
+
+/**
+ * do_margs - Pass in mount arguments
+ * @p: the margs command
+ *
+ * Returns: errno
+ */
+
+static int
+do_margs(char *p)
+{
+	char *new_buf, *old_buf;
+
+	p = find_argument(p + 5);
+	if (!p)
+		return -ENOENT;
+
+	new_buf = kmalloc(strlen(p) + 1, GFP_KERNEL);
+	if (!new_buf)
+		return -ENOMEM;
+	strcpy(new_buf, p);
+
+	spin_lock(&gfs_proc_margs_lock);
+	old_buf = gfs_proc_margs;
+	gfs_proc_margs = new_buf;
+	spin_unlock(&gfs_proc_margs_lock);
+
+	if (old_buf)
+		kfree(old_buf);
+
+	return 0;
+}
+
+/**
+ * do_withdraw - withdraw a from the cluster for one filesystem
+ * @p: the cookie of the filesystem
+ *
+ * Returns: errno
+ */
+
+static int
+do_withdraw(char *p)
+{
+	struct list_head *tmp;
+	struct gfs_sbd *sdp;
+	char num[21];
+	int error = 0;
+
+	p = find_argument(p + 8);
+	if (!p)
+		return -ENOENT;
+
+	down(&gfs_fs_lock);
+
+	for (tmp = gfs_fs_list.next; tmp != &gfs_fs_list; tmp = tmp->next) {
+		sdp = list_entry(tmp, struct gfs_sbd, sd_list);
+		sprintf(num, "%lu", (unsigned long)sdp);
+		if (strcmp(num, p) == 0)
+			break;
+	}
+
+	if (tmp == &gfs_fs_list)
+		error = -ENOENT;
+	else 
+		gfs_lm_withdraw(sdp,
+				"GFS: fsid=%s: withdrawing from cluster at user's request\n",
+				sdp->sd_fsname);
+
+	up(&gfs_fs_lock);
+
+	return error;
+}
+
+/**
+ * do_lockdump - Copy out the lock hash table to userspace
+ * @p: the cookie of the filesystem
+ * @buf:
+ * @size:
+ *
+ * Returns: errno
+ */
+
+static int
+do_lockdump(char *p, char *buf, size_t size)
+{
+	struct list_head *tmp;
+	struct gfs_sbd *sdp;
+	char num[21];
+	struct gfs_user_buffer ub;
+	int error = 0;
+
+	p = find_argument(p + 8);
+	if (!p)
+		return -ENOENT;
+
+	down(&gfs_fs_lock);
+
+	for (tmp = gfs_fs_list.next; tmp != &gfs_fs_list; tmp = tmp->next) {
+		sdp = list_entry(tmp, struct gfs_sbd, sd_list);
+		sprintf(num, "%lu", (unsigned long)sdp);
+		if (strcmp(num, p) == 0)
+			break;
+	}
+
+	if (tmp == &gfs_fs_list)
+		error = -ENOENT;
+	else {
+		ub.ub_data = buf;
+		ub.ub_size = size;
+		ub.ub_count = 0;
+
+		error = gfs_dump_lockstate(sdp, &ub);
+		if (!error)
+			error = ub.ub_count;
+	}
+
+	up(&gfs_fs_lock);
+
+	return error;
+}
+
+/**
+ * gfs_proc_write - take a command from userspace
+ * @file:
+ * @buf:
+ * @size:
+ * @offset:
+ *
+ * Returns: -errno or the number of bytes taken
+ */
+
+static ssize_t
+gfs_proc_write(struct file *file, const char *buf, size_t size, loff_t *offset)
+{
+	char *p;
+
+	spin_lock(&req_lock);
+	p = file->private_data;
+	file->private_data = NULL;
+	spin_unlock(&req_lock);
+
+	if (p)
+		kfree(p);
+
+	if (!size)
+		return -EINVAL;
+
+	p = kmalloc(size + 1, GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+	p[size] = 0;
+
+	if (copy_from_user(p, buf, size)) {
+		kfree(p);
+		return -EFAULT;
+	}
+
+	spin_lock(&req_lock);
+	file->private_data = p;
+	spin_unlock(&req_lock);
+
+	return size;
+}
+
+/**
+ * gfs_proc_read - return the results of a command
+ * @file:
+ * @buf:
+ * @size:
+ * @offset:
+ *
+ * Returns: -errno or the number of bytes returned
+ */
+
+static ssize_t
+gfs_proc_read(struct file *file, char *buf, size_t size, loff_t *offset)
+{
+	char *p;
+	int error;
+
+	spin_lock(&req_lock);
+	p = file->private_data;
+	file->private_data = NULL;
+	spin_unlock(&req_lock);
+
+	if (!p)
+		return -ENOENT;
+
+	if (!size) {
+		kfree(p);
+		return -EINVAL;
+	}
+
+	if (strncmp(p, "list", 4) == 0)
+		error = do_list(buf, size);
+	else if (strncmp(p, "freeze", 6) == 0)
+		error = do_freeze(p);
+	else if (strncmp(p, "unfreeze", 8) == 0)
+		error = do_unfreeze(p);
+	else if (strncmp(p, "margs", 5) == 0)
+		error = do_margs(p);
+	else if (strncmp(p, "withdraw", 8) == 0)
+		error = do_withdraw(p);
+	else if (strncmp(p, "lockdump", 8) == 0)
+		error = do_lockdump(p, buf, size);
+	else
+		error = -ENOSYS;
+
+	kfree(p);
+
+	return error;
+}
+
+/**
+ * gfs_proc_close - free any mismatches writes
+ * @inode:
+ * @file:
+ *
+ * Returns: 0
+ */
+
+static int
+gfs_proc_close(struct inode *inode, struct file *file)
+{
+	if (file->private_data)
+		kfree(file->private_data);
+	return 0;
+}
+
+static struct file_operations gfs_proc_fops =
+{
+	.owner = THIS_MODULE,
+	.write = gfs_proc_write,
+	.read = gfs_proc_read,
+	.release = gfs_proc_close,
+};
+
+/**
+ * gfs_proc_init - initialize GFS' proc interface
+ *
+ */
+
+void
+gfs_proc_init(void)
+{
+	struct proc_dir_entry *pde;
+
+	INIT_LIST_HEAD(&gfs_fs_list);
+	init_MUTEX(&gfs_fs_lock);
+	gfs_proc_margs = NULL;
+	spin_lock_init(&gfs_proc_margs_lock);
+	spin_lock_init(&req_lock);
+
+	pde = create_proc_entry("fs/gfs", S_IFREG | 0600, NULL);
+	if (pde) {
+		pde->owner = THIS_MODULE;
+		pde->proc_fops = &gfs_proc_fops;
+	}
+}
+
+/**
+ * gfs_proc_uninit - uninitialize GFS' proc interface
+ *
+ */
+
+void
+gfs_proc_uninit(void)
+{
+	if (gfs_proc_margs)
+		kfree(gfs_proc_margs);
+	remove_proc_entry("fs/gfs", NULL);
+}
+
diff -pruN linux-2.6.9.orig/fs/gfs/proc.h linux-2.6.9.debug/fs/gfs/proc.h
--- linux-2.6.9.orig/fs/gfs/proc.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/proc.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,27 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __PROC_DOT_H__
+#define __PROC_DOT_H__
+
+/* Allow args to be passed to GFS when using an initial ram disk */
+extern char *gfs_proc_margs;
+extern spinlock_t gfs_proc_margs_lock;
+
+void gfs_proc_fs_add(struct gfs_sbd *sdp);
+void gfs_proc_fs_del(struct gfs_sbd *sdp);
+
+void gfs_proc_init(void);
+void gfs_proc_uninit(void);
+
+#endif /* __PROC_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/quota.c linux-2.6.9.debug/fs/gfs/quota.c
--- linux-2.6.9.orig/fs/gfs/quota.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/quota.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1152 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/tty.h>
+
+#include "gfs.h"
+#include "bmap.h"
+#include "file.h"
+#include "glock.h"
+#include "glops.h"
+#include "log.h"
+#include "quota.h"
+#include "rgrp.h"
+#include "super.h"
+#include "trans.h"
+
+/**
+ * gfs_quota_get - Get a structure to represent a quota change
+ * @sdp: the filesystem
+ * @user: TRUE if this is a user quota
+ * @id: the uid or gid
+ * @create: if TRUE, create the structure, otherwise return NULL
+ * @qdp: the returned quota structure
+ *
+ * Returns: errno
+ */
+
+int
+gfs_quota_get(struct gfs_sbd *sdp, int user, uint32_t id, int create,
+	      struct gfs_quota_data **qdp)
+{
+	struct gfs_quota_data *qd = NULL, *new_qd = NULL;
+	struct list_head *tmp, *head;
+	int error;
+
+	*qdp = NULL;
+
+	for (;;) {
+		spin_lock(&sdp->sd_quota_lock);
+
+		for (head = &sdp->sd_quota_list, tmp = head->next;
+		     tmp != head;
+		     tmp = tmp->next) {
+			qd = list_entry(tmp, struct gfs_quota_data, qd_list);
+			if (qd->qd_id == id &&
+			    !test_bit(QDF_USER, &qd->qd_flags) == !user) {
+				qd->qd_count++;
+				break;
+			}
+		}
+
+		if (tmp == head)
+			qd = NULL;
+
+		if (!qd && new_qd) {
+			qd = new_qd;
+			list_add(&qd->qd_list, &sdp->sd_quota_list);
+			new_qd = NULL;
+		}
+
+		spin_unlock(&sdp->sd_quota_lock);
+
+		if (qd || !create) {
+			if (new_qd) {
+				gfs_lvb_unhold(new_qd->qd_gl);
+				kfree(new_qd);
+				atomic_dec(&sdp->sd_quota_count);
+			}
+			*qdp = qd;
+			return 0;
+		}
+
+		new_qd = kmalloc(sizeof(struct gfs_quota_data), GFP_KERNEL);
+		if (!new_qd)
+			return -ENOMEM;
+		memset(new_qd, 0, sizeof(struct gfs_quota_data));
+
+		new_qd->qd_count = 1;
+
+		new_qd->qd_id = id;
+		if (user)
+			set_bit(QDF_USER, &new_qd->qd_flags);
+
+		INIT_LIST_HEAD(&new_qd->qd_le_list);
+
+		error = gfs_glock_get(sdp, 2 * (uint64_t)id + ((user) ? 0 : 1),
+				      &gfs_quota_glops, CREATE,
+				      &new_qd->qd_gl);
+		if (error) {
+			kfree(new_qd);
+			return error;
+		}
+
+		error = gfs_lvb_hold(new_qd->qd_gl);
+
+		gfs_glock_put(new_qd->qd_gl);
+
+		if (error) {
+			kfree(new_qd);
+			return error;
+		}
+
+		atomic_inc(&sdp->sd_quota_count);
+	}
+}
+
+/**
+ * gfs_quota_hold - increment the usage count on a struct gfs_quota_data
+ * @sdp: the filesystem
+ * @qd: the structure
+ *
+ */
+
+void
+gfs_quota_hold(struct gfs_sbd *sdp, struct gfs_quota_data *qd)
+{
+	spin_lock(&sdp->sd_quota_lock);
+	gfs_assert(sdp, qd->qd_count,);
+	qd->qd_count++;
+	spin_unlock(&sdp->sd_quota_lock);
+}
+
+/**
+ * gfs_quota_put - decrement the usage count on a struct gfs_quota_data
+ * @sdp: the filesystem
+ * @qd: the structure
+ *
+ * Free the structure if its reference count hits zero.
+ *
+ */
+
+void
+gfs_quota_put(struct gfs_sbd *sdp, struct gfs_quota_data *qd)
+{
+	spin_lock(&sdp->sd_quota_lock);
+	gfs_assert(sdp, qd->qd_count,);
+	qd->qd_count--;
+	spin_unlock(&sdp->sd_quota_lock);
+}
+
+/**
+ * quota_find - Find a quota change to sync to the quota file
+ * @sdp: the filesystem
+ *
+ * The returned structure is locked and needs to be unlocked
+ * with quota_unlock().
+ *
+ * Returns: A quota structure, or NULL
+ */
+
+static struct gfs_quota_data *
+quota_find(struct gfs_sbd *sdp)
+{
+	struct list_head *tmp, *head;
+	struct gfs_quota_data *qd = NULL;
+
+	if (test_bit(SDF_ROFS, &sdp->sd_flags))
+		return NULL;
+
+	gfs_log_lock(sdp);
+	spin_lock(&sdp->sd_quota_lock);
+
+	if (!atomic_read(&sdp->sd_quota_od_count))
+		goto out;
+
+	for (head = &sdp->sd_quota_list, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		qd = list_entry(tmp, struct gfs_quota_data, qd_list);
+
+		if (test_bit(QDF_LOCK, &qd->qd_flags))
+			continue;
+		if (!test_bit(QDF_OD_LIST, &qd->qd_flags))
+			continue;
+		if (qd->qd_sync_gen >= sdp->sd_quota_sync_gen)
+			continue;
+
+		list_move_tail(&qd->qd_list, &sdp->sd_quota_list);
+
+		set_bit(QDF_LOCK, &qd->qd_flags);
+		qd->qd_count++;
+		qd->qd_change_sync = qd->qd_change_od;
+
+		goto out;
+	}
+
+	qd = NULL;
+
+ out:
+	spin_unlock(&sdp->sd_quota_lock);
+	gfs_log_unlock(sdp);
+
+	return qd;
+}
+
+/**
+ * quota_trylock - Try to lock a given quota entry
+ * @sdp: the filesystem
+ * @qd: the quota data structure
+ *
+ * Returns: TRUE if the lock was successful, FALSE, otherwise
+ */
+
+static int
+quota_trylock(struct gfs_sbd *sdp, struct gfs_quota_data *qd)
+{
+	int ret = FALSE;
+
+	if (test_bit(SDF_ROFS, &sdp->sd_flags))
+		return FALSE;
+
+	gfs_log_lock(sdp);
+	spin_lock(&sdp->sd_quota_lock);
+
+	if (test_bit(QDF_LOCK, &qd->qd_flags))
+		goto out;
+	if (!test_bit(QDF_OD_LIST, &qd->qd_flags))
+		goto out;
+
+	list_move_tail(&qd->qd_list, &sdp->sd_quota_list);
+
+	set_bit(QDF_LOCK, &qd->qd_flags);
+	qd->qd_count++;
+	qd->qd_change_sync = qd->qd_change_od;
+
+	ret = TRUE;
+
+ out:
+	spin_unlock(&sdp->sd_quota_lock);
+	gfs_log_unlock(sdp);
+
+	return ret;
+}
+
+/**
+ * quota_unlock - drop and a reference on a quota structure
+ * @sdp: the filesystem
+ * @qd: the quota inode structure
+ *
+ */
+
+static void
+quota_unlock(struct gfs_sbd *sdp, struct gfs_quota_data *qd)
+{
+	spin_lock(&sdp->sd_quota_lock);
+
+	gfs_assert_warn(sdp, test_bit(QDF_LOCK, &qd->qd_flags));
+	clear_bit(QDF_LOCK, &qd->qd_flags);
+
+	gfs_assert(sdp, qd->qd_count,);
+	qd->qd_count--;
+
+	spin_unlock(&sdp->sd_quota_lock);
+}
+
+/**
+ * gfs_quota_merge - add/remove a quota change from the in-memory list
+ * @sdp: the filesystem
+ * @tag: the quota change tag
+ *
+ * Returns: errno
+ */
+
+int
+gfs_quota_merge(struct gfs_sbd *sdp, struct gfs_quota_tag *tag)
+{
+	struct gfs_quota_data *qd;
+	int error;
+
+	error = gfs_quota_get(sdp,
+			      tag->qt_flags & GFS_QTF_USER, tag->qt_id,
+			      CREATE, &qd);
+	if (error)
+		return error;
+
+	gfs_assert(sdp, qd->qd_change_ic == qd->qd_change_od,);
+
+	gfs_log_lock(sdp);
+
+	qd->qd_change_ic += tag->qt_change;
+	qd->qd_change_od += tag->qt_change;
+
+	if (qd->qd_change_od) {
+		if (!test_bit(QDF_OD_LIST, &qd->qd_flags)) {
+			gfs_quota_hold(sdp, qd);
+			set_bit(QDF_OD_LIST, &qd->qd_flags);
+			atomic_inc(&sdp->sd_quota_od_count);
+		}
+	} else {
+		gfs_assert_warn(sdp, test_bit(QDF_OD_LIST, &qd->qd_flags));
+		clear_bit(QDF_OD_LIST, &qd->qd_flags);
+		gfs_quota_put(sdp, qd);
+		gfs_assert(sdp, atomic_read(&sdp->sd_quota_od_count) > 0,);
+		atomic_dec(&sdp->sd_quota_od_count);
+	}
+
+	gfs_log_unlock(sdp);
+
+	gfs_quota_put(sdp, qd);
+
+	return 0;
+}
+
+/**
+ * gfs_quota_scan - Look for unused struct gfs_quota_data structures to throw away
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_quota_scan(struct gfs_sbd *sdp)
+{
+	struct list_head *head, *tmp, *next;
+	struct gfs_quota_data *qd;
+	LIST_HEAD(dead);
+
+	spin_lock(&sdp->sd_quota_lock);
+
+	for (head = &sdp->sd_quota_list, tmp = head->next, next = tmp->next;
+	     tmp != head;
+	     tmp = next, next = next->next) {
+		qd = list_entry(tmp, struct gfs_quota_data, qd_list);
+		if (!qd->qd_count)
+			list_move(&qd->qd_list, &dead);
+	}
+
+	spin_unlock(&sdp->sd_quota_lock);
+
+	while (!list_empty(&dead)) {
+		qd = list_entry(dead.next, struct gfs_quota_data, qd_list);
+
+		gfs_assert_warn(sdp, !qd->qd_count);
+		gfs_assert_warn(sdp, !test_bit(QDF_OD_LIST, &qd->qd_flags) &&
+				!test_bit(QDF_LOCK, &qd->qd_flags));
+		gfs_assert_warn(sdp, !qd->qd_change_new && !qd->qd_change_ic &&
+				!qd->qd_change_od);
+
+		list_del(&qd->qd_list);
+		gfs_lvb_unhold(qd->qd_gl);
+		kfree(qd);
+		atomic_dec(&sdp->sd_quota_count);
+	}
+}
+
+/**
+ * gfs_quota_cleanup - get rid of any extra struct gfs_quota_data structures
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_quota_cleanup(struct gfs_sbd *sdp)
+{
+	struct gfs_quota_data *qd;
+
+ restart:
+	gfs_log_lock(sdp);
+
+	spin_lock(&sdp->sd_quota_lock);
+
+	while (!list_empty(&sdp->sd_quota_list)) {
+		qd = list_entry(sdp->sd_quota_list.next,
+				struct gfs_quota_data,
+				qd_list);
+
+		if (qd->qd_count > 1) {
+			spin_unlock(&sdp->sd_quota_lock);
+			gfs_log_unlock(sdp);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ);
+			goto restart;
+
+		} else if (qd->qd_count) {
+			gfs_assert_warn(sdp,
+					test_bit(QDF_OD_LIST, &qd->qd_flags) &&
+					!test_bit(QDF_LOCK, &qd->qd_flags));
+			gfs_assert_warn(sdp, qd->qd_change_od &&
+					qd->qd_change_od == qd->qd_change_ic);
+			gfs_assert_warn(sdp, !qd->qd_change_new);
+
+			list_del(&qd->qd_list);
+			atomic_dec(&sdp->sd_quota_od_count);
+
+			spin_unlock(&sdp->sd_quota_lock);
+			gfs_lvb_unhold(qd->qd_gl);
+			kfree(qd);
+			atomic_dec(&sdp->sd_quota_count);
+			spin_lock(&sdp->sd_quota_lock);
+
+		} else {
+			gfs_assert_warn(sdp,
+					!test_bit(QDF_OD_LIST, &qd->qd_flags) &&
+					!test_bit(QDF_LOCK, &qd->qd_flags));
+			gfs_assert_warn(sdp, !qd->qd_change_new &&
+					!qd->qd_change_ic &&
+					!qd->qd_change_od);
+
+			list_del(&qd->qd_list);
+
+			spin_unlock(&sdp->sd_quota_lock);
+			gfs_lvb_unhold(qd->qd_gl);
+			kfree(qd);
+			atomic_dec(&sdp->sd_quota_count);
+			spin_lock(&sdp->sd_quota_lock);
+		}
+	}
+
+	spin_unlock(&sdp->sd_quota_lock);
+
+	gfs_assert(sdp, !atomic_read(&sdp->sd_quota_od_count),);
+
+	gfs_log_unlock(sdp);
+}
+
+/**
+ * sort_qd - figure out the order between two quota data structures
+ * @a: first quota data structure
+ * @b: second quota data structure
+ *
+ * Returns: -1 if @a comes before @b, 0 if @a equals @b, 1 if @b comes before @a
+ */
+
+static int
+sort_qd(const void *a, const void *b)
+{
+	struct gfs_quota_data *qd_a = *(struct gfs_quota_data **)a;
+	struct gfs_quota_data *qd_b = *(struct gfs_quota_data **)b;
+	int ret = 0;
+
+	if (!test_bit(QDF_USER, &qd_a->qd_flags) !=
+	    !test_bit(QDF_USER, &qd_b->qd_flags)) {
+		if (test_bit(QDF_USER, &qd_a->qd_flags))
+			ret = -1;
+		else
+			ret = 1;
+	} else {
+		if (qd_a->qd_id < qd_b->qd_id)
+			ret = -1;
+		else if (qd_a->qd_id > qd_b->qd_id)
+			ret = 1;
+	}
+
+	return ret;
+}
+
+/**
+ * do_quota_sync - Sync a bunch quota changes to the quota file
+ * @sdp: the filesystem
+ * @qda: an array of struct gfs_quota_data structures to be synced
+ * @num_qd: the number of elements in @qda
+ *
+ * Returns: errno
+ */
+
+static int
+do_quota_sync(struct gfs_sbd *sdp, struct gfs_quota_data **qda,
+	      unsigned int num_qd)
+{
+	struct gfs_inode *ip = sdp->sd_qinode;
+	struct gfs_alloc *al = NULL;
+	struct gfs_holder i_gh, *ghs;
+	struct gfs_quota q;
+	char buf[sizeof(struct gfs_quota)];
+	uint64_t offset;
+	unsigned int qx, x;
+	int ar;
+	unsigned int nalloc = 0;
+	unsigned int data_blocks, ind_blocks;
+	int error;
+
+	gfs_write_calc_reserv(ip, sizeof(struct gfs_quota), &data_blocks,
+			      &ind_blocks);
+
+	ghs = kmalloc(num_qd * sizeof(struct gfs_holder), GFP_KERNEL);
+	if (!ghs)
+		return -ENOMEM;
+
+	gfs_sort(qda, num_qd, sizeof (struct gfs_quota_data *), sort_qd);
+	for (qx = 0; qx < num_qd; qx++) {
+		error = gfs_glock_nq_init(qda[qx]->qd_gl,
+					  LM_ST_EXCLUSIVE,
+					  GL_NOCACHE, &ghs[qx]);
+		if (error)
+			goto fail;
+	}
+
+	error = gfs_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		goto fail;
+
+	for (x = 0; x < num_qd; x++) {
+		offset = (2 * (uint64_t)qda[x]->qd_id +
+			  ((test_bit(QDF_USER, &qda[x]->qd_flags)) ? 0 : 1)) *
+			sizeof(struct gfs_quota);
+
+		error = gfs_write_alloc_required(ip, offset,
+						 sizeof(struct gfs_quota),
+						 &ar);
+		if (error)
+			goto fail_gunlock;
+
+		if (ar)
+			nalloc++;
+	}
+
+	if (nalloc) {
+		al = gfs_alloc_get(ip);
+
+		error =
+		    gfs_quota_hold_m(ip, NO_QUOTA_CHANGE,
+					   NO_QUOTA_CHANGE);
+		if (error)
+			goto fail_alloc;
+
+		al->al_requested_meta = nalloc * (data_blocks + ind_blocks);
+
+		error = gfs_inplace_reserve(ip);
+		if (error)
+			goto fail_qs;
+
+		/* Trans may require:
+		   two (journaled) data blocks, a dinode block, RG bitmaps to allocate from,
+		   indirect blocks, and a quota block */
+
+		error = gfs_trans_begin(sdp,
+					1 + al->al_rgd->rd_ri.ri_length +
+					num_qd * data_blocks +
+					nalloc * ind_blocks,
+					gfs_struct2blk(sdp, num_qd + 2,
+						       sizeof(struct gfs_quota_tag)));
+		if (error)
+			goto fail_ipres;
+	} else {
+		/* Trans may require:
+		   Data blocks, a dinode block, and quota blocks */
+
+		error = gfs_trans_begin(sdp,
+					1 + data_blocks * num_qd,
+					gfs_struct2blk(sdp, num_qd,
+						       sizeof(struct gfs_quota_tag)));
+		if (error)
+			goto fail_gunlock;
+	}
+
+	for (x = 0; x < num_qd; x++) {
+		offset = (2 * (uint64_t)qda[x]->qd_id +
+			  ((test_bit(QDF_USER, &qda[x]->qd_flags)) ? 0 : 1)) *
+			sizeof(struct gfs_quota);
+
+		/*  The quota file may not be a multiple of sizeof(struct gfs_quota) bytes.  */
+		memset(buf, 0, sizeof(struct gfs_quota));
+
+		error = gfs_internal_read(ip, buf, offset,
+					  sizeof(struct gfs_quota));
+		if (error < 0)
+			goto fail_end_trans;
+
+		gfs_quota_in(&q, buf);
+		q.qu_value += qda[x]->qd_change_sync;
+		gfs_quota_out(&q, buf);
+
+		error = gfs_internal_write(ip, buf, offset,
+					   sizeof(struct gfs_quota));
+		if (error < 0)
+			goto fail_end_trans;
+		else if (error != sizeof(struct gfs_quota)) {
+			error = -EIO;
+			goto fail_end_trans;
+		}
+
+		if (test_bit(QDF_USER, &qda[x]->qd_flags))
+			gfs_trans_add_quota(sdp, -qda[x]->qd_change_sync,
+					    qda[x]->qd_id, NO_QUOTA_CHANGE);
+		else
+			gfs_trans_add_quota(sdp, -qda[x]->qd_change_sync,
+					    NO_QUOTA_CHANGE, qda[x]->qd_id);
+
+		memset(&qda[x]->qd_qb, 0, sizeof(struct gfs_quota_lvb));
+		qda[x]->qd_qb.qb_magic = GFS_MAGIC;
+		qda[x]->qd_qb.qb_limit = q.qu_limit;
+		qda[x]->qd_qb.qb_warn = q.qu_warn;
+		qda[x]->qd_qb.qb_value = q.qu_value;
+
+		gfs_quota_lvb_out(&qda[x]->qd_qb, qda[x]->qd_gl->gl_lvb);
+	}
+
+	gfs_trans_end(sdp);
+
+	if (nalloc) {
+		gfs_assert_warn(sdp, al->al_alloced_meta);
+		gfs_inplace_release(ip);
+		gfs_quota_unhold_m(ip);
+		gfs_alloc_put(ip);
+	}
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	for (x = 0; x < num_qd; x++)
+		gfs_glock_dq_uninit(&ghs[x]);
+
+	kfree(ghs);
+
+	gfs_log_flush_glock(ip->i_gl);
+
+	return 0;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_ipres:
+	if (nalloc)
+		gfs_inplace_release(ip);
+
+ fail_qs:
+	if (nalloc)
+		gfs_quota_unhold_m(ip);
+
+ fail_alloc:
+	if (nalloc)
+		gfs_alloc_put(ip);
+
+ fail_gunlock:
+	gfs_glock_dq_uninit(&i_gh);
+
+ fail:
+	while (qx--)
+		gfs_glock_dq_uninit(&ghs[qx]);
+
+	kfree(ghs);
+
+	return error;
+}
+
+/**
+ * glock_q - Acquire a lock for a quota entry
+ * @sdp: the filesystem
+ * @qd: the quota data structure to glock
+ * @force_refresh: If TRUE, always read from the quota file
+ * @q_gh: the glock holder for the quota lock
+ *
+ * Returns: errno
+ */
+
+static int
+glock_q(struct gfs_sbd *sdp, struct gfs_quota_data *qd, int force_refresh,
+	struct gfs_holder *q_gh)
+{
+	struct gfs_holder i_gh;
+	struct gfs_quota q;
+	char buf[sizeof(struct gfs_quota)];
+	int error;
+
+ restart:
+	error = gfs_glock_nq_init(qd->qd_gl, LM_ST_SHARED, 0, q_gh);
+	if (error)
+		return error;
+
+	gfs_quota_lvb_in(&qd->qd_qb, qd->qd_gl->gl_lvb);
+
+	if (force_refresh ||
+	    qd->qd_qb.qb_magic != GFS_MAGIC) {
+		gfs_glock_dq_uninit(q_gh);
+		error = gfs_glock_nq_init(qd->qd_gl,
+					  LM_ST_EXCLUSIVE, GL_NOCACHE,
+					  q_gh);
+		if (error)
+			return error;
+
+		error = gfs_glock_nq_init(sdp->sd_qinode->i_gl,
+					  LM_ST_SHARED, 0,
+					  &i_gh);
+		if (error)
+			goto fail;
+
+		memset(buf, 0, sizeof(struct gfs_quota));
+
+		error = gfs_internal_read(sdp->sd_qinode, buf,
+					  (2 * (uint64_t)qd->qd_id +
+					   ((test_bit(QDF_USER, &qd->qd_flags)) ? 0 : 1)) *
+					  sizeof(struct gfs_quota),
+					  sizeof(struct gfs_quota));
+		if (error < 0)
+			goto fail_gunlock;
+
+		gfs_glock_dq_uninit(&i_gh);
+
+		gfs_quota_in(&q, buf);
+
+		memset(&qd->qd_qb, 0, sizeof(struct gfs_quota_lvb));
+		qd->qd_qb.qb_magic = GFS_MAGIC;
+		qd->qd_qb.qb_limit = q.qu_limit;
+		qd->qd_qb.qb_warn = q.qu_warn;
+		qd->qd_qb.qb_value = q.qu_value;
+
+		gfs_quota_lvb_out(&qd->qd_qb, qd->qd_gl->gl_lvb);
+
+		gfs_glock_dq_uninit(q_gh);
+		force_refresh = FALSE;
+		goto restart;
+	}
+
+	return 0;
+
+ fail_gunlock:
+	gfs_glock_dq_uninit(&i_gh);
+
+ fail:
+	gfs_glock_dq_uninit(q_gh);
+
+	return error;
+}
+
+/**
+ * gfs_quota_hold_m - Hold the quota structures for up to 4 IDs
+ * @ip: Two of the IDs are the UID and GID from this file
+ * @uid: a UID or the constant NO_QUOTA_CHANGE
+ * @gid: a GID or the constant NO_QUOTA_CHANGE
+ *
+ * The struct gfs_quota_data structures representing the locks are
+ * stored in the ip->i_alloc->al_qd array.
+ * 
+ * Returns:  errno
+ */
+
+int
+gfs_quota_hold_m(struct gfs_inode *ip, uint32_t uid, uint32_t gid)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	unsigned int x = 0;
+	int error;
+
+	if (gfs_assert_warn(sdp, !al->al_qd_num &&
+			    !test_bit(GIF_QD_LOCKED, &ip->i_flags)))
+		return -EIO;
+
+	if (!gfs_tune_get(sdp, gt_quota_account))
+		return 0;
+
+	error = gfs_quota_get(sdp, TRUE, ip->i_di.di_uid,
+			      CREATE, &al->al_qd[x]);
+	if (error)
+		goto fail;
+	x++;
+
+	error = gfs_quota_get(sdp, FALSE, ip->i_di.di_gid,
+			      CREATE, &al->al_qd[x]);
+	if (error)
+		goto fail;
+	x++;
+
+	if (uid != NO_QUOTA_CHANGE) {
+		error = gfs_quota_get(sdp, TRUE, uid,
+				      CREATE, &al->al_qd[x]);
+		if (error)
+			goto fail;
+		x++;
+	}
+
+	if (gid != NO_QUOTA_CHANGE) {
+		error = gfs_quota_get(sdp, FALSE, gid,
+				      CREATE, &al->al_qd[x]);
+		if (error)
+			goto fail;
+		x++;
+	}
+
+	al->al_qd_num = x;
+
+	return 0;
+
+ fail:
+	if (x) {
+		al->al_qd_num = x;
+		gfs_quota_unhold_m(ip);
+	}
+
+	return error;
+}
+
+/**
+ * gfs_quota_unhold_m - throw away some quota locks
+ * @ip: the inode who's ip->i_alloc->al_qd array holds the structures
+ *
+ */
+
+void
+gfs_quota_unhold_m(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	unsigned int x;
+
+	gfs_assert_warn(sdp, !test_bit(GIF_QD_LOCKED, &ip->i_flags));
+
+	for (x = 0; x < al->al_qd_num; x++) {
+		gfs_quota_put(sdp, al->al_qd[x]);
+		al->al_qd[x] = NULL;
+	}
+	al->al_qd_num = 0;
+}
+
+/**
+ * gfs_quota_lock_m - Acquire the quota locks for up to 4 IDs
+ * @ip: Two of the IDs are the UID and GID from this file
+ * @uid: a UID or the constant NO_QUOTA_CHANGE
+ * @gid: a GID or the constant NO_QUOTA_CHANGE
+ *
+ * The struct gfs_quota_data structures representing the locks are
+ * stored in the ip->i_alloc->al_qd array.
+ * 
+ * Returns:  errno
+ */
+
+int
+gfs_quota_lock_m(struct gfs_inode *ip, uint32_t uid, uint32_t gid)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	unsigned int x;
+	int error;
+
+	gfs_quota_hold_m(ip, uid, gid);
+
+	if (!gfs_tune_get(sdp, gt_quota_enforce))
+		return 0;
+	if (capable(CAP_SYS_RESOURCE))
+		return 0;
+
+	gfs_sort(al->al_qd, al->al_qd_num,
+		 sizeof(struct gfs_quota_data *), sort_qd);
+
+	for (x = 0; x < al->al_qd_num; x++) {
+		error = glock_q(sdp, al->al_qd[x], FALSE, &al->al_qd_ghs[x]);
+		if (error)
+			goto fail;
+	}
+
+	set_bit(GIF_QD_LOCKED, &ip->i_flags);
+
+	return 0;
+
+      fail:
+	while (x--)
+		gfs_glock_dq_uninit(&al->al_qd_ghs[x]);
+
+	return error;
+}
+
+/**
+ * gfs_quota_unlock_m - drop some quota locks
+ * @ip: the inode who's ip->i_alloc->al_qd array holds the locks
+ *
+ */
+
+void
+gfs_quota_unlock_m(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	struct gfs_quota_data *qd, *qda[4];
+	int64_t value;
+	unsigned int count = 0;
+	unsigned int x;
+	int do_sync;
+
+	if (!test_and_clear_bit(GIF_QD_LOCKED, &ip->i_flags))
+		goto out;
+
+	for (x = 0; x < al->al_qd_num; x++) {
+		qd = al->al_qd[x];
+
+		spin_lock(&sdp->sd_quota_lock);
+		value = qd->qd_change_new + qd->qd_change_ic;
+		spin_unlock(&sdp->sd_quota_lock);
+
+		do_sync = TRUE;
+		if (!qd->qd_qb.qb_limit)
+			do_sync = FALSE;
+		else if (qd->qd_qb.qb_value >= (int64_t)qd->qd_qb.qb_limit)
+			do_sync = FALSE;
+		else {
+			struct gfs_tune *gt = &sdp->sd_tune;
+			unsigned int num, den;
+			int64_t v;
+
+			spin_lock(&gt->gt_spin);
+			num = gt->gt_quota_scale_num;
+			den = gt->gt_quota_scale_den;
+			spin_unlock(&gt->gt_spin);
+
+			v = value * gfs_num_journals(sdp) * num;
+			do_div(v, den);
+			v += qd->qd_qb.qb_value;
+			if (v < (int64_t)qd->qd_qb.qb_limit)
+				do_sync = FALSE;
+		}
+
+		gfs_glock_dq_uninit(&al->al_qd_ghs[x]);
+
+		if (do_sync) {
+			gfs_log_flush(sdp);
+			if (quota_trylock(sdp, qd))
+				qda[count++] = qd;
+		}
+	}
+
+	if (count) {
+		do_quota_sync(sdp, qda, count);
+
+		for (x = 0; x < count; x++)
+			quota_unlock(sdp, qda[x]);
+	}
+
+ out:
+	gfs_quota_unhold_m(ip);
+}
+
+/**
+ * print_quota_message - print a message to the user's tty about quotas
+ * @sdp: the filesystem
+ * @qd: the quota ID that the message is about
+ * @type: the type of message ("exceeded" or "warning")
+ *
+ * Returns: errno
+ */
+
+static int
+print_quota_message(struct gfs_sbd *sdp, struct gfs_quota_data *qd, char *type)
+{
+	struct tty_struct *tty;
+	char *line;
+	int len;
+
+	line = kmalloc(256, GFP_KERNEL);
+	if (!line)
+		return -ENOMEM;
+
+	len = snprintf(line, 256, "GFS: fsid=%s: quota %s for %s %u\r\n",
+		       sdp->sd_fsname, type,
+		       (test_bit(QDF_USER, &qd->qd_flags)) ? "user" : "group",
+		       qd->qd_id);
+
+	if (current->signal) {
+		tty = current->signal->tty;
+		if (tty && tty->driver->write)
+			tty->driver->write(tty, 0, line, len);
+	}
+
+	kfree(line);
+
+	return 0;
+}
+
+/**
+ * gfs_quota_check - Check to see if a block allocation is possible
+ * @ip: the inode who's ip->i_res.ir_qd array holds the quota locks
+ * @uid: the UID the block is allocated for
+ * @gid: the GID the block is allocated for
+ *
+ */
+
+int
+gfs_quota_check(struct gfs_inode *ip, uint32_t uid, uint32_t gid)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	struct gfs_quota_data *qd;
+	int64_t value;
+	unsigned int x;
+	int error = 0;
+
+	if (!al)
+		return 0;
+
+        if (!gfs_tune_get(sdp, gt_quota_enforce))
+                return 0;
+
+	for (x = 0; x < al->al_qd_num; x++) {
+		qd = al->al_qd[x];
+
+		if (!((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) ||
+		      (qd->qd_id == gid && !test_bit(QDF_USER, &qd->qd_flags))))
+			continue;
+
+		spin_lock(&sdp->sd_quota_lock);
+		value = qd->qd_change_new + qd->qd_change_ic;
+		spin_unlock(&sdp->sd_quota_lock);
+		value += qd->qd_qb.qb_value;
+
+		if (qd->qd_qb.qb_limit && (int64_t)qd->qd_qb.qb_limit < value) {
+			print_quota_message(sdp, qd, "exceeded");
+			error = -EDQUOT;
+			break;
+		} else if (qd->qd_qb.qb_warn &&
+			   (int64_t)qd->qd_qb.qb_warn < value &&
+			   time_after_eq(jiffies,
+					 qd->qd_last_warn +
+					 gfs_tune_get(sdp, gt_quota_warn_period) * HZ)) {
+			error = print_quota_message(sdp, qd, "warning");
+			qd->qd_last_warn = jiffies;
+		}
+	}
+
+	return error;
+}
+
+/**
+ * gfs_quota_sync - Sync quota changes to the quota file
+ * @sdp: the filesystem
+ *
+ * Returns: errno
+ */
+
+int
+gfs_quota_sync(struct gfs_sbd *sdp)
+{
+	struct gfs_quota_data **qda;
+	unsigned int max_qd = gfs_tune_get(sdp, gt_quota_simul_sync);
+	unsigned int num_qd;
+	unsigned int x;
+	int error = 0;
+
+	sdp->sd_quota_sync_gen++;
+
+	qda = kmalloc(max_qd * sizeof(struct gfs_quota_data *), GFP_KERNEL);
+	if (!qda)
+		return -ENOMEM;
+	memset(qda, 0, max_qd * sizeof(struct gfs_quota_data *));
+
+	do {
+		num_qd = 0;
+
+		for (;;) {
+			qda[num_qd] = quota_find(sdp);
+			if (!qda[num_qd])
+				break;
+
+			if (++num_qd == max_qd)
+				break;
+		}
+
+		if (num_qd) {
+			error = do_quota_sync(sdp, qda, num_qd);
+			if (!error)
+				for (x = 0; x < num_qd; x++)
+					qda[x]->qd_sync_gen =
+						sdp->sd_quota_sync_gen;
+
+			for (x = 0; x < num_qd; x++)
+				quota_unlock(sdp, qda[x]);
+		}
+	}
+	while (!error && num_qd == max_qd);
+
+	kfree(qda);
+
+	return error;
+}
+
+/**
+ * gfs_quota_refresh - Refresh the LVB for a given quota ID
+ * @sdp: the filesystem
+ * @user:
+ * @id:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_quota_refresh(struct gfs_sbd *sdp, int user, uint32_t id)
+{
+	struct gfs_quota_data *qd;
+	struct gfs_holder q_gh;
+	int error;
+
+	error = gfs_quota_get(sdp, user, id, CREATE, &qd);
+	if (error)
+		return error;
+
+	error = glock_q(sdp, qd, TRUE, &q_gh);
+	if (!error)
+		gfs_glock_dq_uninit(&q_gh);
+
+	gfs_quota_put(sdp, qd);
+
+	return error;
+}
+
+/**
+ * gfs_quota_read - Read the info a given quota ID
+ * @sdp: the filesystem
+ * @user:
+ * @id:
+ * @q:
+ *
+ * Returns: errno
+ */
+
+int
+gfs_quota_read(struct gfs_sbd *sdp, int user, uint32_t id,
+	       struct gfs_quota *q)
+{
+	struct gfs_quota_data *qd;
+	struct gfs_holder q_gh;
+	int error;
+
+	if (((user) ? (id != current->fsuid) : (!in_group_p(id))) &&
+	    !capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	error = gfs_quota_get(sdp, user, id, CREATE, &qd);
+	if (error)
+		return error;
+
+	error = glock_q(sdp, qd, FALSE, &q_gh);
+	if (error)
+		goto out;
+
+	memset(q, 0, sizeof(struct gfs_quota));
+	q->qu_limit = qd->qd_qb.qb_limit;
+	q->qu_warn = qd->qd_qb.qb_warn;
+	q->qu_value = qd->qd_qb.qb_value;
+
+	spin_lock(&sdp->sd_quota_lock);
+	q->qu_value += qd->qd_change_new + qd->qd_change_ic;
+	spin_unlock(&sdp->sd_quota_lock);
+
+	gfs_glock_dq_uninit(&q_gh);
+
+ out:
+	gfs_quota_put(sdp, qd);
+
+	return error;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/quota.h linux-2.6.9.debug/fs/gfs/quota.h
--- linux-2.6.9.orig/fs/gfs/quota.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/quota.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,41 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __QUOTA_DOT_H__
+#define __QUOTA_DOT_H__
+
+#define NO_QUOTA_CHANGE ((uint32_t)-1)
+
+int gfs_quota_get(struct gfs_sbd *sdp, int user, uint32_t id, int create,
+		     struct gfs_quota_data **qdp);
+void gfs_quota_hold(struct gfs_sbd *sdp, struct gfs_quota_data *qd);
+void gfs_quota_put(struct gfs_sbd *sdp, struct gfs_quota_data *qd);
+
+int gfs_quota_merge(struct gfs_sbd *sdp, struct gfs_quota_tag *tag);
+void gfs_quota_scan(struct gfs_sbd *sdp);
+void gfs_quota_cleanup(struct gfs_sbd *sdp);
+
+int gfs_quota_hold_m(struct gfs_inode *ip, uint32_t uid, uint32_t gid);
+void gfs_quota_unhold_m(struct gfs_inode *ip);
+
+int gfs_quota_lock_m(struct gfs_inode *ip, uint32_t uid, uint32_t gid);
+void gfs_quota_unlock_m(struct gfs_inode *ip);
+
+int gfs_quota_check(struct gfs_inode *ip, uint32_t uid, uint32_t gid);
+
+int gfs_quota_sync(struct gfs_sbd *sdp);
+int gfs_quota_refresh(struct gfs_sbd *sdp, int user, uint32_t id);
+int gfs_quota_read(struct gfs_sbd *sdp, int user, uint32_t id,
+		   struct gfs_quota *q);
+
+#endif /* __QUOTA_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/recovery.c linux-2.6.9.debug/fs/gfs/recovery.c
--- linux-2.6.9.orig/fs/gfs/recovery.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/recovery.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,779 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "glock.h"
+#include "glops.h"
+#include "lm.h"
+#include "lops.h"
+#include "recovery.h"
+
+#define bn2seg(bn) (((uint32_t)((bn) - jdesc->ji_addr)) / sdp->sd_sb.sb_seg_size)
+#define seg2bn(seg) ((seg) * sdp->sd_sb.sb_seg_size + jdesc->ji_addr)
+
+struct dirty_j {
+	struct list_head dj_list;
+	unsigned int dj_jid;
+	struct gfs_jindex dj_desc;
+};
+
+/**
+ * gfs_add_dirty_j - add a jid to the list of dirty journals
+ * @sdp: the filesystem
+ * @jid: the journal ID number
+ *
+ */
+
+void
+gfs_add_dirty_j(struct gfs_sbd *sdp, unsigned int jid)
+{
+	struct dirty_j *dj;
+
+	dj = gmalloc(sizeof(struct dirty_j));
+	memset(dj, 0, sizeof(struct dirty_j));
+
+	dj->dj_jid = jid;
+
+	spin_lock(&sdp->sd_dirty_j_lock);
+	list_add(&dj->dj_list, &sdp->sd_dirty_j);
+	spin_unlock(&sdp->sd_dirty_j_lock);
+}
+
+/**
+ * get_dirty_j - return a dirty journal from the list
+ * @sdp: the filesystem
+ *
+ * Returns: a struct dirty_j or NULL
+ */
+
+static struct dirty_j *
+get_dirty_j(struct gfs_sbd *sdp)
+{
+	struct dirty_j *dj = NULL;
+
+	spin_lock(&sdp->sd_dirty_j_lock);
+	if (!list_empty(&sdp->sd_dirty_j)) {
+		dj = list_entry(sdp->sd_dirty_j.prev, struct dirty_j, dj_list);
+		list_del(&dj->dj_list);
+	}
+	spin_unlock(&sdp->sd_dirty_j_lock);
+
+	return dj;
+}
+
+/**
+ * gfs_clear_dirty_j - destroy the list of dirty journals
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_clear_dirty_j(struct gfs_sbd *sdp)
+{
+	struct dirty_j *dj;
+	for (;;) {
+		dj = get_dirty_j(sdp);
+		if (!dj)
+			break;
+		kfree(dj);
+	}
+}
+
+/**
+ * gfs_log_header - read the log header for a given segment
+ * @sdp: the filesystem
+ * @jdesc: the journal
+ * @gl: the journal's glock
+ * @seg: the segment to look at
+ * @lh: the log header to return
+ *
+ * Read the log header for a given segement in a given journal.  Do a few
+ * sanity checks on it.
+ *
+ * Returns: 0 on success, 1 if the header was invalid or incomplete and, errno on error
+ */
+
+static int
+get_log_header(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+	       struct gfs_glock *gl, uint32_t seg, struct gfs_log_header *lh)
+{
+	struct buffer_head *bh;
+	struct gfs_log_header lh2;
+	int error;
+
+	error = gfs_dread(gl, seg2bn(seg), DIO_START | DIO_WAIT, &bh);
+	if (error)
+		return error;
+
+	gfs_log_header_in(lh, bh->b_data);
+	gfs_log_header_in(&lh2,
+			  bh->b_data + GFS_BASIC_BLOCK -
+			  sizeof(struct gfs_log_header));
+
+	brelse(bh);
+
+	if (memcmp(lh, &lh2, sizeof(struct gfs_log_header)) != 0 ||
+	    lh->lh_header.mh_magic != GFS_MAGIC ||
+	    lh->lh_header.mh_type != GFS_METATYPE_LH)
+		error = 1;
+
+	return error;
+}
+
+/**
+ * find_good_lh - find a good log header
+ * @sdp: the filesystem
+ * @jdesc: the journal
+ * @gl: the journal's glock
+ * @seg: the segment to start searching from (it's also filled in with a new value.) 
+ * @lh: the log header to fill in
+ * @forward: if true search forward in the log, else search backward
+ *
+ * Call get_log_header() to get a log header for a segment, but if the
+ * segment is bad, either scan forward or backward until we find a good one.
+ *
+ * Returns: errno
+ */
+
+static int
+find_good_lh(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+	     struct gfs_glock *gl, uint32_t *seg, struct gfs_log_header *lh,
+	     int forward)
+{
+	int error;
+	uint32_t orig_seg = *seg;
+
+	for (;;) {
+		error = get_log_header(sdp, jdesc, gl, *seg, lh);
+		if (error <= 0)
+			return error;
+
+		if (forward) {
+			if (++*seg == jdesc->ji_nsegment)
+				*seg = 0;
+		} else {
+			if (*seg-- == 0)
+				*seg = jdesc->ji_nsegment - 1;
+		}
+
+		if (*seg == orig_seg) {
+			gfs_consist(sdp);
+			return -EIO;
+		}
+	}
+}
+
+/**
+ * verify_jhead - make sure we've found the head of the log
+ * @sdp: the filesystem
+ * @jdesc: the journal
+ * @gl: the journal's glock
+ * @head: this is filled in with the log descriptor of the head
+ *
+ * At this point, seg and lh should be either the head of the log or just
+ * before.  Scan forward until we find the head.
+ *
+ * Returns: errno
+ */
+
+static int
+verify_jhead(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+	     struct gfs_glock *gl, struct gfs_log_header *head)
+{
+	struct gfs_log_header lh;
+	uint32_t seg;
+	int error;
+
+	seg = bn2seg(head->lh_first);
+
+	for (;;) {
+		if (++seg == jdesc->ji_nsegment)
+			seg = 0;
+
+		error = get_log_header(sdp, jdesc, gl, seg, &lh);
+		if (error < 0)
+			return error;
+
+		if (error == 1)
+			continue;
+		if (lh.lh_sequence == head->lh_sequence)
+			continue;
+
+		if (lh.lh_sequence < head->lh_sequence)
+			break;
+
+		memcpy(head, &lh, sizeof(struct gfs_log_header));
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_find_jhead - find the head of a log
+ * @sdp: the filesystem
+ * @jdesc: the journal
+ * @gl: the journal's glock
+ * @head: the log descriptor for the head of the log is returned here
+ *
+ * Do a binary search of a journal and find the valid log entry with the
+ * highest sequence number.  (i.e. the log head)
+ *
+ * Returns: errno
+ */
+
+int
+gfs_find_jhead(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+	       struct gfs_glock *gl, struct gfs_log_header *head)
+{
+	struct gfs_log_header lh1, lh_m;
+	uint32_t seg1, seg2, seg_m;
+	int error;
+
+	seg1 = 0;
+	seg2 = jdesc->ji_nsegment - 1;
+
+	for (;;) {
+		seg_m = (seg1 + seg2) / 2;
+
+		error = find_good_lh(sdp, jdesc, gl, &seg1, &lh1, TRUE);
+		if (error)
+			break;
+
+		if (seg1 == seg_m) {
+			error = verify_jhead(sdp, jdesc, gl, &lh1);
+			if (unlikely(error)) {
+				printk("GFS: verify_jhead error=%d\n", error);
+				return error;
+			}
+			memcpy(head, &lh1, sizeof(struct gfs_log_header));
+			break;
+		}
+
+		error = find_good_lh(sdp, jdesc, gl, &seg_m, &lh_m, FALSE);
+		if (error)
+			break;
+
+		if (lh1.lh_sequence <= lh_m.lh_sequence)
+			seg1 = seg_m;
+		else
+			seg2 = seg_m;
+	}
+
+	return error;
+}
+
+/**
+ * gfs_increment_blkno - move to the next block in a journal
+ * @sdp: the filesystem
+ * @jdesc: the journal
+ * @gl: the journal's glock
+ * @addr: the block number to increment
+ * @skip_header: if this is TRUE, skip log headers
+ *
+ * Replace @addr with the location of the next block in the log.
+ * Take care of journal wrap and skip of log header if necessary.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_increment_blkno(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+		    struct gfs_glock *gl, uint64_t *addr, int skip_headers)
+{
+	struct gfs_log_header header;
+	int error;
+
+	(*addr)++;
+
+	/* Handle journal wrap */
+
+	if (*addr == seg2bn(jdesc->ji_nsegment))
+		*addr -= jdesc->ji_nsegment * sdp->sd_sb.sb_seg_size;
+
+	gfs_start_ra(gl, *addr,
+		     jdesc->ji_addr +
+		     jdesc->ji_nsegment * sdp->sd_sb.sb_seg_size - *addr);
+
+	/* Handle landing on a header block */
+
+	if (skip_headers && !do_mod(*addr, sdp->sd_sb.sb_seg_size)) {
+		error = get_log_header(sdp, jdesc, gl, bn2seg(*addr), &header);
+		if (error < 0)
+			return error;
+
+		if (error) { /* Corrupt headers here are bad */
+			if (gfs_consist(sdp))
+				printk("GFS: fsid=%s: *addr = %"PRIu64"\n",
+				       sdp->sd_fsname, *addr);
+			return -EIO;
+		}
+		if (header.lh_first == *addr) {
+			if (gfs_consist(sdp))
+				printk("GFS: fsid=%s: *addr = %"PRIu64"\n",
+				       sdp->sd_fsname, *addr);
+			gfs_log_header_print(&header);
+			return -EIO;
+		}
+
+		(*addr)++;
+		/* Can't wrap here */
+	}
+
+	return 0;
+}
+
+/**
+ * foreach_descriptor - go through the active part of the log
+ * @sdp: the filesystem
+ * @jdesc: the journal
+ * @gl: the journal's glock
+ * @start: the first log header in the active region
+ * @end: the last log header (don't process the contents of this entry))
+ * @pass: the recovery pass
+ *
+ * Call a given function once for every log descriptor in the active
+ * portion of the log.
+ *
+ * Returns: errno
+ */
+
+static int
+foreach_descriptor(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+		   struct gfs_glock *gl, uint64_t start, uint64_t end,
+		   unsigned int pass)
+{
+	struct gfs_log_header header;
+	struct gfs_log_descriptor desc;
+	struct buffer_head *bh;
+	int error = 0;
+
+	while (start != end) {
+		if (do_mod(start, sdp->sd_sb.sb_seg_size)) {
+			gfs_consist(sdp);
+			return -EIO;
+		}
+
+		error = get_log_header(sdp, jdesc, gl, bn2seg(start), &header);
+		if (error < 0)
+			return error;
+
+		if (error) { /* Corrupt headers here are bad */
+			if (gfs_consist(sdp))
+				printk("GFS: fsid=%s: start = %"PRIu64"\n",
+				       sdp->sd_fsname, start);
+			return -EIO;
+		}
+		if (header.lh_first != start) {
+			if (gfs_consist(sdp))
+				printk("GFS: fsid=%s: start = %"PRIu64"\n",
+				       sdp->sd_fsname, start);
+			gfs_log_header_print(&header);
+			return -EIO;
+		}
+
+		start++;
+
+		for (;;) {
+			error = gfs_dread(gl, start, DIO_START | DIO_WAIT, &bh);
+			if (error)
+				return error;
+
+			if (gfs_metatype_check(sdp, bh, GFS_METATYPE_LD)) {
+				brelse(bh);
+				return -EIO;
+			}
+
+			gfs_desc_in(&desc, bh->b_data);
+			brelse(bh);
+
+			if (desc.ld_type != GFS_LOG_DESC_LAST) {
+				error = LO_SCAN_ELEMENTS(sdp, jdesc, gl, start,
+							 &desc, pass);
+				if (error)
+					return error;
+
+				while (desc.ld_length--) {
+					error = gfs_increment_blkno(sdp, jdesc, gl,
+								    &start, TRUE);
+					if (error)
+						return error;
+				}
+			} else {
+				while (desc.ld_length--) {
+					error = gfs_increment_blkno(sdp, jdesc, gl,
+								    &start,
+								    !!desc.ld_length);
+					if (error)
+						return error;
+				}
+
+				break;
+			}
+		}
+	}
+
+	return error;
+}
+
+/**
+ * clean_journal - mark a dirty journal as being clean
+ * @sdp: the filesystem
+ * @jdesc: the journal
+ * @gl: the journal's glock
+ * @head: the head journal to start from
+ *
+ * Returns: errno
+ */
+
+static int
+clean_journal(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+	      struct gfs_glock *gl, struct gfs_log_header *head)
+{
+	struct gfs_log_header lh;
+	struct gfs_log_descriptor desc;
+	struct buffer_head *bh;
+	uint32_t seg;
+	uint64_t blkno;
+	int error;
+
+	seg = bn2seg(head->lh_first);
+
+	for (;;) {
+		if (++seg == jdesc->ji_nsegment)
+			seg = 0;
+
+		error = get_log_header(sdp, jdesc, gl, seg, &lh);
+		if (error < 0)
+			return error;
+
+		/* Rewrite corrupt header blocks */
+
+		if (error == 1) {
+			bh = gfs_dgetblk(gl, seg2bn(seg));
+
+			gfs_prep_new_buffer(bh);
+			gfs_buffer_clear(bh);
+			gfs_log_header_out(head, bh->b_data);
+			gfs_log_header_out(head,
+					   bh->b_data + GFS_BASIC_BLOCK -
+					   sizeof(struct gfs_log_header));
+
+			error = gfs_dwrite(sdp, bh, DIO_DIRTY | DIO_START | DIO_WAIT);
+			brelse(bh);
+			if (error)
+				return error;
+		}
+
+		/* Stop when we get to the end of the log. */
+
+		if (lh.lh_sequence < head->lh_sequence)
+			break;
+	}
+
+	/*  Build a "last" descriptor for the transaction we are
+	   about to commit by writing the shutdown header.  */
+
+	memset(&desc, 0, sizeof(struct gfs_log_descriptor));
+	desc.ld_header.mh_magic = GFS_MAGIC;
+	desc.ld_header.mh_type = GFS_METATYPE_LD;
+	desc.ld_header.mh_format = GFS_FORMAT_LD;
+	desc.ld_type = GFS_LOG_DESC_LAST;
+	desc.ld_length = 0;
+
+	for (blkno = head->lh_first + 1; blkno != seg2bn(seg);) {
+		if (do_mod(blkno, sdp->sd_sb.sb_seg_size))
+			desc.ld_length++;
+		if (++blkno == seg2bn(jdesc->ji_nsegment))
+			blkno -= jdesc->ji_nsegment * sdp->sd_sb.sb_seg_size;
+	}
+
+	/*  Write the descriptor  */
+
+	bh = gfs_dgetblk(gl, head->lh_first + 1);
+
+	gfs_prep_new_buffer(bh);
+	gfs_buffer_clear(bh);
+	gfs_desc_out(&desc, bh->b_data);
+
+	error = gfs_dwrite(sdp, bh, DIO_DIRTY | DIO_START | DIO_WAIT);
+	brelse(bh);
+	if (error)
+		return error;
+
+	/*  Build a log header that says the journal is clean  */
+
+	memset(&lh, 0, sizeof(struct gfs_log_header));
+	lh.lh_header.mh_magic = GFS_MAGIC;
+	lh.lh_header.mh_type = GFS_METATYPE_LH;
+	lh.lh_header.mh_format = GFS_FORMAT_LH;
+	lh.lh_flags = GFS_LOG_HEAD_UNMOUNT;
+	lh.lh_first = seg2bn(seg);
+	lh.lh_sequence = head->lh_sequence + 1;
+	/*  Don't care about tail  */
+	lh.lh_last_dump = head->lh_last_dump;
+
+	/*  Write the header  */
+
+	bh = gfs_dgetblk(gl, lh.lh_first);
+
+	gfs_prep_new_buffer(bh);
+	gfs_buffer_clear(bh);
+	gfs_log_header_out(&lh, bh->b_data);
+	gfs_log_header_out(&lh,
+			   bh->b_data + GFS_BASIC_BLOCK -
+			   sizeof(struct gfs_log_header));
+
+	error = gfs_dwrite(sdp, bh, DIO_DIRTY | DIO_START | DIO_WAIT);
+	brelse(bh);
+
+	return error;
+}
+
+/**
+ * gfs_recover_journal - recovery a given journal
+ * @sdp: the filesystem
+ * @jid: the number of the journal to recover
+ * @jdesc: the struct gfs_jindex describing the journal
+ * @wait: Don't return until the journal is clean (or an error is encountered)
+ *
+ * Acquire the journal's lock, check to see if the journal is clean, and
+ * do recovery if necessary.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_recover_journal(struct gfs_sbd *sdp,
+		    unsigned int jid, struct gfs_jindex *jdesc,
+		    int wait)
+{
+	struct gfs_log_header head;
+	struct gfs_holder j_gh, t_gh;
+	unsigned long t;
+	int error;
+
+	printk("GFS: fsid=%s: jid=%u: Trying to acquire journal lock...\n",
+	       sdp->sd_fsname, jid);
+
+	/*  Aquire the journal lock so we can do recovery  */
+
+	error = gfs_glock_nq_num(sdp,
+				 jdesc->ji_addr, &gfs_meta_glops,
+				 LM_ST_EXCLUSIVE,
+				 LM_FLAG_NOEXP |
+				 ((wait) ? 0 : LM_FLAG_TRY) |
+				 GL_NOCACHE, &j_gh);
+	switch (error) {
+	case 0:
+		break;
+
+	case GLR_TRYFAILED:
+		printk("GFS: fsid=%s: jid=%u: Busy\n", sdp->sd_fsname, jid);
+		error = 0;
+
+	default:
+		goto fail;
+	};
+
+	printk("GFS: fsid=%s: jid=%u: Looking at journal...\n",
+	       sdp->sd_fsname, jid);
+
+	error = gfs_find_jhead(sdp, jdesc, j_gh.gh_gl, &head);
+	if (error)
+		goto fail_gunlock;
+
+	if (!(head.lh_flags & GFS_LOG_HEAD_UNMOUNT)) {
+		if (test_bit(SDF_ROFS, &sdp->sd_flags)) {
+			printk("GFS: fsid=%s: jid=%u: Can't replay: read-only FS\n",
+			       sdp->sd_fsname, jid);
+			error = -EROFS;
+			goto fail_gunlock;
+		}
+
+		printk("GFS: fsid=%s: jid=%u: Acquiring the transaction lock...\n",
+		       sdp->sd_fsname, jid);
+
+		t = jiffies;
+
+		/*  Acquire an exclusive hold on the transaction lock  */
+
+		error = gfs_glock_nq_init(sdp->sd_trans_gl,
+					  LM_ST_EXCLUSIVE,
+					  LM_FLAG_NOEXP |
+					  LM_FLAG_PRIORITY |
+					  GL_NOCANCEL |
+					  GL_NOCACHE,
+					  &t_gh);
+		if (error)
+			goto fail_gunlock;
+
+		if (test_bit(SDF_ROFS, &sdp->sd_flags)) {
+			printk("GFS: fsid=%s: jid=%u: Can't replay: read-only FS\n",
+			       sdp->sd_fsname, jid);
+			error = -EROFS;
+			goto fail_gunlock_tr;
+		}
+
+		printk("GFS: fsid=%s: jid=%u: Replaying journal...\n",
+		       sdp->sd_fsname, jid);
+
+		set_bit(GLF_DIRTY, &j_gh.gh_gl->gl_flags);
+
+		LO_BEFORE_SCAN(sdp, jid, &head, GFS_RECPASS_A1);
+
+		error = foreach_descriptor(sdp, jdesc, j_gh.gh_gl,
+					   head.lh_tail, head.lh_first,
+					   GFS_RECPASS_A1);
+		if (error)
+			goto fail_gunlock_tr;
+
+		LO_AFTER_SCAN(sdp, jid, GFS_RECPASS_A1);
+
+		gfs_replay_wait(sdp);
+
+		error = clean_journal(sdp, jdesc, j_gh.gh_gl, &head);
+		if (error)
+			goto fail_gunlock_tr;
+
+		gfs_glock_dq_uninit(&t_gh);
+
+		t = DIV_RU(jiffies - t, HZ);
+		
+		printk("GFS: fsid=%s: jid=%u: Journal replayed in %lus\n",
+		       sdp->sd_fsname, jid, t);
+	}
+
+	gfs_lm_recovery_done(sdp, jid, LM_RD_SUCCESS);
+
+	gfs_glock_dq_uninit(&j_gh);
+
+	printk("GFS: fsid=%s: jid=%u: Done\n", sdp->sd_fsname, jid);
+
+	return 0;
+
+ fail_gunlock_tr:
+	gfs_replay_wait(sdp);
+	gfs_glock_dq_uninit(&t_gh);
+
+ fail_gunlock:
+	gfs_glock_dq_uninit(&j_gh);
+
+	printk("GFS: fsid=%s: jid=%u: %s\n",
+	       sdp->sd_fsname, jid, (error) ? "Failed" : "Done");
+
+ fail:
+	gfs_lm_recovery_done(sdp, jid, LM_RD_GAVEUP);
+
+	return error;
+}
+
+/**
+ * gfs_check_journals - Recover any dirty journals
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_check_journals(struct gfs_sbd *sdp)
+{
+	struct dirty_j *dj;
+
+	for (;;) {
+		dj = get_dirty_j(sdp);
+		if (!dj)
+			break;
+
+		down(&sdp->sd_jindex_lock);
+
+		if (dj->dj_jid != sdp->sd_lockstruct.ls_jid &&
+		    dj->dj_jid < sdp->sd_journals) {
+			memcpy(&dj->dj_desc,
+			       sdp->sd_jindex + dj->dj_jid,
+			       sizeof(struct gfs_jindex));
+			up(&sdp->sd_jindex_lock);
+
+			gfs_recover_journal(sdp,
+					    dj->dj_jid, &dj->dj_desc,
+					    FALSE);
+			
+		} else {
+			up(&sdp->sd_jindex_lock);
+			gfs_lm_recovery_done(sdp, dj->dj_jid, LM_RD_GAVEUP);
+		}
+
+		kfree(dj);
+	}
+}
+
+/**
+ * gfs_recover_dump - recover the log elements in this machine's journal
+ * @sdp: the filesystem
+ *
+ * Returns: errno
+ */
+
+int
+gfs_recover_dump(struct gfs_sbd *sdp)
+{
+	struct gfs_log_header head;
+	int error;
+
+	error = gfs_find_jhead(sdp, &sdp->sd_jdesc, sdp->sd_journal_gh.gh_gl,
+			       &head);
+	if (error)
+		goto fail;
+
+	if (!(head.lh_flags & GFS_LOG_HEAD_UNMOUNT)) {
+		gfs_consist(sdp);
+		return -EIO;
+	}
+	if (!head.lh_last_dump)
+		return error;
+
+	printk("GFS: fsid=%s: Scanning for log elements...\n",
+	       sdp->sd_fsname);
+
+	LO_BEFORE_SCAN(sdp, sdp->sd_lockstruct.ls_jid, &head, GFS_RECPASS_B1);
+
+	error = foreach_descriptor(sdp, &sdp->sd_jdesc, sdp->sd_journal_gh.gh_gl,
+				   head.lh_last_dump, head.lh_first,
+				   GFS_RECPASS_B1);
+	if (error)
+		goto fail;
+
+	LO_AFTER_SCAN(sdp, sdp->sd_lockstruct.ls_jid, GFS_RECPASS_B1);
+
+	/* We need to make sure if we crash during the next log dump that
+	   all intermediate headers in the transaction point to the last
+	   log dump before the one we're making so we don't lose it. */
+
+	sdp->sd_log_dump_last = head.lh_last_dump;
+
+	printk("GFS: fsid=%s: Done\n", sdp->sd_fsname);
+
+	return 0;
+
+ fail:
+	printk("GFS: fsid=%s: Failed\n", sdp->sd_fsname);
+
+	return error;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/recovery.h linux-2.6.9.debug/fs/gfs/recovery.h
--- linux-2.6.9.orig/fs/gfs/recovery.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/recovery.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,36 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __RECOVERY_DOT_H__
+#define __RECOVERY_DOT_H__
+
+#define GFS_RECPASS_A1  (12)
+#define GFS_RECPASS_B1  (14)
+
+void gfs_add_dirty_j(struct gfs_sbd *sdp, unsigned int jid);
+void gfs_clear_dirty_j(struct gfs_sbd *sdp);
+
+int gfs_find_jhead(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+		   struct gfs_glock *gl, struct gfs_log_header *head);
+int gfs_increment_blkno(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
+			struct gfs_glock *gl, uint64_t *addr,
+			int skip_headers);
+
+int gfs_recover_journal(struct gfs_sbd *sdp,
+			unsigned int jid, struct gfs_jindex *jdesc,
+			int wait);
+void gfs_check_journals(struct gfs_sbd *sdp);
+
+int gfs_recover_dump(struct gfs_sbd *sdp);
+
+#endif /* __RECOVERY_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/rgrp.c linux-2.6.9.debug/fs/gfs/rgrp.c
--- linux-2.6.9.orig/fs/gfs/rgrp.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/rgrp.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,2141 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "bits.h"
+#include "dio.h"
+#include "file.h"
+#include "glock.h"
+#include "glops.h"
+#include "rgrp.h"
+#include "super.h"
+#include "trans.h"
+
+/**
+ * mhc_hash: find the mhc hash bucket for a buffer
+ * @bh: the buffer
+ *
+ * Returns: The bucket number
+ */
+
+static unsigned int
+mhc_hash(struct buffer_head *bh)
+{
+	uint64_t blkno;
+	unsigned int h;
+
+	blkno = bh->b_blocknr;
+	h = gfs_hash(&blkno, sizeof(uint64_t)) & GFS_MHC_HASH_MASK;
+
+	return h;
+}
+
+/**
+ * mhc_trim - Throw away cached meta-headers, if there are too many of them
+ * @sdp:  The filesystem instance
+ * @max:  Max # of cached meta-headers allowed to survive
+ *
+ * Walk filesystem's list of cached meta-headers, in least-recently-used order,
+ *   and keep throwing them away until we're under the max threshold. 
+ */
+
+static void
+mhc_trim(struct gfs_sbd *sdp, unsigned int max)
+{
+	struct gfs_meta_header_cache *mc;
+
+	for (;;) {
+		spin_lock(&sdp->sd_mhc_lock);
+		if (list_empty(&sdp->sd_mhc_single)) {
+			spin_unlock(&sdp->sd_mhc_lock);
+			return;
+		} else {
+			mc = list_entry(sdp->sd_mhc_single.prev,
+					struct gfs_meta_header_cache,
+					mc_list_single);
+			list_del(&mc->mc_list_hash);
+			list_del(&mc->mc_list_single);
+			list_del(&mc->mc_list_rgd);
+			spin_unlock(&sdp->sd_mhc_lock);
+
+			kmem_cache_free(gfs_mhc_cachep, mc);
+			atomic_dec(&sdp->sd_mhc_count);
+
+			if (atomic_read(&sdp->sd_mhc_count) <= max)
+				return;
+		}
+	}
+}
+
+/**
+ * gfs_mhc_add - add buffer(s) to the cache of metadata headers
+ * @rgd: Resource Group in which the buffered block(s) reside
+ * @bh: an array of buffer_head pointers
+ * @num: the number of bh pointers in the array
+ *
+ * Increment each meta-header's generation # by 2.
+ * Alloc and add each gfs_meta-header_cache to 3 lists/caches:
+ *   Filesystem's meta-header cache (hash)
+ *   Filesystem's list of cached meta-headers
+ *   Resource Group's list of cached meta-headers
+ * If we now have too many cached, throw some older ones away
+ */
+
+void
+gfs_mhc_add(struct gfs_rgrpd *rgd,
+	    struct buffer_head **bh, unsigned int num)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	unsigned int x;
+
+	for (x = 0; x < num; x++) {
+		struct gfs_meta_header_cache *mc;
+		struct list_head *head;
+		uint64_t gen;
+
+		if (gfs_meta_check(sdp, bh[x]))
+			return;
+
+		mc = kmem_cache_alloc(gfs_mhc_cachep, GFP_KERNEL);
+		if (!mc)
+			return;
+		memset(mc, 0, sizeof(struct gfs_meta_header_cache));
+
+		mc->mc_block = bh[x]->b_blocknr;
+		memcpy(&mc->mc_mh, bh[x]->b_data,
+		       sizeof(struct gfs_meta_header));
+
+		gen = gfs64_to_cpu(mc->mc_mh.mh_generation) + 2;
+		mc->mc_mh.mh_generation = cpu_to_gfs64(gen);
+
+		head = &sdp->sd_mhc[mhc_hash(bh[x])];
+
+		spin_lock(&sdp->sd_mhc_lock);
+		list_add(&mc->mc_list_hash, head);
+		list_add(&mc->mc_list_single, &sdp->sd_mhc_single);
+		list_add(&mc->mc_list_rgd, &rgd->rd_mhc);
+		spin_unlock(&sdp->sd_mhc_lock);
+
+		atomic_inc(&sdp->sd_mhc_count);
+	}
+
+	x = gfs_tune_get(sdp, gt_max_mhc);
+
+	/* If we've got too many cached, throw some older ones away */
+	if (atomic_read(&sdp->sd_mhc_count) > x)
+		mhc_trim(sdp, x);
+}
+
+/**
+ * gfs_mhc_fish - Try to fill in a meta buffer with meta-header from the cache
+ * @sdp: the filesystem
+ * @bh: the buffer to fill in
+ *
+ * Returns: TRUE if the buffer was cached, FALSE otherwise
+ *
+ * If buffer is referenced in meta-header cache (search using hash):
+ *   Copy the cached meta-header into the buffer (instead of reading from disk).
+ *     Note that only the meta-header portion of the buffer will have valid data
+ *     (as would be on disk), rest of buffer does *not* reflect disk contents.
+ *   Remove cached gfs_meta_header_cache from all cache lists, free its memory.
+ */
+
+int
+gfs_mhc_fish(struct gfs_sbd *sdp, struct buffer_head *bh)
+{
+	struct list_head *tmp, *head;
+	struct gfs_meta_header_cache *mc;
+
+	head = &sdp->sd_mhc[mhc_hash(bh)];
+
+	spin_lock(&sdp->sd_mhc_lock);
+
+	for (tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		mc = list_entry(tmp, struct gfs_meta_header_cache, mc_list_hash);
+		if (mc->mc_block != bh->b_blocknr)
+			continue;
+
+		list_del(&mc->mc_list_hash);
+		list_del(&mc->mc_list_single);
+		list_del(&mc->mc_list_rgd);
+		spin_unlock(&sdp->sd_mhc_lock);
+
+		gfs_prep_new_buffer(bh);
+		memcpy(bh->b_data, &mc->mc_mh,
+		       sizeof(struct gfs_meta_header));
+
+		kmem_cache_free(gfs_mhc_cachep, mc);
+		atomic_dec(&sdp->sd_mhc_count);
+
+		return TRUE;
+	}
+
+	spin_unlock(&sdp->sd_mhc_lock);
+
+	return FALSE;
+}
+
+/**
+ * gfs_mhc_zap - Throw away an RG's list of cached metadata headers
+ * @rgd: The resource group whose list we want to clear
+ *
+ * Simply throw away all cached metadata headers on RG's list,
+ *   and free their memory.
+ */
+
+void
+gfs_mhc_zap(struct gfs_rgrpd *rgd)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	struct gfs_meta_header_cache *mc;
+
+	spin_lock(&sdp->sd_mhc_lock);
+
+	while (!list_empty(&rgd->rd_mhc)) {
+		mc = list_entry(rgd->rd_mhc.next,
+				struct gfs_meta_header_cache,
+				mc_list_rgd);
+
+		list_del(&mc->mc_list_hash);
+		list_del(&mc->mc_list_single);
+		list_del(&mc->mc_list_rgd);
+		spin_unlock(&sdp->sd_mhc_lock);
+
+		kmem_cache_free(gfs_mhc_cachep, mc);
+		atomic_dec(&sdp->sd_mhc_count);
+
+		spin_lock(&sdp->sd_mhc_lock);
+	}
+
+	spin_unlock(&sdp->sd_mhc_lock);
+}
+
+/**
+ * depend_hash() - Turn glock number into hash bucket number
+ * @formal_ino:
+ *
+ * Returns: The number of the corresponding hash bucket
+ */
+
+static unsigned int
+depend_hash(uint64_t formal_ino)
+{
+	unsigned int h;
+
+	h = gfs_hash(&formal_ino, sizeof(uint64_t));
+	h &= GFS_DEPEND_HASH_MASK;
+
+	return h;
+}
+
+/**
+ * depend_sync_one - Sync metadata (not data) for a dependency inode
+ * @sdp: filesystem instance
+ * @gd: dependency descriptor
+ *
+ * Remove dependency from superblock's hash table and rgrp's list.
+ * Sync dependency inode's metadata to log and in-place location.
+ */
+
+static void
+depend_sync_one(struct gfs_sbd *sdp, struct gfs_depend *gd)
+{
+	struct gfs_glock *gl;
+
+	spin_lock(&sdp->sd_depend_lock);
+	list_del(&gd->gd_list_hash);
+	spin_unlock(&sdp->sd_depend_lock);
+	list_del(&gd->gd_list_rgd);
+
+	gl = gfs_glock_find(sdp,
+			    &(struct lm_lockname){gd->gd_formal_ino,
+						  LM_TYPE_INODE});
+	if (gl) {
+		if (gl->gl_ops->go_sync)
+			gl->gl_ops->go_sync(gl,
+					    DIO_METADATA |
+					    DIO_INVISIBLE);
+		gfs_glock_put(gl);
+	}
+
+	kfree(gd);
+	atomic_dec(&sdp->sd_depend_count);
+}
+
+/**
+ * depend_sync_old - Sync older rgrp-dependent inodes to disk.
+ * @rgd: Resource group containing dependent inodes
+ *
+ * Look at oldest entries in resource group's dependency list,
+ *   sync 'em if they're older than timeout threshold.
+ */
+
+static void
+depend_sync_old(struct gfs_rgrpd *rgd)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	struct gfs_depend *gd;
+
+	while (!list_empty(&rgd->rd_depend)) {
+		/* Oldest entries are in prev direction */
+		gd = list_entry(rgd->rd_depend.prev,
+				struct gfs_depend,
+				gd_list_rgd);
+
+		if (time_before(jiffies,
+				gd->gd_time +
+				gfs_tune_get(sdp, gt_depend_secs) * HZ))
+			return;
+
+		depend_sync_one(sdp, gd);
+	}
+}
+
+/**
+ * gfs_depend_add - Add a dependent inode to rgrp's and filesystem's list
+ * @rgd: Resource group containing blocks associated with inode
+ * @formal_ino: inode
+ *
+ * Dependent inodes must be flushed to log and in-place blocks before
+ *   releasing an EXCLUSIVE rgrp lock.
+ * Find pre-existing dependency for this inode/rgrp combination in
+ *   incore superblock struct's sd_depend hash table, or create a new one.
+ * Either way, move or attach dependency to head of superblock's hash bucket
+ *   and top of rgrp's list.
+ * If we create a new one, take a moment to sync older dependencies to disk.
+ */
+
+void
+gfs_depend_add(struct gfs_rgrpd *rgd, uint64_t formal_ino)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	struct list_head *head, *tmp;
+	struct gfs_depend *gd;
+
+	head = &sdp->sd_depend[depend_hash(formal_ino)];
+
+	spin_lock(&sdp->sd_depend_lock);
+
+	for (tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		gd = list_entry(tmp, struct gfs_depend, gd_list_hash);
+		if (gd->gd_rgd == rgd &&
+		    gd->gd_formal_ino == formal_ino) {
+			list_move(&gd->gd_list_hash, head);
+			spin_unlock(&sdp->sd_depend_lock);
+			list_move(&gd->gd_list_rgd, &rgd->rd_depend);
+			gd->gd_time = jiffies;
+			return;
+		}
+	}
+
+	spin_unlock(&sdp->sd_depend_lock);
+
+	gd = gmalloc(sizeof(struct gfs_depend));
+	memset(gd, 0, sizeof(struct gfs_depend));
+
+	gd->gd_rgd = rgd;
+	gd->gd_formal_ino = formal_ino;
+	gd->gd_time = jiffies;
+
+	spin_lock(&sdp->sd_depend_lock);
+	list_add(&gd->gd_list_hash, head);
+	spin_unlock(&sdp->sd_depend_lock);
+	list_add(&gd->gd_list_rgd, &rgd->rd_depend);
+
+	atomic_inc(&sdp->sd_depend_count);
+
+	depend_sync_old(rgd);
+}
+
+/**
+ * gfs_depend_sync - Sync metadata (not data) for an rgrp's dependent inodes
+ * @rgd: Resource group containing the dependent inodes
+ *
+ * As long as this node owns an EXCLUSIVE lock on the rgrp, we can keep
+ *   rgrp's modified metadata blocks in buffer cache.
+ *
+ * When this node releases the EX lock, we must flush metadata, so other
+ *   nodes can read the modified content from disk.
+ */
+
+void
+gfs_depend_sync(struct gfs_rgrpd *rgd)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	struct gfs_depend *gd;
+
+	while (!list_empty(&rgd->rd_depend)) {
+		gd = list_entry(rgd->rd_depend.next,
+				struct gfs_depend,
+				gd_list_rgd);
+		depend_sync_one(sdp, gd);
+	}
+}
+
+/**
+ * rgrp_verify - Verify that a resource group is consistent
+ * @sdp: the filesystem
+ * @rgd: the rgrp
+ *
+ * Somebody should have already called gfs_glock_rg() on this RG.
+ */
+
+static void
+rgrp_verify(struct gfs_rgrpd *rgd)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	struct gfs_bitmap *bits = NULL;
+	uint32_t length = rgd->rd_ri.ri_length;
+	uint32_t count[4], tmp;
+	int buf, x;
+
+	memset(count, 0, 4 * sizeof(uint32_t));
+
+	/* Count # blocks in each of 4 possible allocation states */
+	for (buf = 0; buf < length; buf++) {
+		bits = &rgd->rd_bits[buf];
+		for (x = 0; x < 4; x++)
+			count[x] += gfs_bitcount(rgd,
+						 rgd->rd_bh[buf]->b_data +
+						 bits->bi_offset,
+						 bits->bi_len, x);
+	}
+
+	if (count[0] != rgd->rd_rg.rg_free) {
+		if (gfs_consist_rgrpd(rgd))
+			printk("GFS: fsid=%s: free data mismatch:  %u != %u\n",
+			       sdp->sd_fsname, count[0], rgd->rd_rg.rg_free);
+		return;
+	}
+
+	tmp = rgd->rd_ri.ri_data -
+		(rgd->rd_rg.rg_usedmeta + rgd->rd_rg.rg_freemeta) -
+		(rgd->rd_rg.rg_useddi + rgd->rd_rg.rg_freedi) -
+		rgd->rd_rg.rg_free;
+	if (count[1] != tmp) {
+		if (gfs_consist_rgrpd(rgd))
+			printk("GFS: fsid=%s: used data mismatch:  %u != %u\n",
+			       sdp->sd_fsname, count[1], tmp);
+		return;
+	}
+
+	if (count[2] != rgd->rd_rg.rg_freemeta) {
+		if (gfs_consist_rgrpd(rgd))
+			printk("GFS: fsid=%s: free metadata mismatch:  %u != %u\n",
+			       sdp->sd_fsname, count[2], rgd->rd_rg.rg_freemeta);
+		return;
+	}
+
+	tmp = rgd->rd_rg.rg_usedmeta +
+		(rgd->rd_rg.rg_useddi + rgd->rd_rg.rg_freedi);
+	if (count[3] != tmp) {
+		if (gfs_consist_rgrpd(rgd))
+			printk("GFS: fsid=%s: used metadata mismatch:  %u != %u\n",
+			       sdp->sd_fsname, count[3], tmp);
+		return;
+	}
+}
+
+/**
+ * gfs_blk2rgrpd - Find resource group for a given data/meta block number
+ * @sdp: The GFS superblock
+ * @n: The data block number
+ *
+ * Returns: The resource group, or NULL if not found
+ *
+ * Don't try to use this for non-allocatable block numbers (i.e. rgrp header
+ *   or bitmap blocks); it's for allocatable (data/meta) blocks only.
+ */
+
+struct gfs_rgrpd *
+gfs_blk2rgrpd(struct gfs_sbd *sdp, uint64_t blk)
+{
+	struct list_head *tmp, *head;
+	struct gfs_rgrpd *rgd = NULL;
+	struct gfs_rindex *ri;
+
+	spin_lock(&sdp->sd_rg_mru_lock);
+
+	for (head = &sdp->sd_rg_mru_list, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		rgd = list_entry(tmp, struct gfs_rgrpd, rd_list_mru);
+		ri = &rgd->rd_ri;
+
+		if (ri->ri_data1 <= blk && blk < ri->ri_data1 + ri->ri_data) {
+			list_move(&rgd->rd_list_mru, &sdp->sd_rg_mru_list);
+			spin_unlock(&sdp->sd_rg_mru_lock);
+			return rgd;
+		}
+	}
+
+	spin_unlock(&sdp->sd_rg_mru_lock);
+
+	return NULL;
+}
+
+/**
+ * gfs_rgrpd_get_first - get the first Resource Group in the filesystem
+ * @sdp: The GFS superblock
+ *
+ * Returns: The first rgrp in the filesystem
+ */
+
+struct gfs_rgrpd *
+gfs_rgrpd_get_first(struct gfs_sbd *sdp)
+{
+	gfs_assert(sdp, !list_empty(&sdp->sd_rglist),);
+	return list_entry(sdp->sd_rglist.next, struct gfs_rgrpd, rd_list);
+}
+
+/**
+ * gfs_rgrpd_get_next - get the next RG
+ * @rgd: A RG
+ *
+ * Returns: The next rgrp
+ */
+
+struct gfs_rgrpd *
+gfs_rgrpd_get_next(struct gfs_rgrpd *rgd)
+{
+	if (rgd->rd_list.next == &rgd->rd_sbd->sd_rglist)
+		return NULL;
+	return list_entry(rgd->rd_list.next, struct gfs_rgrpd, rd_list);
+}
+
+/**
+ * clear_rgrpdi - Clear up rgrps
+ * @sdp: The GFS superblock
+ *
+ */
+
+void
+clear_rgrpdi(struct gfs_sbd *sdp)
+{
+	struct gfs_rgrpd *rgd;
+	struct gfs_glock *gl;
+
+	spin_lock(&sdp->sd_rg_forward_lock);
+	sdp->sd_rg_forward = NULL;
+	spin_unlock(&sdp->sd_rg_forward_lock);
+
+	spin_lock(&sdp->sd_rg_recent_lock);
+	while (!list_empty(&sdp->sd_rg_recent)) {
+		rgd = list_entry(sdp->sd_rg_recent.next,
+				 struct gfs_rgrpd, rd_recent);
+		list_del(&rgd->rd_recent);
+	}
+	spin_unlock(&sdp->sd_rg_recent_lock);
+
+	while (!list_empty(&sdp->sd_rglist)) {
+		rgd = list_entry(sdp->sd_rglist.next,
+				 struct gfs_rgrpd, rd_list);
+		gl = rgd->rd_gl;
+
+		list_del(&rgd->rd_list);
+		list_del(&rgd->rd_list_mru);
+
+		if (gl) {
+			gfs_glock_force_drop(gl);
+			if (atomic_read(&gl->gl_lvb_count))
+				gfs_lvb_unhold(gl);
+			gl2rgd(gl) = NULL;
+			gfs_glock_put(gl);
+		}
+
+		if (rgd->rd_bits)
+			kfree(rgd->rd_bits);
+		if (rgd->rd_bh)
+			kfree(rgd->rd_bh);
+
+		kfree(rgd);
+	}
+}
+
+/**
+ * gfs_clear_rgrpd - Clear up rgrps
+ * @sdp: The GFS superblock
+ *
+ */
+
+void
+gfs_clear_rgrpd(struct gfs_sbd *sdp)
+{
+	down(&sdp->sd_rindex_lock);
+	clear_rgrpdi(sdp);
+	up(&sdp->sd_rindex_lock);
+}
+
+/**
+ * gfs_compute_bitstructs - Compute the bitmap sizes
+ * @rgd: The resource group descriptor
+ *
+ * Calculates bitmap descriptors, one for each block that contains bitmap data
+ *
+ * Returns: errno
+ */
+
+static int
+compute_bitstructs(struct gfs_rgrpd *rgd)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	struct gfs_bitmap *bits;
+	uint32_t length = rgd->rd_ri.ri_length; /* # blocks in hdr & bitmap */
+	uint32_t bytes_left, bytes;
+	int x;
+
+	rgd->rd_bits = kmalloc(length * sizeof(struct gfs_bitmap), GFP_KERNEL);
+	if (!rgd->rd_bits)
+		return -ENOMEM;
+	memset(rgd->rd_bits, 0, length * sizeof(struct gfs_bitmap));
+
+	bytes_left = rgd->rd_ri.ri_bitbytes;
+
+	for (x = 0; x < length; x++) {
+		bits = &rgd->rd_bits[x];
+
+		/* small rgrp; bitmap stored completely in header block */
+		if (length == 1) {
+			bytes = bytes_left;
+			bits->bi_offset = sizeof(struct gfs_rgrp);
+			bits->bi_start = 0;
+			bits->bi_len = bytes;
+		/* header block */
+		} else if (x == 0) {
+			bytes = sdp->sd_sb.sb_bsize - sizeof(struct gfs_rgrp);
+			bits->bi_offset = sizeof(struct gfs_rgrp);
+			bits->bi_start = 0;
+			bits->bi_len = bytes;
+		/* last block */
+		} else if (x + 1 == length) {
+			bytes = bytes_left;
+			bits->bi_offset = sizeof(struct gfs_meta_header);
+			bits->bi_start = rgd->rd_ri.ri_bitbytes - bytes_left;
+			bits->bi_len = bytes;
+		/* other blocks */
+		} else {
+			bytes = sdp->sd_sb.sb_bsize - sizeof(struct gfs_meta_header);
+			bits->bi_offset = sizeof(struct gfs_meta_header);
+			bits->bi_start = rgd->rd_ri.ri_bitbytes - bytes_left;
+			bits->bi_len = bytes;
+		}
+
+		bytes_left -= bytes;
+	}
+
+	if (bytes_left) {
+		gfs_consist_rgrpd(rgd);
+		return -EIO;
+	}
+        if ((rgd->rd_bits[length - 1].bi_start +
+	     rgd->rd_bits[length - 1].bi_len) * GFS_NBBY !=
+	    rgd->rd_ri.ri_data) {
+		if (gfs_consist_rgrpd(rgd)) {
+			gfs_rindex_print(&rgd->rd_ri);
+			printk("GFS: fsid=%s: start=%u len=%u offset=%u\n",
+			       sdp->sd_fsname,
+			       rgd->rd_bits[length - 1].bi_start,
+			       rgd->rd_bits[length - 1].bi_len,
+			       rgd->rd_bits[length - 1].bi_offset);
+		}
+		return -EIO;
+	}
+
+	rgd->rd_bh = kmalloc(length * sizeof(struct buffer_head *), GFP_KERNEL);
+	if (!rgd->rd_bh) {
+		kfree(rgd->rd_bits);
+		return -ENOMEM;
+	}
+	memset(rgd->rd_bh, 0, length * sizeof(struct buffer_head *));
+
+	return 0;
+}
+
+/**
+ * gfs_ri_update - Pull in a new resource index from the disk
+ * @gl: The glock covering the rindex inode
+ *
+ * Returns: 0 on successful update, error code otherwise
+ */
+
+static int
+gfs_ri_update(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_rgrpd *rgd;
+	char buf[sizeof(struct gfs_rindex)];
+	int error;
+
+	if (do_mod(ip->i_di.di_size, sizeof(struct gfs_rindex))) {
+		gfs_consist_inode(ip);
+		return -EIO;
+	}
+
+	clear_rgrpdi(sdp);
+
+	for (sdp->sd_rgcount = 0;; sdp->sd_rgcount++) {
+		error = gfs_internal_read(ip, buf,
+					  sdp->sd_rgcount *
+					  sizeof(struct gfs_rindex),
+					  sizeof(struct gfs_rindex));
+		if (!error)
+			break;
+		if (error != sizeof(struct gfs_rindex)) {
+			if (error > 0)
+				error = -EIO;
+			goto fail;
+		}
+
+		rgd = kmalloc(sizeof(struct gfs_rgrpd), GFP_KERNEL);
+		error = -ENOMEM;
+		if (!rgd)
+			goto fail;
+		memset(rgd, 0, sizeof(struct gfs_rgrpd));
+
+		INIT_LIST_HEAD(&rgd->rd_mhc);
+		INIT_LIST_HEAD(&rgd->rd_depend);
+		rgd->rd_sbd = sdp;
+
+		list_add_tail(&rgd->rd_list, &sdp->sd_rglist);
+		list_add_tail(&rgd->rd_list_mru, &sdp->sd_rg_mru_list);
+
+		gfs_rindex_in(&rgd->rd_ri, buf);
+
+		error = compute_bitstructs(rgd);
+		if (error)
+			goto fail;
+
+		error = gfs_glock_get(sdp, rgd->rd_ri.ri_addr, &gfs_rgrp_glops,
+				      CREATE, &rgd->rd_gl);
+		if (error)
+			goto fail;
+
+		error = gfs_lvb_hold(rgd->rd_gl);
+		if (error)
+			goto fail;
+
+		gl2rgd(rgd->rd_gl) = rgd;
+		rgd->rd_rg_vn = rgd->rd_gl->gl_vn - 1;
+	}
+
+	sdp->sd_riinode_vn = ip->i_gl->gl_vn;
+
+	return 0;
+
+ fail:
+	clear_rgrpdi(sdp);
+
+	return error;
+}
+
+/**
+ * gfs_rindex_hold - Grab a lock on the rindex
+ * @sdp: The GFS superblock
+ * @ri_gh: the glock holder
+ *
+ * We grab a lock on the rindex inode to make sure that it doesn't
+ * change whilst we are performing an operation. We keep this lock
+ * for quite long periods of time compared to other locks. This
+ * doesn't matter, since it is shared and it is very, very rarely
+ * accessed in the exclusive mode (i.e. only when expanding the filesystem).
+ *
+ * This makes sure that we're using the latest copy of the resource index
+ *   special file, which might have been updated if someone expanded the
+ *   filesystem (via gfs_grow utility), which adds new resource groups.
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+int
+gfs_rindex_hold(struct gfs_sbd *sdp, struct gfs_holder *ri_gh)
+{
+	struct gfs_inode *ip = sdp->sd_riinode;
+	struct gfs_glock *gl = ip->i_gl;
+	int error;
+
+	error = gfs_glock_nq_init(gl, LM_ST_SHARED, 0, ri_gh);
+	if (error)
+		return error;
+
+	/* Read new copy from disk if we don't have the latest */
+	if (sdp->sd_riinode_vn != gl->gl_vn) {
+		down(&sdp->sd_rindex_lock);
+		if (sdp->sd_riinode_vn != gl->gl_vn) {
+			error = gfs_ri_update(ip);
+			if (error)
+				gfs_glock_dq_uninit(ri_gh);
+		}
+		up(&sdp->sd_rindex_lock);
+	}
+
+	return error;
+}
+
+/**
+ * gfs_rgrp_read - Read in a RG's header and bitmaps
+ * @rgd: the struct gfs_rgrpd describing the RG to read in
+ *
+ * Read in all of a Resource Group's header and bitmap blocks.
+ * Caller must eventually call gfs_rgrp_relse() to free the bitmaps.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_rgrp_read(struct gfs_rgrpd *rgd)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	struct gfs_glock *gl = rgd->rd_gl;
+	unsigned int x, length = rgd->rd_ri.ri_length;
+	int error;
+
+	for (x = 0; x < length; x++) {
+		gfs_assert_warn(sdp, !rgd->rd_bh[x]);
+		rgd->rd_bh[x] = gfs_dgetblk(gl, rgd->rd_ri.ri_addr + x);
+	}
+
+	for (x = 0; x < length; x++) {
+		error = gfs_dreread(sdp, rgd->rd_bh[x], DIO_START);
+		if (error)
+			goto fail;
+	}
+
+	for (x = length; x--;) {
+		error = gfs_dreread(sdp, rgd->rd_bh[x], DIO_WAIT);
+		if (error)
+			goto fail;
+		if (gfs_metatype_check(sdp, rgd->rd_bh[x],
+				       (x) ? GFS_METATYPE_RB : GFS_METATYPE_RG)) {
+			error = -EIO;
+			goto fail;
+		}
+	}
+
+	if (rgd->rd_rg_vn != gl->gl_vn) {
+		gfs_rgrp_in(&rgd->rd_rg, (rgd->rd_bh[0])->b_data);
+		rgd->rd_rg_vn = gl->gl_vn;
+	}
+
+	return 0;
+
+ fail:
+	for (x = 0; x < length; x++) {
+		brelse(rgd->rd_bh[x]);
+		rgd->rd_bh[x] = NULL;
+	}
+
+	return error;
+}
+
+/**
+ * gfs_rgrp_relse - Release RG bitmaps read in with gfs_rgrp_read()
+ * @rgd: the struct gfs_rgrpd describing the RG to read in
+ *
+ */
+
+void
+gfs_rgrp_relse(struct gfs_rgrpd *rgd)
+{
+	int x, length = rgd->rd_ri.ri_length;
+
+	for (x = 0; x < length; x++) {
+		brelse(rgd->rd_bh[x]);
+		rgd->rd_bh[x] = NULL;
+	}
+}
+
+/**
+ * gfs_rgrp_lvb_fill - copy RG usage data out of the struct gfs_rgrp into the struct gfs_rgrp_lvb
+ * @rgd: the resource group data structure
+ *
+ */
+
+void
+gfs_rgrp_lvb_fill(struct gfs_rgrpd *rgd)
+{
+	struct gfs_rgrp *rg = &rgd->rd_rg;
+	struct gfs_rgrp_lvb *rb = (struct gfs_rgrp_lvb *)rgd->rd_gl->gl_lvb;
+
+	rb->rb_magic = cpu_to_gfs32(GFS_MAGIC);
+	rb->rb_free = cpu_to_gfs32(rg->rg_free);
+	rb->rb_useddi = cpu_to_gfs32(rg->rg_useddi);
+	rb->rb_freedi = cpu_to_gfs32(rg->rg_freedi);
+	rb->rb_usedmeta = cpu_to_gfs32(rg->rg_usedmeta);
+	rb->rb_freemeta = cpu_to_gfs32(rg->rg_freemeta);
+}
+
+/**
+ * gfs_rgrp_lvb_init - Init the data of a RG LVB
+ * @rgd: the resource group data structure
+ *
+ * Returns:  errno
+ */
+
+int
+gfs_rgrp_lvb_init(struct gfs_rgrpd *rgd)
+{
+	struct gfs_glock *gl = rgd->rd_gl;
+	struct gfs_holder rgd_gh;
+	int error;
+
+	error = gfs_glock_nq_init(gl, LM_ST_EXCLUSIVE, 0, &rgd_gh);
+	if (!error) {
+		gfs_rgrp_lvb_fill(rgd);
+		gfs_glock_dq_uninit(&rgd_gh);
+	}
+
+	return error;
+}
+
+/**
+ * gfs_alloc_get - allocate a struct gfs_alloc structure for an inode
+ * @ip: the incore GFS inode structure
+ *
+ * Alloc and zero an in-place reservation structure,
+ *   and attach it to the GFS incore inode.
+ *
+ * FIXME: Don't use gmalloc()
+ *
+ * Returns: the struct gfs_alloc
+ */
+
+struct gfs_alloc *
+gfs_alloc_get(struct gfs_inode *ip)
+{
+	struct gfs_alloc *al = ip->i_alloc;
+
+	gfs_assert_warn(ip->i_sbd, !al);
+
+	al = gmalloc(sizeof(struct gfs_alloc));
+	memset(al, 0, sizeof(struct gfs_alloc));
+
+	ip->i_alloc = al;
+
+	return al;
+}
+
+/**
+ * gfs_alloc_put - throw away the struct gfs_alloc for an inode
+ * @ip: the inode
+ *
+ */
+
+void
+gfs_alloc_put(struct gfs_inode *ip)
+{
+	struct gfs_alloc *al = ip->i_alloc;
+
+	if (gfs_assert_warn(ip->i_sbd, al))
+		return;
+
+	ip->i_alloc = NULL;
+	kfree(al);
+}
+
+/**
+ * try_rgrp_fit - See if a given reservation will fit in a given RG
+ * @rgd: the RG data
+ * @al: the struct gfs_alloc structure describing the reservation
+ *
+ * If there's room for the requested blocks to be allocated from the RG:
+ *   Sets the $al_reserved_data field in @al.
+ *   Sets the $al_reserved_meta field in @al.
+ *   Sets the $al_rgd field in @al.
+ *
+ * Returns: 1 on success (it fits), 0 on failure (it doesn't fit)
+ */
+
+static int
+try_rgrp_fit(struct gfs_rgrpd *rgd, struct gfs_alloc *al)
+{
+	uint32_t freeblks = rgd->rd_rg.rg_free;
+	uint32_t freemeta = rgd->rd_rg.rg_freemeta;
+	uint32_t metares = al->al_requested_meta;
+	uint32_t datares = al->al_requested_data;
+
+	/* First take care of the data blocks required */
+
+	if (freeblks < al->al_requested_data)
+		return 0;
+
+	freeblks -= al->al_requested_data;
+
+	/* Then take care of the dinodes */
+
+	metares += al->al_requested_di;
+
+	/* Then take care of the metadata blocks */
+
+	while (freemeta < metares) {
+		if (freeblks < GFS_META_CLUMP)
+			return 0;
+
+		freeblks -= GFS_META_CLUMP;
+		freemeta += GFS_META_CLUMP;
+
+		datares += GFS_META_CLUMP;
+	}
+
+	al->al_rgd = rgd;
+	al->al_reserved_meta = metares;
+	al->al_reserved_data = datares;
+
+	return 1;
+}
+
+/**
+ * recent_rgrp_first - get first RG from "recent" list
+ * @sdp: The GFS superblock
+ * @rglast: address of the rgrp used last
+ *
+ * Returns: The first rgrp in the recent list
+ */
+
+static struct gfs_rgrpd *
+recent_rgrp_first(struct gfs_sbd *sdp, uint64_t rglast)
+{
+	struct list_head *tmp, *head;
+	struct gfs_rgrpd *rgd = NULL;
+
+	spin_lock(&sdp->sd_rg_recent_lock);
+
+	if (list_empty(&sdp->sd_rg_recent))
+		goto out;
+
+	if (!rglast)
+		goto first;
+
+	for (head = &sdp->sd_rg_recent, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		rgd = list_entry(tmp, struct gfs_rgrpd, rd_recent);
+		if (rgd->rd_ri.ri_addr == rglast)
+			goto out;
+	}
+
+ first:
+	rgd = list_entry(sdp->sd_rg_recent.next, struct gfs_rgrpd, rd_recent);
+
+ out:
+	spin_unlock(&sdp->sd_rg_recent_lock);
+
+	return rgd;
+}
+
+/**
+ * recent_rgrp_next - get next RG from "recent" list
+ * @cur_rgd: current rgrp
+ * @remove:
+ *
+ * Returns: The next rgrp in the recent list
+ */
+
+static struct gfs_rgrpd *
+recent_rgrp_next(struct gfs_rgrpd *cur_rgd, int remove)
+{
+	struct gfs_sbd *sdp = cur_rgd->rd_sbd;
+	struct list_head *tmp, *head;
+	struct gfs_rgrpd *rgd;
+
+	spin_lock(&sdp->sd_rg_recent_lock);
+
+	for (head = &sdp->sd_rg_recent, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		rgd = list_entry(tmp, struct gfs_rgrpd, rd_recent);
+		if (rgd == cur_rgd) {
+			if (cur_rgd->rd_recent.next != head)
+				rgd = list_entry(cur_rgd->rd_recent.next,
+						 struct gfs_rgrpd, rd_recent);
+			else
+				rgd = NULL;
+
+			if (remove)
+				list_del(&cur_rgd->rd_recent);
+
+			goto out;
+		}
+	}
+
+	rgd = NULL;
+	if (!list_empty(head))
+		rgd = list_entry(head->next, struct gfs_rgrpd, rd_recent);
+
+ out:
+	spin_unlock(&sdp->sd_rg_recent_lock);
+
+	return rgd;
+}
+
+/**
+ * recent_rgrp_add - add an RG to tail of "recent" list
+ * @new_rgd: The rgrp to add
+ *
+ * Before adding, make sure that:
+ *   1) it's not already on the list
+ *   2) there's still room for more entries
+ * The capacity limit imposed on the "recent" list is basically a node's "share"
+ *   of rgrps within a cluster, i.e. (total # rgrps) / (# nodes (journals))
+ */
+
+static void
+recent_rgrp_add(struct gfs_rgrpd *new_rgd)
+{
+	struct gfs_sbd *sdp = new_rgd->rd_sbd;
+	struct list_head *tmp, *head;
+	struct gfs_rgrpd *rgd = NULL;
+	unsigned int count = 0;
+	unsigned int max = sdp->sd_rgcount / gfs_num_journals(sdp);
+
+	spin_lock(&sdp->sd_rg_recent_lock);
+
+	for (head = &sdp->sd_rg_recent, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		rgd = list_entry(tmp, struct gfs_rgrpd, rd_recent);
+		if (rgd == new_rgd)
+			goto out;
+
+		if (++count >= max)
+			goto out;
+	}
+	new_rgd->rd_try_counter = 0;
+	list_add_tail(&new_rgd->rd_recent, &sdp->sd_rg_recent);
+
+ out:
+	spin_unlock(&sdp->sd_rg_recent_lock);
+}
+
+/**
+ * forward_rgrp_get - get an rgrp to try next from full list
+ * @sdp: The GFS superblock
+ *
+ * Returns: The rgrp to try next
+ */
+
+static struct gfs_rgrpd *
+forward_rgrp_get(struct gfs_sbd *sdp)
+{
+	struct gfs_rgrpd *rgd;
+	unsigned int journals = gfs_num_journals(sdp);
+	unsigned int rg = 0, x;
+
+	spin_lock(&sdp->sd_rg_forward_lock);
+
+	rgd = sdp->sd_rg_forward;
+	if (!rgd) {
+		if (sdp->sd_rgcount >= journals)
+			rg = sdp->sd_rgcount *
+				sdp->sd_lockstruct.ls_jid /
+				journals;
+
+		for (x = 0, rgd = gfs_rgrpd_get_first(sdp);
+		     x < rg;
+		     x++, rgd = gfs_rgrpd_get_next(rgd))
+			/* Do Nothing */;
+
+		sdp->sd_rg_forward = rgd;
+	}
+
+	spin_unlock(&sdp->sd_rg_forward_lock);
+
+	return rgd;
+}
+
+/**
+ * forward_rgrp_set - set the forward rgrp pointer
+ * @sdp: the filesystem
+ * @rgd: The new forward rgrp
+ *
+ */
+
+static void
+forward_rgrp_set(struct gfs_sbd *sdp, struct gfs_rgrpd *rgd)
+{
+	spin_lock(&sdp->sd_rg_forward_lock);
+	sdp->sd_rg_forward = rgd;
+	spin_unlock(&sdp->sd_rg_forward_lock);
+}
+
+/**
+ * get_local_rgrp - Choose and lock a rgrp for allocation
+ * @ip: the inode to reserve space for
+ * @rgp: the chosen and locked rgrp
+ *
+ * Try to acquire rgrp in way which avoids contending with others.
+ *
+ * Returns: errno
+ */
+
+static int
+get_local_rgrp(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_rgrpd *rgd, *begin = NULL;
+	struct gfs_alloc *al = ip->i_alloc;
+	int flags = LM_FLAG_TRY;
+	int skipped = 0;
+	int loops = 0;
+	int error;
+	int try_flag;
+	unsigned int try_threshold = gfs_tune_get(sdp, gt_rgrp_try_threshold);
+
+	/* Try recently successful rgrps */
+
+	rgd = recent_rgrp_first(sdp, ip->i_last_rg_alloc);
+
+	while (rgd) {
+		try_flag = (rgd->rd_try_counter >= try_threshold) ?
+			0: LM_FLAG_TRY;
+		error = gfs_glock_nq_init(rgd->rd_gl,
+					  LM_ST_EXCLUSIVE, try_flag,
+					  &al->al_rgd_gh);
+		switch (error) {
+		case 0:
+			if (try_rgrp_fit(rgd, al)) {
+				rgd->rd_try_counter = 0;
+				goto out;
+			}
+			gfs_glock_dq_uninit(&al->al_rgd_gh);
+			rgd = recent_rgrp_next(rgd, TRUE);
+			break;
+
+		case GLR_TRYFAILED:
+			rgd->rd_try_counter++;
+			rgd = recent_rgrp_next(rgd, FALSE);
+			break;
+
+		default:
+			return error;
+		}
+	}
+
+	/* Go through full list of rgrps */
+
+	begin = rgd = forward_rgrp_get(sdp);
+
+	for (;;) {
+		error = gfs_glock_nq_init(rgd->rd_gl,
+					  LM_ST_EXCLUSIVE, flags,
+					  &al->al_rgd_gh);
+		switch (error) {
+		case 0:
+			if (try_rgrp_fit(rgd, al))
+				goto out;
+			gfs_glock_dq_uninit(&al->al_rgd_gh);
+			break;
+
+		case GLR_TRYFAILED:
+			skipped++;
+			break;
+
+		default:
+			return error;
+		}
+
+		rgd = gfs_rgrpd_get_next(rgd);
+		if (!rgd)
+			rgd = gfs_rgrpd_get_first(sdp);
+
+		if (rgd == begin) {
+			if (++loops >= 2 || !skipped)
+				return -ENOSPC;
+			flags = 0;
+		}
+	}
+
+ out:
+	ip->i_last_rg_alloc = rgd->rd_ri.ri_addr;
+
+	if (begin) {
+		recent_rgrp_add(rgd);
+		rgd = gfs_rgrpd_get_next(rgd);
+		if (!rgd)
+			rgd = gfs_rgrpd_get_first(sdp);
+		forward_rgrp_set(sdp, rgd);
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_inplace_reserve_i - Reserve space in the filesystem
+ * @ip: the inode to reserve space for
+ *
+ * Acquire resource group locks to allow for the maximum allocation
+ * described by "res".
+ *
+ * This should probably become more complex again, but for now, let's go
+ * for simple (one resource group) reservations.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_inplace_reserve_i(struct gfs_inode *ip,
+		     char *file, unsigned int line)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	int error;
+
+        if (gfs_assert_warn(sdp,
+			    al->al_requested_di ||
+			    al->al_requested_data ||
+			    al->al_requested_meta))
+		return -EINVAL;
+
+	error = gfs_rindex_hold(sdp, &al->al_ri_gh);
+	if (error)
+		return error;
+
+	error = get_local_rgrp(ip);
+	if (error) {
+		gfs_glock_dq_uninit(&al->al_ri_gh);
+		return error;
+	}
+
+	gfs_depend_sync(al->al_rgd);
+
+	al->al_file = file;
+	al->al_line = line;
+
+	return 0;
+}
+
+/**
+ * gfs_inplace_release - release an inplace reservation
+ * @ip: the inode the reservation was taken out on
+ *
+ * Release a reservation made by gfs_inplace_reserve().
+ */
+
+void
+gfs_inplace_release(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+
+	if (gfs_assert_warn(sdp, al->al_alloced_di <= al->al_requested_di) == -1)
+		printk("GFS: fsid=%s: al_alloced_di = %u, al_requested_di = %u\n"
+		       "GFS: fsid=%s: al_file = %s, al_line = %u\n",
+		       sdp->sd_fsname, al->al_alloced_di, al->al_requested_di,
+		       sdp->sd_fsname, al->al_file, al->al_line);
+	if (gfs_assert_warn(sdp, al->al_alloced_meta <= al->al_reserved_meta) == -1)
+		printk("GFS: fsid=%s: al_alloced_meta = %u, al_reserved_meta = %u\n"
+		       "GFS: fsid=%s: al_file = %s, al_line = %u\n",
+		       sdp->sd_fsname, al->al_alloced_meta, al->al_reserved_meta,
+		       sdp->sd_fsname, al->al_file, al->al_line);
+	if (gfs_assert_warn(sdp, al->al_alloced_data <= al->al_reserved_data) == -1)
+		printk("GFS: fsid=%s: al_alloced_data = %u, al_reserved_data = %u\n"
+		       "GFS: fsid=%s: al_file = %s, al_line = %u\n",
+		       sdp->sd_fsname, al->al_alloced_data, al->al_reserved_data,
+		       sdp->sd_fsname, al->al_file, al->al_line);
+
+	al->al_rgd = NULL;
+	gfs_glock_dq_uninit(&al->al_rgd_gh);
+	gfs_glock_dq_uninit(&al->al_ri_gh);
+}
+
+/**
+ * gfs_get_block_type - Check a block in a RG is of given type
+ * @rgd: the resource group holding the block
+ * @block: the block number
+ *
+ * Returns: The block type (GFS_BLKST_*)
+ */
+
+unsigned char
+gfs_get_block_type(struct gfs_rgrpd *rgd, uint64_t block)
+{
+	struct gfs_bitmap *bits = NULL;
+	uint32_t length, rgrp_block, buf_block;
+	unsigned int buf;
+	unsigned char type;
+
+	length = rgd->rd_ri.ri_length;
+	rgrp_block = block - rgd->rd_ri.ri_data1;
+
+	for (buf = 0; buf < length; buf++) {
+		bits = &rgd->rd_bits[buf];
+		if (rgrp_block < (bits->bi_start + bits->bi_len) * GFS_NBBY)
+			break;
+	}
+
+	gfs_assert(rgd->rd_sbd, buf < length,);
+	buf_block = rgrp_block - bits->bi_start * GFS_NBBY;
+
+	type = gfs_testbit(rgd,
+			   rgd->rd_bh[buf]->b_data + bits->bi_offset,
+			   bits->bi_len, buf_block);
+
+	return type;
+}
+
+/**
+ * blkalloc_internal - find a block in @old_state, change allocation
+ *           state to @new_state
+ * @rgd: the resource group descriptor
+ * @goal: the goal block within the RG (start here to search for avail block)
+ * @old_state: GFS_BLKST_XXX the before-allocation state to find
+ * @new_state: GFS_BLKST_XXX the after-allocation block state
+ *
+ * Walk rgrp's bitmap to find bits that represent a block in @old_state.
+ * Add the found bitmap buffer to the transaction.
+ * Set the found bits to @new_state to change block's allocation state.
+ *
+ * This function never fails, because we wouldn't call it unless we
+ *   know (from reservation results, etc.) that a block is available.
+ *
+ * Scope of @goal and returned block is just within rgrp (32-bit),
+ *   not the whole filesystem (64-bit).
+ *
+ * Returns:  the block # allocated (32-bit rgrp scope)
+ */
+
+static uint32_t
+blkalloc_internal(struct gfs_rgrpd *rgd,
+		  uint32_t goal,
+		  unsigned char old_state, unsigned char new_state)
+{
+	struct gfs_bitmap *bits = NULL;
+	uint32_t length = rgd->rd_ri.ri_length;
+	uint32_t blk = 0;
+	unsigned int buf, x;
+
+	/* Find bitmap block that contains bits for goal block */
+	for (buf = 0; buf < length; buf++) {
+		bits = &rgd->rd_bits[buf];
+		if (goal < (bits->bi_start + bits->bi_len) * GFS_NBBY)
+			break;
+	}
+
+	gfs_assert(rgd->rd_sbd, buf < length,);
+
+	/* Convert scope of "goal" from rgrp-wide to within found bit block */
+	goal -= bits->bi_start * GFS_NBBY;
+
+	/* Search (up to entire) bitmap in this rgrp for allocatable block.
+	   "x <= length", instead of "x < length", because we typically start
+	   the search in the middle of a bit block, but if we can't find an
+	   allocatable block anywhere else, we want to be able wrap around and
+	   search in the first part of our first-searched bit block.  */
+	for (x = 0; x <= length; x++) {
+		blk = gfs_bitfit(rgd,
+				 rgd->rd_bh[buf]->b_data + bits->bi_offset,
+				 bits->bi_len, goal, old_state);
+		if (blk != BFITNOENT)
+			break;
+
+		/* Try next bitmap block (wrap back to rgrp header if at end) */
+		buf = (buf + 1) % length;
+		bits = &rgd->rd_bits[buf];
+		goal = 0;
+	}
+
+	if (gfs_assert_withdraw(rgd->rd_sbd, x <= length))
+		blk = 0;
+
+	/* Attach bitmap buffer to trans, modify bits to do block alloc */
+	gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[buf]);
+	gfs_setbit(rgd,
+		   rgd->rd_bh[buf]->b_data + bits->bi_offset,
+		   bits->bi_len, blk, new_state);
+
+	/* Return allocated block #, rgrp scope (32-bit) */
+	return bits->bi_start * GFS_NBBY + blk;
+}
+
+/**
+ * blkfree_internal - Change alloc state of given block(s)
+ * @sdp: the filesystem
+ * @bstart: first block (64-bit filesystem scope) of a run of contiguous blocks
+ * @blen: the length of the block run (all must lie within ONE RG!)
+ * @new_state: GFS_BLKST_XXX the after-allocation block state
+ *
+ * Returns:  Resource group containing the block(s)
+ *
+ * Find rgrp containing @bstart.
+ * For each block in run:
+ *   Find allocation bitmap buffer.
+ *   Add bitmap buffer to transaction.
+ *   Set bits to new state.
+ * Typically used to free blocks to GFS_BLKST_FREE or GFS_BLKST_FREEMETA,
+ *   but @new_state can be any GFS_BLKST_XXX
+ * 
+ */
+
+static struct gfs_rgrpd *
+blkfree_internal(struct gfs_sbd *sdp, uint64_t bstart, uint32_t blen,
+		 unsigned char new_state)
+{
+	struct gfs_rgrpd *rgd;
+	struct gfs_bitmap *bits = NULL;
+	uint32_t length, rgrp_blk, buf_blk;
+	unsigned int buf;
+
+	/* Find rgrp */
+	rgd = gfs_blk2rgrpd(sdp, bstart);
+	if (!rgd) {
+		if (gfs_consist(sdp))
+			printk("GFS: fsid=%s: block = %"PRIu64"\n",
+			       sdp->sd_fsname, bstart);
+		return NULL;
+	}
+
+	length = rgd->rd_ri.ri_length;
+
+	/* Convert blk # from filesystem scope (64-bit) to RG scope (32-bit) */
+	rgrp_blk = bstart - rgd->rd_ri.ri_data1;
+
+	while (blen--) {
+		/* Find bitmap buffer for this block */
+		for (buf = 0; buf < length; buf++) {
+			bits = &rgd->rd_bits[buf];
+			if (rgrp_blk < (bits->bi_start + bits->bi_len) * GFS_NBBY)
+				break;
+		}
+
+		gfs_assert(rgd->rd_sbd, buf < length,);
+
+		/* Find bits and set 'em */
+		buf_blk = rgrp_blk - bits->bi_start * GFS_NBBY;
+		rgrp_blk++;
+
+		gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[buf]);
+		gfs_setbit(rgd,
+			   rgd->rd_bh[buf]->b_data + bits->bi_offset,
+			   bits->bi_len, buf_blk, new_state);
+	}
+
+	return rgd;
+}
+
+/**
+ * clump_alloc - Allocate a clump of metadata blocks
+ * @rgd: the resource group in which to allocate
+ * @first: returns the first block allocated
+ *
+ * Returns: errno
+ *
+ * Bitmap-allocate a clump of metadata blocks
+ * Write metadata blocks to disk with dummy meta-headers
+ * Add meta-headers to incore meta-header cache
+ */
+
+static int
+clump_alloc(struct gfs_rgrpd *rgd, uint32_t *first)
+{
+	struct gfs_sbd *sdp = rgd->rd_sbd;
+	struct gfs_meta_header mh;
+	struct buffer_head **bh;
+	uint32_t goal, blk;
+	unsigned int x;
+	int error = 0;
+
+	/* Dummy meta-header template */
+	memset(&mh, 0, sizeof(struct gfs_meta_header));
+	mh.mh_magic = GFS_MAGIC;
+	mh.mh_type = GFS_METATYPE_NONE;
+
+	/* Array of bh pointers used in several steps */
+	bh = gmalloc(GFS_META_CLUMP * sizeof(struct buffer_head *));
+	memset(bh, 0, GFS_META_CLUMP * sizeof(struct buffer_head *));
+
+	/* Since we're looking for data blocks to change into meta blocks,
+	   use last alloc'd *data* (not meta) block as start point */
+	goal = rgd->rd_last_alloc_data;
+
+	for (x = 0; x < GFS_META_CLUMP; x++) {
+		blk = blkalloc_internal(rgd, goal, GFS_BLKST_FREE,
+					GFS_BLKST_FREEMETA);
+		if (!x)
+			*first = blk;
+
+		bh[x] = gfs_dgetblk(rgd->rd_gl, rgd->rd_ri.ri_data1 + blk);
+
+		gfs_prep_new_buffer(bh[x]);
+
+		gfs_meta_header_out(&mh, bh[x]->b_data);
+		((struct gfs_meta_header *)bh[x]->b_data)->mh_generation = 0;
+
+		/* start write of new meta-buffer to disk */
+		error = gfs_dwrite(sdp, bh[x], DIO_DIRTY | DIO_START);
+		if (error)
+			goto out;
+
+		goal = blk;
+	}
+
+	/* Block alloc start point for next time */
+	rgd->rd_last_alloc_data = goal;
+
+	/* Wait for all new meta-buffers to get on-disk */
+	for (x = 0; x < GFS_META_CLUMP; x++) {
+		error = gfs_dwrite(sdp, bh[x], DIO_WAIT);
+		if (error)
+			goto out;
+	}
+
+	/* Add all new meta-headers to meta-header cache */
+	gfs_mhc_add(rgd, bh, GFS_META_CLUMP);
+
+	gfs_assert_withdraw(sdp, rgd->rd_rg.rg_free >= GFS_META_CLUMP);
+	rgd->rd_rg.rg_free -= GFS_META_CLUMP;
+	rgd->rd_rg.rg_freemeta += GFS_META_CLUMP;
+
+ out:
+	for (x = 0; x < GFS_META_CLUMP; x++)
+		if (bh[x]) {
+			gfs_dwrite(sdp, bh[x], DIO_WAIT);
+			brelse(bh[x]);
+		}
+	kfree(bh);
+
+	return error;
+}
+
+/**
+ * gfs_blkalloc - Allocate a data block
+ * @ip: the inode to allocate the data block for
+ * @block: the block allocated
+ *
+ */
+
+void
+gfs_blkalloc(struct gfs_inode *ip, uint64_t *block)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	struct gfs_rgrpd *rgd = al->al_rgd;
+	uint32_t goal, blk;
+	int same;
+
+	same = (rgd->rd_ri.ri_addr == ip->i_di.di_goal_rgrp);
+	goal = (same) ? ip->i_di.di_goal_dblk : rgd->rd_last_alloc_data;
+
+	blk = blkalloc_internal(rgd, goal,
+				GFS_BLKST_FREE, GFS_BLKST_USED);
+	rgd->rd_last_alloc_data = blk;
+
+	if (!same) {
+		ip->i_di.di_goal_rgrp = rgd->rd_ri.ri_addr;
+		ip->i_di.di_goal_mblk = 0;
+	}
+	ip->i_di.di_goal_dblk = blk;
+
+	*block = rgd->rd_ri.ri_data1 + blk;
+
+	gfs_assert_withdraw(sdp, rgd->rd_rg.rg_free);
+	rgd->rd_rg.rg_free--;
+
+	gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[0]);
+	gfs_rgrp_out(&rgd->rd_rg, rgd->rd_bh[0]->b_data);
+
+	al->al_alloced_data++;
+
+	gfs_trans_add_quota(sdp, +1, ip->i_di.di_uid, ip->i_di.di_gid);
+}
+
+/**
+ * gfs_metaalloc - Allocate a metadata block to a file
+ * @ip:  the file
+ * @block: the block allocated
+ *
+ * Returns: errno
+ */
+
+int
+gfs_metaalloc(struct gfs_inode *ip, uint64_t *block)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_alloc *al = ip->i_alloc;
+	struct gfs_rgrpd *rgd = al->al_rgd;
+	uint32_t goal, blk;
+	int same;
+	int error;
+
+	same = (rgd->rd_ri.ri_addr == ip->i_di.di_goal_rgrp);
+
+	if (!rgd->rd_rg.rg_freemeta) {
+		error = clump_alloc(rgd, &goal);
+		if (error)
+			return error;
+
+		al->al_alloced_data += GFS_META_CLUMP;
+	} else
+		goal = (same) ? ip->i_di.di_goal_mblk : rgd->rd_last_alloc_meta;
+
+	blk = blkalloc_internal(rgd, goal,
+				GFS_BLKST_FREEMETA, GFS_BLKST_USEDMETA);
+	rgd->rd_last_alloc_meta = blk;
+
+	if (!same) {
+		ip->i_di.di_goal_rgrp = rgd->rd_ri.ri_addr;
+		ip->i_di.di_goal_dblk = 0;
+	}
+	ip->i_di.di_goal_mblk = blk;
+
+	*block = rgd->rd_ri.ri_data1 + blk;
+
+	gfs_assert_withdraw(sdp, rgd->rd_rg.rg_freemeta);
+	rgd->rd_rg.rg_freemeta--;
+	rgd->rd_rg.rg_usedmeta++;
+
+	gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[0]);
+	gfs_rgrp_out(&rgd->rd_rg, rgd->rd_bh[0]->b_data);
+
+	al->al_alloced_meta++;
+
+	gfs_trans_add_quota(sdp, +1, ip->i_di.di_uid, ip->i_di.di_gid);
+
+	return 0;
+}
+
+/**
+ * gfs_dialloc - Allocate a dinode
+ * @dip: the directory that the inode is going in
+ * @block: the block (result) which this function allocates as the dinode
+ *     (64-bit filesystem scope)
+ *
+ * Returns: errno
+ */
+
+int
+gfs_dialloc(struct gfs_inode *dip, uint64_t *block)
+{
+	struct gfs_alloc *al = dip->i_alloc;
+	struct gfs_rgrpd *rgd = al->al_rgd;
+	uint32_t goal, blk;
+	int error = 0;
+
+	if (rgd->rd_rg.rg_freemeta)
+		/* pick up where we left off last time */
+		goal = rgd->rd_last_alloc_meta;
+	else {
+		/* no free meta blocks, allocate a bunch more */
+		error = clump_alloc(rgd, &goal);
+		if (error)
+			return error;
+
+		al->al_alloced_data += GFS_META_CLUMP;
+	}
+
+	/* Alloc the dinode; 32-bit "blk" is block offset within rgrp */
+	blk = blkalloc_internal(rgd, goal,
+				GFS_BLKST_FREEMETA, GFS_BLKST_USEDMETA);
+
+	/* remember where we left off, for next time */
+	rgd->rd_last_alloc_meta = blk;
+
+	/* convert from rgrp scope (32-bit) to filesystem scope (64-bit) */
+	*block = rgd->rd_ri.ri_data1 + blk;
+
+	gfs_assert_withdraw(rgd->rd_sbd, rgd->rd_rg.rg_freemeta);
+	rgd->rd_rg.rg_freemeta--;
+	rgd->rd_rg.rg_useddi++;
+
+	/* Attach rgrp header to trans, update freemeta and useddi stats */
+	gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[0]);
+	gfs_rgrp_out(&rgd->rd_rg, rgd->rd_bh[0]->b_data);
+
+	/* Update stats in in-place reservation struct */
+	al->al_alloced_di++;
+	al->al_alloced_meta++;
+
+	return error;
+}
+
+/**
+ * gfs_blkfree - free a contiguous run of data block(s)
+ * @ip: the inode these blocks are being freed from
+ * @bstart: first block (64-bit filesystem scope) of a run of contiguous blocks
+ * @blen: the length of the block run (all must lie within ONE RG!)
+ *
+ * Bitmap-deallocate the blocks (to FREE data state), add bitmap blks to trans
+ * Update rgrp alloc statistics in rgrp header, add rgrp header buf to trans
+ * Update quotas, add to trans.
+ */
+
+void
+gfs_blkfree(struct gfs_inode *ip, uint64_t bstart, uint32_t blen)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_rgrpd *rgd;
+
+	rgd = blkfree_internal(sdp, bstart, blen, GFS_BLKST_FREE);
+	if (!rgd)
+		return;
+
+	rgd->rd_rg.rg_free += blen;
+
+	gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[0]);
+	gfs_rgrp_out(&rgd->rd_rg, rgd->rd_bh[0]->b_data);
+
+	gfs_trans_add_quota(sdp, -(int64_t)blen,
+			    ip->i_di.di_uid,
+			    ip->i_di.di_gid);
+}
+
+/**
+ * gfs_metafree - free a contiguous run of metadata block(s)
+ * @ip: the inode these blocks are being freed from
+ * @bstart: first block (64-bit filesystem scope) of a run of contiguous blocks
+ * @blen: the length of the block run (all must lie within ONE RG!)
+ *
+ * Bitmap-deallocate the blocks (to FREEMETA state), add bitmap blks to trans.
+ * Update rgrp alloc statistics in rgrp header, add rgrp header to trans.
+ * Update quotas (quotas include metadata, not just data block usage),
+ *    add to trans.
+ * Release deallocated buffers, add to meta-header cache (we save these in-core
+ *    so we don't need to re-read meta blocks if/when they are re-alloc'd).
+ */
+
+void
+gfs_metafree(struct gfs_inode *ip, uint64_t bstart, uint32_t blen)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	struct gfs_rgrpd *rgd;
+
+	rgd = blkfree_internal(sdp, bstart, blen, GFS_BLKST_FREEMETA);
+	if (!rgd)
+		return;
+
+	if (rgd->rd_rg.rg_usedmeta < blen)
+		gfs_consist_rgrpd(rgd);
+	rgd->rd_rg.rg_usedmeta -= blen;
+	rgd->rd_rg.rg_freemeta += blen;
+
+	gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[0]);
+	gfs_rgrp_out(&rgd->rd_rg, rgd->rd_bh[0]->b_data);
+
+	gfs_trans_add_quota(sdp, -(int64_t)blen,
+			    ip->i_di.di_uid,
+			    ip->i_di.di_gid);
+	gfs_wipe_buffers(ip, rgd, bstart, blen);
+}
+
+/**
+ * gfs_difree_uninit - free a dinode block
+ * @rgd: the resource group that contains the dinode
+ * @addr: the dinode address
+ *
+ * De-allocate the dinode to FREEMETA using block alloc bitmap.
+ * Update rgrp's block usage statistics (used dinode--, free meta++).
+ * Add rgrp header to transaction.
+ */
+
+void
+gfs_difree_uninit(struct gfs_rgrpd *rgd, uint64_t addr)
+{
+	struct gfs_rgrpd *tmp_rgd;
+
+	tmp_rgd = blkfree_internal(rgd->rd_sbd, addr, 1,
+				   GFS_BLKST_FREEMETA);
+	if (!tmp_rgd)
+		return;
+	gfs_assert_withdraw(rgd->rd_sbd, rgd == tmp_rgd);
+
+	if (!rgd->rd_rg.rg_useddi)
+		gfs_consist_rgrpd(rgd);
+	rgd->rd_rg.rg_useddi--;
+	rgd->rd_rg.rg_freemeta++;
+
+	gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[0]);
+	gfs_rgrp_out(&rgd->rd_rg, rgd->rd_bh[0]->b_data);
+}
+
+/**
+ * gfs_difree - free a dinode block
+ * @rgd: the resource group that contains the dinode
+ * @ip: the inode representing the dinode to free
+ *
+ * Free the dinode block to FREEMETA, update rgrp's block usage stats.
+ * Update quotas (quotas include metadata, not just data block usage),
+ *    add to trans.
+ * Release deallocated buffers, add to meta-header cache (we save these in-core
+ *    so we don't need to re-read meta blocks if/when they are re-alloc'd).
+ */
+
+void
+gfs_difree(struct gfs_rgrpd *rgd, struct gfs_inode *ip)
+{
+	gfs_difree_uninit(rgd, ip->i_num.no_addr);
+	gfs_trans_add_quota(ip->i_sbd, -1, ip->i_di.di_uid, ip->i_di.di_gid);
+	gfs_wipe_buffers(ip, rgd, ip->i_num.no_addr, 1);
+}
+
+/**
+ * gfs_rlist_add - add a RG to a list of RGs
+ * @sdp: the filesystem
+ * @rlist: the list of resource groups
+ * @block: the block
+ *
+ * Figure out what RG a block belongs to and add that RG to the list
+ *
+ * FIXME: Don't use gmalloc()
+ *
+ */
+
+void
+gfs_rlist_add(struct gfs_sbd *sdp, struct gfs_rgrp_list *rlist, uint64_t block)
+{
+	struct gfs_rgrpd *rgd;
+	struct gfs_rgrpd **tmp;
+	unsigned int new_space;
+	unsigned int x;
+
+	if (gfs_assert_warn(sdp, !rlist->rl_ghs))
+		return;
+
+	rgd = gfs_blk2rgrpd(sdp, block);
+	if (!rgd) {
+		if (gfs_consist(sdp))
+			printk("GFS: fsid=%s: block = %"PRIu64"\n",
+			       sdp->sd_fsname, block);
+		return;
+	}
+
+	for (x = 0; x < rlist->rl_rgrps; x++)
+		if (rlist->rl_rgd[x] == rgd)
+			return;
+
+	if (rlist->rl_rgrps == rlist->rl_space) {
+		new_space = rlist->rl_space + 10;
+
+		tmp = gmalloc(new_space * sizeof(struct gfs_rgrpd *));
+
+		if (rlist->rl_rgd) {
+			memcpy(tmp, rlist->rl_rgd,
+			       rlist->rl_space * sizeof(struct gfs_rgrpd *));
+			kfree(rlist->rl_rgd);
+		}
+
+		rlist->rl_space = new_space;
+		rlist->rl_rgd = tmp;
+	}
+
+	rlist->rl_rgd[rlist->rl_rgrps++] = rgd;
+}
+
+/**
+ * gfs_rlist_alloc - all RGs have been added to the rlist, now allocate
+ *      and initialize an array of glock holders for them
+ * @rlist: the list of resource groups
+ * @state: the lock state to acquire the RG lock in
+ * @flags: the modifier flags for the holder structures
+ *
+ * FIXME: Don't use gmalloc()
+ *
+ */
+
+void
+gfs_rlist_alloc(struct gfs_rgrp_list *rlist, unsigned int state, int flags)
+{
+	unsigned int x;
+
+	rlist->rl_ghs = gmalloc(rlist->rl_rgrps * sizeof(struct gfs_holder));
+	for (x = 0; x < rlist->rl_rgrps; x++)
+		gfs_holder_init(rlist->rl_rgd[x]->rd_gl,
+				state, flags,
+				&rlist->rl_ghs[x]);
+}
+
+/**
+ * gfs_rlist_free - free a resource group list
+ * @list: the list of resource groups
+ *
+ */
+
+void
+gfs_rlist_free(struct gfs_rgrp_list *rlist)
+{
+	unsigned int x;
+
+	if (rlist->rl_rgd)
+		kfree(rlist->rl_rgd);
+
+	if (rlist->rl_ghs) {
+		for (x = 0; x < rlist->rl_rgrps; x++)
+			gfs_holder_uninit(&rlist->rl_ghs[x]);
+		kfree(rlist->rl_ghs);
+	}
+}
+
+/**
+ * gfs_reclaim_metadata - reclaims unused metadata
+ * @sdp: the file system
+ * @inodes:
+ * @metadata:
+ *
+ * This function will look through the resource groups and
+ * free the unused metadata.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_reclaim_metadata(struct gfs_sbd *sdp, 
+		     uint64_t *inodes,
+		     uint64_t *metadata)
+{
+	struct gfs_holder ji_gh, ri_gh, rgd_gh, t_gh;
+	struct gfs_rgrpd *rgd;
+	struct gfs_rgrp *rg;
+	struct gfs_dinode *di;
+	struct gfs_inum next;
+	struct buffer_head *bh;
+	uint32_t flags;
+	uint32_t goal;
+	unsigned int x;
+	int error = 0;
+
+	*inodes = *metadata = 0;
+
+	/* Acquire the jindex lock here so we don't deadlock with a
+	   process writing the the jindex inode. :-( */
+
+	error = gfs_jindex_hold(sdp, &ji_gh);
+	if (error)
+		goto fail;
+
+	error = gfs_rindex_hold(sdp, &ri_gh);
+	if (error)
+		goto fail_jindex_relse;
+
+	for (rgd = gfs_rgrpd_get_first(sdp);
+	     rgd;
+	     rgd = gfs_rgrpd_get_next(rgd)) {
+		error = gfs_glock_nq_init(rgd->rd_gl,
+					  LM_ST_EXCLUSIVE, GL_NOCACHE,
+					  &rgd_gh);
+		if (error)
+			goto fail_rindex_relse;
+
+		rgrp_verify(rgd);
+
+		rg = &rgd->rd_rg;
+
+		if (!rg->rg_freedi && !rg->rg_freemeta) {
+			gfs_glock_dq_uninit(&rgd_gh);
+			continue;
+		}
+
+		gfs_mhc_zap(rgd);
+		gfs_depend_sync(rgd);
+
+		error = gfs_lock_fs_check_clean(sdp, LM_ST_EXCLUSIVE, &t_gh);
+		if (error)
+			goto fail_gunlock_rg;
+
+		error = gfs_trans_begin(sdp, rgd->rd_ri.ri_length, 0);
+		if (error)
+			goto fail_unlock_fs;
+
+		next = rg->rg_freedi_list;
+
+		for (x = rg->rg_freedi; x--;) {
+			if (!next.no_formal_ino || !next.no_addr) {
+				gfs_consist_rgrpd(rgd);
+				error = -EIO;
+				goto fail_end_trans;
+			}
+
+			blkfree_internal(sdp, next.no_addr, 1, GFS_BLKST_FREE);
+
+			error = gfs_dread(rgd->rd_gl, next.no_addr,
+					  DIO_FORCE | DIO_START | DIO_WAIT, &bh);
+			if (error)
+				goto fail_end_trans;
+
+			di = (struct gfs_dinode *)bh->b_data;
+			flags = di->di_flags;
+			flags = gfs32_to_cpu(flags);
+			if (!(flags & GFS_DIF_UNUSED)) {
+				gfs_consist_rgrpd(rgd);
+				brelse(bh);
+				error = -EIO;
+				goto fail_end_trans;
+			}
+
+			gfs_inum_in(&next, (char *)&di->di_next_unused);
+
+			brelse(bh);
+
+			rg->rg_freedi--;
+			rg->rg_free++;
+			(*inodes)++;
+		}
+
+		if (next.no_formal_ino || next.no_addr) {
+			gfs_consist_rgrpd(rgd);
+			error = -EIO;
+			goto fail_end_trans;
+		}
+		rg->rg_freedi_list = next;
+
+		goal = 0;
+		for (x = rg->rg_freemeta; x--;) {
+			goal = blkalloc_internal(rgd, goal,
+						 GFS_BLKST_FREEMETA, GFS_BLKST_FREE);
+			rg->rg_freemeta--;
+			rg->rg_free++;
+			(*metadata)++;
+		}
+
+		gfs_trans_add_bh(rgd->rd_gl, rgd->rd_bh[0]);
+		gfs_rgrp_out(rg, rgd->rd_bh[0]->b_data);
+
+		gfs_trans_end(sdp);
+
+		gfs_glock_dq_uninit(&t_gh);
+
+		gfs_glock_dq_uninit(&rgd_gh);
+	}
+
+	gfs_glock_dq_uninit(&ri_gh);
+
+	gfs_glock_dq_uninit(&ji_gh);
+
+	return 0;
+
+ fail_end_trans:
+	gfs_trans_end(sdp);
+
+ fail_unlock_fs:
+	gfs_glock_dq_uninit(&t_gh);
+
+ fail_gunlock_rg:
+	gfs_glock_dq_uninit(&rgd_gh);
+
+ fail_rindex_relse:
+	gfs_glock_dq_uninit(&ri_gh);
+
+ fail_jindex_relse:
+	gfs_glock_dq_uninit(&ji_gh);
+
+ fail:
+	return error;
+}
diff -pruN linux-2.6.9.orig/fs/gfs/rgrp.h linux-2.6.9.debug/fs/gfs/rgrp.h
--- linux-2.6.9.orig/fs/gfs/rgrp.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/rgrp.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,83 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __RGRP_DOT_H__
+#define __RGRP_DOT_H__
+
+void gfs_mhc_add(struct gfs_rgrpd *rgd, struct buffer_head **bh,
+			 unsigned int num);
+int gfs_mhc_fish(struct gfs_sbd *sdp, struct buffer_head *bh);
+void gfs_mhc_zap(struct gfs_rgrpd *rgd);
+
+void gfs_depend_add(struct gfs_rgrpd *rgd, uint64_t formal_ino);
+void gfs_depend_sync(struct gfs_rgrpd *rgd);
+
+struct gfs_rgrpd *gfs_blk2rgrpd(struct gfs_sbd *sdp, uint64_t blk);
+struct gfs_rgrpd *gfs_rgrpd_get_first(struct gfs_sbd *sdp);
+struct gfs_rgrpd *gfs_rgrpd_get_next(struct gfs_rgrpd *rgd);
+
+void gfs_clear_rgrpd(struct gfs_sbd *sdp);
+
+int gfs_rindex_hold(struct gfs_sbd *sdp, struct gfs_holder *ri_gh);
+
+int gfs_rgrp_read(struct gfs_rgrpd *rgd);
+void gfs_rgrp_relse(struct gfs_rgrpd *rgd);
+
+void gfs_rgrp_lvb_fill(struct gfs_rgrpd *rgd);
+int gfs_rgrp_lvb_init(struct gfs_rgrpd *rgd);
+
+struct gfs_alloc *gfs_alloc_get(struct gfs_inode *ip);
+void gfs_alloc_put(struct gfs_inode *ip);
+
+int gfs_inplace_reserve_i(struct gfs_inode *ip,
+			 char *file, unsigned int line);
+#define gfs_inplace_reserve(ip) \
+gfs_inplace_reserve_i((ip), __FILE__, __LINE__)
+
+void gfs_inplace_release(struct gfs_inode *ip);
+
+unsigned char gfs_get_block_type(struct gfs_rgrpd *rgd, uint64_t block);
+
+void gfs_blkalloc(struct gfs_inode *ip, uint64_t *block);
+int gfs_metaalloc(struct gfs_inode *ip, uint64_t *block);
+int gfs_dialloc(struct gfs_inode *dip, uint64_t *block);
+
+void gfs_blkfree(struct gfs_inode *ip, uint64_t bstart, uint32_t blen);
+void gfs_metafree(struct gfs_inode *ip, uint64_t bstart, uint32_t blen);
+void gfs_difree_uninit(struct gfs_rgrpd *rgd, uint64_t addr);
+void gfs_difree(struct gfs_rgrpd *rgd, struct gfs_inode *ip);
+
+/*
+ * gfs_rgrp_list
+ *
+ * Used to collect a list of all resource groups spanned by a given
+ *   inode/file/directory
+ */
+struct gfs_rgrp_list {
+	unsigned int rl_rgrps;      /* # (qty) of rgrps in list (array) */
+	unsigned int rl_space;      /* Current capacity in list for rgrps */
+	struct gfs_rgrpd **rl_rgd;  /* Array of ptrs to rgrp descriptors */
+	struct gfs_holder *rl_ghs;  /* Array of glock holders for rgrps */
+};
+
+void gfs_rlist_add(struct gfs_sbd *sdp, struct gfs_rgrp_list *rlist,
+		   uint64_t block);
+void gfs_rlist_alloc(struct gfs_rgrp_list *rlist, unsigned int state,
+		     int flags);
+void gfs_rlist_free(struct gfs_rgrp_list *rlist);
+
+int gfs_reclaim_metadata(struct gfs_sbd *sdp,
+			 uint64_t *inodes,
+			 uint64_t *metadata);
+
+#endif /* __RGRP_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/super.c linux-2.6.9.debug/fs/gfs/super.c
--- linux-2.6.9.orig/fs/gfs/super.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/super.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1083 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "file.h"
+#include "format.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "log.h"
+#include "quota.h"
+#include "recovery.h"
+#include "rgrp.h"
+#include "super.h"
+#include "unlinked.h"
+
+/**
+ * gfs_tune_init - Fill a gfs_tune structure with default values
+ * @gt: tune
+ *
+ */
+
+void
+gfs_tune_init(struct gfs_tune *gt)
+{
+	spin_lock_init(&gt->gt_spin);
+
+	gt->gt_ilimit1 = 100;
+	gt->gt_ilimit1_tries = 3;
+	gt->gt_ilimit1_min = 1;
+	gt->gt_ilimit2 = 500;
+	gt->gt_ilimit2_tries = 10;
+	gt->gt_ilimit2_min = 3;
+	gt->gt_demote_secs = 300;
+	gt->gt_incore_log_blocks = 1024;
+	gt->gt_jindex_refresh_secs = 60;
+	gt->gt_depend_secs = 60;
+	gt->gt_scand_secs = 5;
+	gt->gt_recoverd_secs = 60;
+	gt->gt_logd_secs = 1;
+	gt->gt_quotad_secs = 5;
+	gt->gt_inoded_secs = 15;
+	gt->gt_quota_simul_sync = 64;
+	gt->gt_quota_warn_period = 10;
+	gt->gt_atime_quantum = 3600;
+	gt->gt_quota_quantum = 60;
+	gt->gt_quota_scale_num = 1;
+	gt->gt_quota_scale_den = 1;
+	gt->gt_quota_enforce = 1;
+	gt->gt_quota_account = 1;
+	gt->gt_new_files_jdata = 0;
+	gt->gt_new_files_directio = 0;
+	gt->gt_max_atomic_write = 4 << 20;
+	gt->gt_max_readahead = 1 << 18;
+	gt->gt_lockdump_size = 131072;
+	gt->gt_stall_secs = 600;
+	gt->gt_complain_secs = 10;
+	gt->gt_reclaim_limit = 5000;
+	gt->gt_entries_per_readdir = 32;
+	gt->gt_prefetch_secs = 10;
+	gt->gt_statfs_slots = 64;
+	gt->gt_max_mhc = 10000;
+	gt->gt_greedy_default = HZ / 10;
+	gt->gt_greedy_quantum = HZ / 40;
+	gt->gt_greedy_max = HZ / 4;
+	gt->gt_rgrp_try_threshold = 100;
+}
+
+/**
+ * gfs_check_sb - Check superblock
+ * @sdp: the filesystem
+ * @sb: The superblock
+ * @silent: Don't print a message if the check fails
+ *
+ * Checks the version code of the FS is one that we understand how to
+ * read and that the sizes of the various on-disk structures have not
+ * changed.
+ */
+
+int
+gfs_check_sb(struct gfs_sbd *sdp, struct gfs_sb *sb, int silent)
+{
+	unsigned int x;
+
+	if (sb->sb_header.mh_magic != GFS_MAGIC ||
+	    sb->sb_header.mh_type != GFS_METATYPE_SB) {
+		if (!silent)
+			printk("GFS: not a GFS filesystem\n");
+		return -EINVAL;
+	}
+
+	/*  If format numbers match exactly, we're done.  */
+
+	if (sb->sb_fs_format == GFS_FORMAT_FS &&
+	    sb->sb_multihost_format == GFS_FORMAT_MULTI)
+		return 0;
+
+	if (sb->sb_fs_format != GFS_FORMAT_FS) {
+		for (x = 0; gfs_old_fs_formats[x]; x++)
+			if (gfs_old_fs_formats[x] == sb->sb_fs_format)
+				break;
+
+		if (!gfs_old_fs_formats[x]) {
+			printk("GFS: code version (%u, %u) is incompatible with ondisk format (%u, %u)\n",
+			       GFS_FORMAT_FS, GFS_FORMAT_MULTI,
+			       sb->sb_fs_format, sb->sb_multihost_format);
+			printk("GFS: I don't know how to upgrade this FS\n");
+			return -EINVAL;
+		}
+	}
+
+	if (sb->sb_multihost_format != GFS_FORMAT_MULTI) {
+		for (x = 0; gfs_old_multihost_formats[x]; x++)
+			if (gfs_old_multihost_formats[x] == sb->sb_multihost_format)
+				break;
+
+		if (!gfs_old_multihost_formats[x]) {
+			printk("GFS: code version (%u, %u) is incompatible with ondisk format (%u, %u)\n",
+			     GFS_FORMAT_FS, GFS_FORMAT_MULTI,
+			       sb->sb_fs_format, sb->sb_multihost_format);
+			printk("GFS: I don't know how to upgrade this FS\n");
+			return -EINVAL;
+		}
+	}
+
+	if (!sdp->sd_args.ar_upgrade) {
+		printk("GFS: code version (%u, %u) is incompatible with ondisk format (%u, %u)\n",
+		       GFS_FORMAT_FS, GFS_FORMAT_MULTI,
+		       sb->sb_fs_format, sb->sb_multihost_format);
+		printk("GFS: Use the \"upgrade\" mount option to upgrade the FS\n");
+		printk("GFS: See the manual for more details\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_read_sb - Read super block
+ * @sdp: The GFS superblock
+ * @gl: the glock for the superblock (assumed to be held)
+ * @silent: Don't print message if mount fails
+ *
+ */
+
+int
+gfs_read_sb(struct gfs_sbd *sdp, struct gfs_glock *gl, int silent)
+{
+	struct buffer_head *bh;
+	uint32_t hash_blocks, ind_blocks, leaf_blocks;
+	uint32_t tmp_blocks;
+	unsigned int x;
+	int error;
+
+	error = gfs_dread(gl, GFS_SB_ADDR >> sdp->sd_fsb2bb_shift,
+			  DIO_FORCE | DIO_START | DIO_WAIT, &bh);
+	if (error) {
+		if (!silent)
+			printk("GFS: fsid=%s: can't read superblock\n",
+			       sdp->sd_fsname);
+		return error;
+	}
+
+	gfs_assert(sdp, sizeof(struct gfs_sb) <= bh->b_size,);
+	gfs_sb_in(&sdp->sd_sb, bh->b_data);
+	brelse(bh);
+
+	error = gfs_check_sb(sdp, &sdp->sd_sb, silent);
+	if (error)
+		return error;
+
+	sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift -
+		GFS_BASIC_BLOCK_SHIFT;
+	sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift;
+	sdp->sd_diptrs = (sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode)) /
+		sizeof(uint64_t);
+	sdp->sd_inptrs = (sdp->sd_sb.sb_bsize - sizeof(struct gfs_indirect)) /
+		sizeof(uint64_t);
+	sdp->sd_jbsize = sdp->sd_sb.sb_bsize - sizeof(struct gfs_meta_header);
+	sdp->sd_hash_bsize = sdp->sd_sb.sb_bsize / 2;
+	sdp->sd_hash_bsize_shift = sdp->sd_sb.sb_bsize_shift - 1;
+	sdp->sd_hash_ptrs = sdp->sd_hash_bsize / sizeof(uint64_t);
+
+	/*  Compute maximum reservation required to add a entry to a directory  */
+
+	hash_blocks = DIV_RU(sizeof(uint64_t) * (1 << GFS_DIR_MAX_DEPTH),
+			     sdp->sd_jbsize);
+
+	ind_blocks = 0;
+	for (tmp_blocks = hash_blocks; tmp_blocks > sdp->sd_diptrs;) {
+		tmp_blocks = DIV_RU(tmp_blocks, sdp->sd_inptrs);
+		ind_blocks += tmp_blocks;
+	}
+
+	leaf_blocks = 2 + GFS_DIR_MAX_DEPTH;
+
+	sdp->sd_max_dirres = hash_blocks + ind_blocks + leaf_blocks;
+
+	sdp->sd_heightsize[0] = sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode);
+	sdp->sd_heightsize[1] = sdp->sd_sb.sb_bsize * sdp->sd_diptrs;
+	for (x = 2;; x++) {
+		uint64_t space, d;
+		uint32_t m;
+
+		space = sdp->sd_heightsize[x - 1] * sdp->sd_inptrs;
+		d = space;
+		m = do_div(d, sdp->sd_inptrs);
+
+		if (d != sdp->sd_heightsize[x - 1] || m)
+			break;
+		sdp->sd_heightsize[x] = space;
+	}
+	sdp->sd_max_height = x;
+	gfs_assert(sdp, sdp->sd_max_height <= GFS_MAX_META_HEIGHT,);
+
+	sdp->sd_jheightsize[0] = sdp->sd_sb.sb_bsize - sizeof(struct gfs_dinode);
+	sdp->sd_jheightsize[1] = sdp->sd_jbsize * sdp->sd_diptrs;
+	for (x = 2;; x++) {
+		uint64_t space, d;
+		uint32_t m;
+
+		space = sdp->sd_jheightsize[x - 1] * sdp->sd_inptrs;
+		d = space;
+		m = do_div(d, sdp->sd_inptrs);
+
+		if (d != sdp->sd_jheightsize[x - 1] || m)
+			break;
+		sdp->sd_jheightsize[x] = space;
+	}
+	sdp->sd_max_jheight = x;
+	gfs_assert(sdp, sdp->sd_max_jheight <= GFS_MAX_META_HEIGHT,);
+
+	return 0;
+}
+
+/**
+ * gfs_do_upgrade - upgrade a filesystem
+ * @sdp: The GFS superblock
+ *
+ */
+
+int
+gfs_do_upgrade(struct gfs_sbd *sdp, struct gfs_glock *sb_gl)
+{
+	struct gfs_holder ji_gh, t_gh, j_gh;
+	struct gfs_log_header lh;
+	struct buffer_head *bh;
+	unsigned int x;
+	int error;
+
+	/*  If format numbers match exactly, we're done.  */
+
+	if (sdp->sd_sb.sb_fs_format == GFS_FORMAT_FS &&
+	    sdp->sd_sb.sb_multihost_format == GFS_FORMAT_MULTI) {
+		printk("GFS: fsid=%s: no upgrade necessary\n",
+		       sdp->sd_fsname);
+		sdp->sd_args.ar_upgrade = FALSE;
+		return 0;
+	}
+
+	error = gfs_jindex_hold(sdp, &ji_gh);
+	if (error)
+		goto fail;
+
+	error = gfs_glock_nq_init(sdp->sd_trans_gl,
+				  LM_ST_EXCLUSIVE, GL_NOCACHE,
+				  &t_gh);
+	if (error)
+		goto fail_ji_relse;
+
+	if (test_bit(SDF_ROFS, &sdp->sd_flags)) {
+		printk("GFS: fsid=%s: can't upgrade: read-only FS\n",
+		       sdp->sd_fsname);
+		error = -EROFS;
+		goto fail_gunlock_tr;
+	}
+
+	for (x = 0; x < sdp->sd_journals; x++) {
+		error = gfs_glock_nq_num(sdp,
+					 sdp->sd_jindex[x].ji_addr,
+					 &gfs_meta_glops, LM_ST_SHARED,
+					 LM_FLAG_TRY | GL_NOCACHE, &j_gh);
+		switch (error) {
+		case 0:
+			break;
+
+		case GLR_TRYFAILED:
+			printk("GFS: fsid=%s: journal %u is busy\n",
+			       sdp->sd_fsname, x);
+			error = -EBUSY;
+
+		default:
+			goto fail_gunlock_tr;
+		}
+
+		error = gfs_find_jhead(sdp, &sdp->sd_jindex[x],
+				       j_gh.gh_gl, &lh);
+
+		gfs_glock_dq_uninit(&j_gh);
+
+		if (error)
+			goto fail_gunlock_tr;
+
+		if (!(lh.lh_flags & GFS_LOG_HEAD_UNMOUNT) || lh.lh_last_dump) {
+			printk("GFS: fsid=%s: journal %u is busy\n",
+			       sdp->sd_fsname, x);
+			error = -EBUSY;
+			goto fail_gunlock_tr;
+		}
+	}
+
+	/* We don't need to journal this change because we're changing
+	   only one sector of one block.  We definitely don't want to have
+	   the journaling code running at this point. */
+
+	error = gfs_dread(sb_gl, GFS_SB_ADDR >> sdp->sd_fsb2bb_shift,
+			  DIO_START | DIO_WAIT, &bh);
+	if (error)
+		goto fail_gunlock_tr;
+
+	gfs_sb_in(&sdp->sd_sb, bh->b_data);
+
+	error = gfs_check_sb(sdp, &sdp->sd_sb, FALSE);
+	if (error) {
+		gfs_consist(sdp);
+		brelse(bh);
+		goto fail_gunlock_tr;
+	}
+
+	sdp->sd_sb.sb_fs_format = GFS_FORMAT_FS;
+	sdp->sd_sb.sb_multihost_format = GFS_FORMAT_MULTI;
+
+	gfs_sb_out(&sdp->sd_sb, bh->b_data);
+
+	set_bit(GLF_DIRTY, &sb_gl->gl_flags);
+	error = gfs_dwrite(sdp, bh, DIO_DIRTY | DIO_START | DIO_WAIT);
+
+	brelse(bh);
+
+	gfs_glock_dq_uninit(&t_gh);
+
+	gfs_glock_dq_uninit(&ji_gh);
+
+	if (!error) {
+		printk("GFS: fsid=%s: upgrade successful\n",
+		       sdp->sd_fsname);
+		sdp->sd_args.ar_upgrade = FALSE;
+	}
+
+	return error;
+
+ fail_gunlock_tr:
+	gfs_glock_dq_uninit(&t_gh);
+
+ fail_ji_relse:
+	gfs_glock_dq_uninit(&ji_gh);
+
+ fail:
+	if (error == -EBUSY)
+		printk("GFS: fsid=%s: can't upgrade: the FS is still busy or contains dirty journals\n",
+		       sdp->sd_fsname);
+	else
+		printk("GFS: fsid=%s: can't upgrade: %d\n",
+		       sdp->sd_fsname, error);
+
+	return error;
+}
+
+/**
+ * clear_journalsi - Clear all the journal index information (without locking)
+ * @sdp: The GFS superblock
+ *
+ */
+
+static void
+clear_journalsi(struct gfs_sbd *sdp)
+{
+	if (sdp->sd_jindex) {
+		kfree(sdp->sd_jindex);
+		sdp->sd_jindex = NULL;
+	}
+	sdp->sd_journals = 0;
+}
+
+/**
+ * gfs_clear_journals - Clear all the journal index information
+ * @sdp: The GFS superblock
+ *
+ */
+
+void
+gfs_clear_journals(struct gfs_sbd *sdp)
+{
+	down(&sdp->sd_jindex_lock);
+	clear_journalsi(sdp);
+	up(&sdp->sd_jindex_lock);
+}
+
+/**
+ * gfs_ji_update - Update the journal index information
+ * @ip: The journal index inode
+ *
+ * Returns: errno
+ */
+
+static int
+gfs_ji_update(struct gfs_inode *ip)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+	char buf[sizeof(struct gfs_jindex)];
+	unsigned int j;
+	int error;
+
+	if (do_mod(ip->i_di.di_size, sizeof(struct gfs_jindex))) {
+		gfs_consist_inode(ip);
+		return -EIO;
+	}
+
+	clear_journalsi(sdp);
+
+	sdp->sd_jindex = kmalloc(ip->i_di.di_size, GFP_KERNEL);
+	if (!sdp->sd_jindex)
+		return -ENOMEM;
+	memset(sdp->sd_jindex, 0, ip->i_di.di_size);
+
+	for (j = 0;; j++) {
+		error = gfs_internal_read(ip, buf,
+					  j * sizeof(struct gfs_jindex),
+					  sizeof(struct gfs_jindex));
+		if (!error)
+			break;
+		if (error != sizeof(struct gfs_jindex)) {
+			if (error > 0)
+				error = -EIO;
+			goto fail;
+		}
+
+		gfs_jindex_in(sdp->sd_jindex + j, buf);
+	}
+
+	sdp->sd_journals = j;
+	sdp->sd_jiinode_vn = ip->i_gl->gl_vn;
+
+	return 0;
+
+ fail:
+	clear_journalsi(sdp);
+	return error;
+}
+
+/**
+ * gfs_jindex_hold - Grab a lock on the jindex
+ * @sdp: The GFS superblock
+ * @ji_gh: the holder for the jindex glock
+ *
+ * This makes sure that we're using the latest copy of the journal index
+ *   special file (this describes all of the journals for this filesystem),
+ *   which might have been updated if someone added journals
+ *   (via gfs_jadd utility).
+ *
+ * This is very similar to the gfs_rindex_hold() function, except that
+ * in general we hold the jindex lock for longer periods of time and
+ * we grab it far less frequently (in general) then the rgrp lock.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_jindex_hold(struct gfs_sbd *sdp, struct gfs_holder *ji_gh)
+{
+	struct gfs_inode *ip = sdp->sd_jiinode;
+	struct gfs_glock *gl = ip->i_gl;
+	int error;
+
+	error = gfs_glock_nq_init(gl, LM_ST_SHARED, 0, ji_gh);
+	if (error)
+		return error;
+
+	/* Read new copy from disk if we don't have the latest */
+	if (sdp->sd_jiinode_vn != gl->gl_vn) {
+		down(&sdp->sd_jindex_lock);
+		if (sdp->sd_jiinode_vn != gl->gl_vn)
+			error = gfs_ji_update(ip);
+		up(&sdp->sd_jindex_lock);
+	}
+
+	if (error)
+		gfs_glock_dq_uninit(ji_gh);
+
+	return error;
+}
+
+/**
+ * gfs_get_jiinode - Read-in the special (hidden) journal index inode
+ * @sdp: The GFS superblock
+ *
+ * Returns: errno
+ *
+ * This reads-in just the dinode, not the special file contents that describe
+ *   the journals themselves (see gfs_jindex_hold()).
+ */
+
+int
+gfs_get_jiinode(struct gfs_sbd *sdp)
+{
+	struct gfs_holder ji_gh;
+	int error;
+
+	error = gfs_glock_nq_num(sdp,
+				 sdp->sd_sb.sb_jindex_di.no_formal_ino,
+				 &gfs_inode_glops,
+				 LM_ST_SHARED, GL_LOCAL_EXCL,
+				 &ji_gh);
+	if (error)
+		return error;
+
+	error = gfs_inode_get(ji_gh.gh_gl, &sdp->sd_sb.sb_jindex_di,
+			      CREATE, &sdp->sd_jiinode);
+	if (!error) {
+		sdp->sd_jiinode_vn = ji_gh.gh_gl->gl_vn - 1;
+		set_bit(GLF_STICKY, &ji_gh.gh_gl->gl_flags);
+	}
+
+	gfs_glock_dq_uninit(&ji_gh);
+
+	return error;
+}
+
+/**
+ * gfs_get_riinode - Read in the special (hidden) resource group index inode
+ * @sdp: The GFS superblock
+ *
+ * Returns: errno
+ *
+ * This reads-in just the dinode, not the special file contents that describe
+ *   the resource groups themselves (see gfs_rindex_hold()).
+ */
+
+int
+gfs_get_riinode(struct gfs_sbd *sdp)
+{
+	struct gfs_holder ri_gh;
+	int error;
+
+	error = gfs_glock_nq_num(sdp,
+				 sdp->sd_sb.sb_rindex_di.no_formal_ino,
+				 &gfs_inode_glops,
+				 LM_ST_SHARED, GL_LOCAL_EXCL,
+				 &ri_gh);
+	if (error)
+		return error;
+
+	error = gfs_inode_get(ri_gh.gh_gl, &sdp->sd_sb.sb_rindex_di,
+			      CREATE, &sdp->sd_riinode);
+	if (!error) {
+		sdp->sd_riinode_vn = ri_gh.gh_gl->gl_vn - 1;
+		set_bit(GLF_STICKY, &ri_gh.gh_gl->gl_flags);
+	}
+
+	gfs_glock_dq_uninit(&ri_gh);
+
+	return error;
+}
+
+/**
+ * gfs_get_rootinode - Read in the filesystem's root inode
+ * @sdp: The GFS superblock
+ *
+ * Returns: errno
+ */
+
+int
+gfs_get_rootinode(struct gfs_sbd *sdp)
+{
+	struct gfs_holder i_gh;
+	int error;
+
+	error = gfs_glock_nq_num(sdp,
+				 sdp->sd_sb.sb_root_di.no_formal_ino,
+				 &gfs_inode_glops,
+				 LM_ST_SHARED, GL_LOCAL_EXCL,
+				 &i_gh);
+	if (error)
+		return error;
+
+	error = gfs_inode_get(i_gh.gh_gl, &sdp->sd_sb.sb_root_di,
+			      CREATE, &sdp->sd_rooti);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_get_qinode - Read in the special (hidden) quota inode
+ * @sdp: The GFS superblock
+ *
+ * If one is not on-disk already, create a new one.
+ * Does not read in file contents, just the dinode.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_get_qinode(struct gfs_sbd *sdp)
+{
+	struct gfs_holder i_gh;
+	int error;
+
+	/* Create, if not on-disk already */
+	if (!sdp->sd_sb.sb_quota_di.no_formal_ino) {
+		error = gfs_alloc_qinode(sdp);
+		if (error)
+			return error;
+	}
+
+	error = gfs_glock_nq_num(sdp,
+				 sdp->sd_sb.sb_quota_di.no_formal_ino,
+				 &gfs_inode_glops,
+				 LM_ST_SHARED, GL_LOCAL_EXCL,
+				 &i_gh);
+	if (error)
+		return error;
+
+	error = gfs_inode_get(i_gh.gh_gl, &sdp->sd_sb.sb_quota_di,
+			      CREATE, &sdp->sd_qinode);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_get_linode - Read in the special (hidden) license inode
+ * @sdp: The GFS superblock
+ *
+ * If one is not on-disk already, create a new one.
+ * Does not read in file contents, just the dinode.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_get_linode(struct gfs_sbd *sdp)
+{
+	struct gfs_holder i_gh;
+	int error;
+
+	/* Create, if not on-disk already */
+	if (!sdp->sd_sb.sb_license_di.no_formal_ino) {
+		error = gfs_alloc_linode(sdp);
+		if (error)
+			return error;
+	}
+
+	error = gfs_glock_nq_num(sdp,
+				 sdp->sd_sb.sb_license_di.no_formal_ino,
+				 &gfs_inode_glops,
+				 LM_ST_SHARED, GL_LOCAL_EXCL,
+				 &i_gh);
+	if (error)
+		return error;
+
+	error = gfs_inode_get(i_gh.gh_gl, &sdp->sd_sb.sb_license_di,
+			      CREATE, &sdp->sd_linode);
+
+	gfs_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gfs_make_fs_rw - Turn a Read-Only FS into a Read-Write one
+ * @sdp: the filesystem
+ *
+ * Returns: errno
+ */
+
+int
+gfs_make_fs_rw(struct gfs_sbd *sdp)
+{
+	struct gfs_glock *j_gl = sdp->sd_journal_gh.gh_gl;
+	struct gfs_holder t_gh;
+	struct gfs_log_header head;
+	int error;
+
+	error = gfs_glock_nq_init(sdp->sd_trans_gl,
+				  LM_ST_SHARED,
+				  GL_LOCAL_EXCL | GL_EXACT,
+				  &t_gh);
+	if (error)
+		return error;
+
+	j_gl->gl_ops->go_inval(j_gl, DIO_METADATA | DIO_DATA);
+
+	error = gfs_find_jhead(sdp, &sdp->sd_jdesc, j_gl, &head);
+	if (error)
+		goto fail;
+
+	if (!(head.lh_flags & GFS_LOG_HEAD_UNMOUNT)) {
+		gfs_consist(sdp);
+		error = -EIO;
+		goto fail;
+	}
+
+	/*  Initialize some head of the log stuff  */
+	sdp->sd_sequence = head.lh_sequence;
+	sdp->sd_log_head = head.lh_first + 1;
+
+	error = gfs_recover_dump(sdp);
+	if (error)
+		goto fail;
+
+	set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
+	clear_bit(SDF_ROFS, &sdp->sd_flags);
+
+	set_bit(GLF_DIRTY, &j_gl->gl_flags);
+	gfs_log_dump(sdp, TRUE);
+
+	gfs_glock_dq_uninit(&t_gh);
+
+	return 0;
+
+ fail:
+	t_gh.gh_flags |= GL_NOCACHE;
+	gfs_glock_dq_uninit(&t_gh);
+
+	return error;
+}
+
+/**
+ * gfs_make_fs_ro - Turn a Read-Write FS into a Read-Only one
+ * @sdp: the filesystem
+ *
+ * Returns: errno
+ */
+
+int
+gfs_make_fs_ro(struct gfs_sbd *sdp)
+{
+	struct gfs_holder t_gh;
+	int error;
+
+	error = gfs_glock_nq_init(sdp->sd_trans_gl,
+				  LM_ST_SHARED,
+				  GL_LOCAL_EXCL | GL_EXACT | GL_NOCACHE,
+				  &t_gh);
+	if (error &&
+	    !test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
+		return error;
+
+	gfs_log_flush(sdp);
+	gfs_quota_sync(sdp);
+	gfs_quota_scan(sdp);
+
+	gfs_sync_meta(sdp);
+	gfs_log_dump(sdp, TRUE);
+	gfs_log_shutdown(sdp);
+
+	set_bit(SDF_ROFS, &sdp->sd_flags);
+	clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
+
+	if (t_gh.gh_gl)
+		gfs_glock_dq_uninit(&t_gh);
+
+	gfs_unlinked_cleanup(sdp);
+	gfs_quota_cleanup(sdp);
+
+	return error;
+}
+
+/**
+ * stat_gfs_fill - fill in the sg for a given RG
+ * @rgd: the RG
+ * @sg: the sg structure
+ *
+ * Returns: 0 on success, -ESTALE if the LVB is invalid
+ */
+
+static int
+stat_gfs_fill(struct gfs_rgrpd *rgd, struct gfs_stat_gfs *sg)
+{
+	struct gfs_rgrp_lvb *rb = (struct gfs_rgrp_lvb *)rgd->rd_gl->gl_lvb;
+
+	if (gfs32_to_cpu(rb->rb_magic) != GFS_MAGIC)
+		return -ESTALE;
+
+	sg->sg_total_blocks += rgd->rd_ri.ri_data;
+	sg->sg_free += gfs32_to_cpu(rb->rb_free);
+	sg->sg_used_dinode += gfs32_to_cpu(rb->rb_useddi);
+	sg->sg_free_dinode += gfs32_to_cpu(rb->rb_freedi);
+	sg->sg_used_meta += gfs32_to_cpu(rb->rb_usedmeta);
+	sg->sg_free_meta += gfs32_to_cpu(rb->rb_freemeta);
+
+	return 0;
+}
+
+/**
+ * stat_gfs_async - Stat a filesystem using asynchronous locking
+ * @sdp: the filesystem
+ * @sg: the sg info that will be returned
+ * @interruptible: TRUE if we should look for signals.
+ *
+ * Any error (other than a signal) will cause this routine to fall back
+ * to the synchronous version.
+ *
+ * FIXME: This really shouldn't busy wait like this.
+ *
+ * Returns: errno
+ */
+
+static int
+stat_gfs_async(struct gfs_sbd *sdp, struct gfs_stat_gfs *sg, int interruptible)
+{
+	struct gfs_rgrpd *rgd_next = gfs_rgrpd_get_first(sdp);
+	struct gfs_holder *gha, *gh;
+	unsigned int slots = gfs_tune_get(sdp, gt_statfs_slots);
+	unsigned int x;
+	int done;
+	int error = 0, err;
+
+	memset(sg, 0, sizeof(struct gfs_stat_gfs));
+
+	gha = kmalloc(slots * sizeof(struct gfs_holder), GFP_KERNEL);
+	if (!gha)
+		return -ENOMEM;
+	memset(gha, 0, slots * sizeof(struct gfs_holder));
+
+	for (;;) {
+		done = TRUE;
+
+		for (x = 0; x < slots; x++) {
+			gh = gha + x;
+
+			if (gh->gh_gl && gfs_glock_poll(gh)) {
+				err = gfs_glock_wait(gh);
+				if (err) {
+					gfs_holder_uninit(gh);
+					error = err;
+				} else {
+					if (!error)
+						error = stat_gfs_fill(gl2rgd(gh->gh_gl), sg);
+					gfs_glock_dq_uninit(gh);
+				}
+			}
+
+			if (gh->gh_gl)
+				done = FALSE;
+			else if (rgd_next && !error) {
+				error = gfs_glock_nq_init(rgd_next->rd_gl,
+							  LM_ST_SHARED,
+							  GL_LOCAL_EXCL | GL_SKIP | GL_ASYNC,
+							  gh);
+				rgd_next = gfs_rgrpd_get_next(rgd_next);
+				done = FALSE;
+			}
+
+			if (interruptible && signal_pending(current))
+				error = -ERESTARTSYS;
+		}
+
+		if (done)
+			break;
+
+		yield();
+	}
+
+	kfree(gha);
+
+	return error;
+}
+
+/**
+ * stat_gfs_sync - Stat a filesystem using synchronous locking
+ * @sdp: the filesystem
+ * @sg: the sg info that will be returned
+ * @interruptible: TRUE if we should look for signals.
+ *
+ * Returns: errno
+ */
+
+static int
+stat_gfs_sync(struct gfs_sbd *sdp, struct gfs_stat_gfs *sg, int interruptible)
+{
+	struct gfs_holder rgd_gh;
+	struct gfs_rgrpd *rgd;
+	int error;
+
+	memset(sg, 0, sizeof(struct gfs_stat_gfs));
+
+	for (rgd = gfs_rgrpd_get_first(sdp);
+	     rgd;
+	     rgd = gfs_rgrpd_get_next(rgd)) {
+		for (;;) {
+			error = gfs_glock_nq_init(rgd->rd_gl,
+						  LM_ST_SHARED,
+						  GL_LOCAL_EXCL | GL_SKIP,
+						  &rgd_gh);
+			if (error)
+				return error;
+
+			error = stat_gfs_fill(rgd, sg);
+
+			gfs_glock_dq_uninit(&rgd_gh);
+
+			if (!error)
+				break;
+
+			error = gfs_rgrp_lvb_init(rgd);
+			if (error)
+				return error;
+		}
+
+		if (interruptible && signal_pending(current))
+			return -ERESTARTSYS;
+	}
+
+	return 0;
+}
+
+/**
+ * gfs_stat_gfs - Do a statfs
+ * @sdp: the filesystem
+ * @sg: the sg structure
+ * @interruptible:  Stop if there is a signal pending
+ *
+ * Returns: errno
+ */
+
+int
+gfs_stat_gfs(struct gfs_sbd *sdp, struct gfs_stat_gfs *sg, int interruptible)
+{
+	struct gfs_holder ri_gh;
+	int error;
+
+	error = gfs_rindex_hold(sdp, &ri_gh);
+	if (error)
+		return error;
+
+	error = stat_gfs_async(sdp, sg, interruptible);
+	if (error == -ESTALE)
+		error = stat_gfs_sync(sdp, sg, interruptible);
+
+	gfs_glock_dq_uninit(&ri_gh);
+
+	return error;
+}
+
+/**
+ * gfs_lock_fs_check_clean - Stop all writes to the FS and check that all journals are clean
+ * @sdp: the file system
+ * @state: the state to put the transaction lock into
+ * @t_gh: the hold on the transaction lock
+ *
+ * Returns: errno
+ */
+
+int
+gfs_lock_fs_check_clean(struct gfs_sbd *sdp, unsigned int state,
+			struct gfs_holder *t_gh)
+{
+	struct gfs_holder ji_gh, cl_gh;
+	struct gfs_log_header lh;
+	unsigned int x;
+	int error;
+
+	error = gfs_jindex_hold(sdp, &ji_gh);
+	if (error)
+		return error;
+
+	error = gfs_glock_nq_num(sdp,
+				 GFS_CRAP_LOCK, &gfs_meta_glops,
+				 LM_ST_SHARED, GL_NOCACHE,
+				 &cl_gh);
+	if (error)
+		goto fail;
+
+	error = gfs_glock_nq_init(sdp->sd_trans_gl, state,
+				  LM_FLAG_PRIORITY | GL_EXACT | GL_NOCACHE,
+				  t_gh);
+	if (error)
+		goto fail_gunlock_craplock;
+
+	for (x = 0; x < sdp->sd_journals; x++) {
+		error = gfs_find_jhead(sdp, &sdp->sd_jindex[x],
+				       cl_gh.gh_gl, &lh);
+		if (error)
+			goto fail_gunlock_trans;
+
+		if (!(lh.lh_flags & GFS_LOG_HEAD_UNMOUNT)) {
+			error = -EBUSY;
+			goto fail_gunlock_trans;
+		}
+	}
+
+	gfs_glock_dq_uninit(&cl_gh);
+	gfs_glock_dq_uninit(&ji_gh);
+
+	return 0;
+
+ fail_gunlock_trans:
+	gfs_glock_dq_uninit(t_gh);
+
+ fail_gunlock_craplock:
+	gfs_glock_dq_uninit(&cl_gh);
+
+ fail:
+	gfs_glock_dq_uninit(&ji_gh);
+
+	return error;
+}
+
+/**
+ * gfs_freeze_fs - freezes the file system
+ * @sdp: the file system
+ *
+ * This function flushes data and meta data for all machines by
+ * aquiring the transaction log exclusively.  All journals are
+ * ensured to be in a clean state as well.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_freeze_fs(struct gfs_sbd *sdp)
+{
+	int error = 0;
+
+	down(&sdp->sd_freeze_lock);
+
+	if (!sdp->sd_freeze_count++) {
+		error = gfs_lock_fs_check_clean(sdp, LM_ST_DEFERRED,
+						&sdp->sd_freeze_gh);
+		if (error)
+			sdp->sd_freeze_count--;
+		else
+			sdp->sd_freeze_gh.gh_owner = NULL;
+	}
+
+	up(&sdp->sd_freeze_lock);
+
+	return error;
+}
+
+/**
+ * gfs_unfreeze_fs - unfreezes the file system
+ * @sdp: the file system
+ *
+ * This function allows the file system to proceed by unlocking
+ * the exclusively held transaction lock.  Other GFS nodes are
+ * now free to acquire the lock shared and go on with their lives.
+ *
+ */
+
+void
+gfs_unfreeze_fs(struct gfs_sbd *sdp)
+{
+	down(&sdp->sd_freeze_lock);
+
+	if (sdp->sd_freeze_count && !--sdp->sd_freeze_count)
+		gfs_glock_dq_uninit(&sdp->sd_freeze_gh);
+
+	up(&sdp->sd_freeze_lock);
+}
diff -pruN linux-2.6.9.orig/fs/gfs/super.h linux-2.6.9.debug/fs/gfs/super.h
--- linux-2.6.9.orig/fs/gfs/super.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/super.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,62 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __SUPER_DOT_H__
+#define __SUPER_DOT_H__
+
+void gfs_tune_init(struct gfs_tune *gt);
+
+int gfs_check_sb(struct gfs_sbd *sdp, struct gfs_sb *sb, int silent);
+int gfs_read_sb(struct gfs_sbd *sdp, struct gfs_glock *gl, int silent);
+int gfs_do_upgrade(struct gfs_sbd *sdp, struct gfs_glock *gl_sb);
+
+static __inline__ unsigned int
+gfs_num_journals(struct gfs_sbd *sdp)
+{
+	unsigned int num;
+	down(&sdp->sd_jindex_lock);
+	num = sdp->sd_journals;
+	up(&sdp->sd_jindex_lock);
+	return num;
+}
+
+int gfs_jindex_hold(struct gfs_sbd *sdp, struct gfs_holder *ji_gh);
+void gfs_clear_journals(struct gfs_sbd *sdp);
+
+int gfs_get_jiinode(struct gfs_sbd *sdp);
+int gfs_get_riinode(struct gfs_sbd *sdp);
+int gfs_get_rootinode(struct gfs_sbd *sdp);
+int gfs_get_qinode(struct gfs_sbd *sdp);
+int gfs_get_linode(struct gfs_sbd *sdp);
+
+int gfs_make_fs_rw(struct gfs_sbd *sdp);
+int gfs_make_fs_ro(struct gfs_sbd *sdp);
+
+struct gfs_stat_gfs {
+	uint64_t sg_total_blocks;
+	uint64_t sg_free;
+	uint64_t sg_used_dinode;
+	uint64_t sg_free_dinode;
+	uint64_t sg_used_meta;
+	uint64_t sg_free_meta;
+};
+
+int gfs_stat_gfs(struct gfs_sbd *sdp, struct gfs_stat_gfs *sg,
+		 int interruptible);
+
+int gfs_lock_fs_check_clean(struct gfs_sbd *sdp, unsigned int state,
+			    struct gfs_holder *t_gh);
+int gfs_freeze_fs(struct gfs_sbd *sdp);
+void gfs_unfreeze_fs(struct gfs_sbd *sdp);
+
+#endif /* __SUPER_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/trans.c linux-2.6.9.debug/fs/gfs/trans.c
--- linux-2.6.9.orig/fs/gfs/trans.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/trans.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,466 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "dio.h"
+#include "glock.h"
+#include "log.h"
+#include "lops.h"
+#include "quota.h"
+#include "trans.h"
+#include "unlinked.h"
+
+/**
+ * gfs_trans_print - Print a transaction to the console
+ * @sdp: the filesystem
+ * @tr: The GFS transaction
+ * @where: Situation of transaction
+ *
+ */
+
+void
+gfs_trans_print(struct gfs_sbd *sdp, struct gfs_trans *tr, unsigned int where)
+{
+	struct gfs_log_element *le;
+	struct list_head *tmp, *head;
+	unsigned int mblks = 0, eblks = 0;
+
+	LO_TRANS_SIZE(sdp, tr, &mblks, &eblks, NULL, NULL);
+
+	printk("Transaction:  (%s, %u)\n", tr->tr_file, tr->tr_line);
+	printk("  tr_mblks_asked = %u, tr_eblks_asked = %u, tr_seg_reserved = %u\n",
+	       tr->tr_mblks_asked, tr->tr_eblks_asked, tr->tr_seg_reserved);
+	printk("  mblks = %u, eblks = %u\n", mblks, eblks);
+	printk("  tr_flags = 0x%.8X\n", tr->tr_flags);
+
+	for (head = &tr->tr_elements, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		le = list_entry(tmp, struct gfs_log_element, le_list);
+		LO_PRINT(sdp, le, where);
+	}
+
+	printk("End Trans\n");
+}
+
+/**
+ * gfs_trans_begin_i - Prepare to start a transaction
+ * @sdp: The GFS superblock
+ * @meta_blocks: Reserve this many metadata blocks in the log
+ * @extra_blocks: Number of non-metadata blocks to reserve
+ *
+ * Allocate the struct gfs_trans struct.
+ * Grab a shared TRANSaction lock (protects this transaction from
+ *   overlapping with unusual fs writes, e.g. journal replay, fs upgrade,
+ *   while allowing simultaneous transaction writes throughout cluster).
+ * Reserve space in the log.  @meta_blocks and @extra_blocks must indicate
+ *   the worst case (maximum) size of the transaction.
+ * Record this transaction as the *one* transaction being built by this
+ *   Linux process, in current->journal_info.
+ *
+ * Returns: errno
+ */
+
+int
+gfs_trans_begin_i(struct gfs_sbd *sdp,
+		  unsigned int meta_blocks, unsigned int extra_blocks,
+		  char *file, unsigned int line)
+{
+	struct gfs_trans *tr;
+	unsigned int blocks;
+	int error;
+
+	tr = kmalloc(sizeof(struct gfs_trans), GFP_KERNEL);
+	if (!tr)
+		return -ENOMEM;
+	memset(tr, 0, sizeof(struct gfs_trans));
+
+	INIT_LIST_HEAD(&tr->tr_elements);
+	INIT_LIST_HEAD(&tr->tr_free_bufs);
+	INIT_LIST_HEAD(&tr->tr_free_bmem);
+	INIT_LIST_HEAD(&tr->tr_bufs);
+	INIT_LIST_HEAD(&tr->tr_ail_bufs);
+	tr->tr_file = file;
+	tr->tr_line = line;
+
+	error = -ENOMEM;
+	tr->tr_t_gh = gfs_holder_get(sdp->sd_trans_gl, LM_ST_SHARED, 0);
+	if (!tr->tr_t_gh)
+		goto fail;
+
+	error = gfs_glock_nq(tr->tr_t_gh);
+	if (error)
+		goto fail_holder_put;
+
+	if (test_bit(SDF_ROFS, &sdp->sd_flags)) {
+		tr->tr_t_gh->gh_flags |= GL_NOCACHE;
+		error = -EROFS;
+		goto fail_gunlock;
+	}
+
+	/*  Do log reservation  */
+
+	tr->tr_mblks_asked = meta_blocks;
+	tr->tr_eblks_asked = extra_blocks;
+
+	blocks = 1;
+	if (meta_blocks)
+		blocks += gfs_struct2blk(sdp, meta_blocks,
+					 sizeof(struct gfs_block_tag)) +
+			meta_blocks;
+	blocks += extra_blocks;
+	tr->tr_seg_reserved = gfs_blk2seg(sdp, blocks);
+
+	error = gfs_log_reserve(sdp, tr->tr_seg_reserved, FALSE);
+	if (error)
+		goto fail_gunlock;
+
+	gfs_assert(sdp, !current_transaction,);
+	current_transaction = tr;
+
+	return 0;
+
+ fail_gunlock:
+	gfs_glock_dq(tr->tr_t_gh);
+
+ fail_holder_put:
+	gfs_holder_put(tr->tr_t_gh);
+
+ fail:
+	kfree(tr);
+
+	return error;
+}
+
+/**
+ * gfs_trans_end - End a transaction
+ * @sdp: The GFS superblock
+ *
+ * If buffers were actually added to the transaction,
+ * commit it.
+ *
+ */
+
+void
+gfs_trans_end(struct gfs_sbd *sdp)
+{
+	struct gfs_trans *tr;
+	struct gfs_holder *t_gh;
+	struct list_head *tmp, *head;
+	struct gfs_log_element *le;
+
+	/* Linux task struct indicates current new trans for this process.
+	 * We're done building it, so set it to NULL */
+	tr = current_transaction;
+	gfs_assert(sdp, tr,);
+	current_transaction = NULL;
+
+	t_gh = tr->tr_t_gh;
+	tr->tr_t_gh = NULL;
+
+	/* If no buffers were ever added to trans, forget it */
+	if (list_empty(&tr->tr_elements)) {
+		gfs_log_release(sdp, tr->tr_seg_reserved);
+		kfree(tr);
+
+		gfs_glock_dq(t_gh);
+		gfs_holder_put(t_gh);
+
+		return;
+	}
+
+	/* Do trans_end log-operation for each log element */
+	for (head = &tr->tr_elements, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		le = list_entry(tmp, struct gfs_log_element, le_list);
+		LO_TRANS_END(sdp, le);
+	}
+
+	gfs_log_commit(sdp, tr);
+
+	gfs_glock_dq(t_gh);
+	gfs_holder_put(t_gh);
+
+	if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS)
+		gfs_log_flush(sdp);
+}
+
+/**
+ * gfs_trans_add_gl - Add a glock to a transaction
+ * @gl: the glock
+ *
+ * If not already attached, add the given glock to this process's transaction.
+ *
+ * Even though no glock info will be written to the on-disk log, the glocks
+ *   associated with a transaction provide bridges by which to combine
+ *   a just-built transaction with an earlier incore committed transaction
+ *   that was protected by the same glock.  See incore_commit().
+ *   Combining transactions makes for more efficient logging.
+ *
+ * Note that more than one glock may be associated with a single transaction.
+ *   However, a given glock protects no more than *one* transaction at a
+ *   given stage in the transaction pipeline (i.e. new or incore-committed).
+ *   After all, the process holds the glock EX (so no other process can be
+ *   building a separate trans protected by this glock), and the process can
+ *   build only one transaction at a time.
+ *
+ * Rules:
+ *   This process must hold the glock in EXclusive mode, since we're going
+ *   to be writing to something protected by this glock.
+ */
+
+void
+gfs_trans_add_gl(struct gfs_glock *gl)
+{
+	if (!gl->gl_new_le.le_trans) {
+		gfs_assert_withdraw(gl->gl_sbd,
+				    gfs_glock_is_locked_by_me(gl) &&
+				    gfs_glock_is_held_excl(gl));
+		gfs_glock_hold(gl); /* Released in glock_trans_end() */
+
+		/* Ask for eventual flush of (meta)data protected by this glock,
+		   once trans is complete and logged.  */
+		set_bit(GLF_DIRTY, &gl->gl_flags);
+
+		/* Invoke generic_le_add() */
+		LO_ADD(gl->gl_sbd, &gl->gl_new_le);
+		gl->gl_new_le.le_trans->tr_num_gl++;
+	}
+}
+
+/**
+ * gfs_trans_add_bh - Add a to-be-modified buffer to the current transaction
+ * @gl: the glock the buffer belongs to
+ * @bh: The buffer to add
+ *
+ * Add a to-be-modified buffer to the current being-built (i.e. new) trans,
+ *   and pin the buffer in memory.
+ *
+ * Caller must hold the glock protecting this buffer.
+ *
+ * Call this as many times as you want during transaction formation.  It does
+ * its attachment work only once.  After buffer is attached to trans, the
+ * process building the trans can modify the buffer again and again (calling
+ * this function before each change).  Only the final result (within this trans)
+ * will be written to log.  A good example is when allocating blocks in an RG,
+ * a given bitmap buffer may be updated many times within a transaction.
+ *
+ * Note:  This final result will also be written to its in-place location,
+ *  unless this transaction gets combined with a later transaction,
+ *  in which case only the later result will go to in-place.
+ *
+ */
+
+void
+gfs_trans_add_bh(struct gfs_glock *gl, struct buffer_head *bh)
+{
+	struct gfs_sbd *sdp = gl->gl_sbd;
+	struct gfs_bufdata *bd;
+
+	/* Make sure GFS private info struct is attached to buffer head */
+	bd = bh2bd(bh);
+	if (!bd) {
+		gfs_attach_bufdata(bh, gl);
+		bd = bh2bd(bh);
+	}
+
+	/* If buffer has already been attached to trans, we're done */
+	if (bd->bd_new_le.le_trans)
+		return;
+
+	gfs_meta_check(sdp, bh);
+
+	gfs_assert(sdp, bd->bd_gl == gl,);
+
+	/* Make sure glock is attached to trans */
+	if (!gl->gl_new_le.le_trans)
+		gfs_trans_add_gl(gl);
+
+	gfs_dpin(sdp, bh);
+
+	/* Attach buffer to trans */
+	LO_ADD(sdp, &bd->bd_new_le);
+	bd->bd_new_le.le_trans->tr_num_buf++;
+}
+
+/**
+ * gfs_trans_add_unlinked - Add an unlinked or dealloced tag to
+ *      the current transaction
+ * @sdp: the filesystem
+ * @type: the type of entry
+ * @inum: the inode number
+ *
+ * Returns: the unlinked structure
+ */
+
+struct gfs_unlinked *
+gfs_trans_add_unlinked(struct gfs_sbd *sdp, unsigned int type,
+		       struct gfs_inum *inum)
+{
+	struct gfs_unlinked *ul;
+
+	/* Find in fileystem's unlinked list, or create */
+	ul = gfs_unlinked_get(sdp, inum, CREATE);
+
+	LO_ADD(sdp, &ul->ul_new_le);
+
+	switch (type) {
+	case GFS_LOG_DESC_IUL:
+		set_bit(ULF_NEW_UL, &ul->ul_flags);
+		ul->ul_new_le.le_trans->tr_num_iul++;
+		break;
+	case GFS_LOG_DESC_IDA:
+		clear_bit(ULF_NEW_UL, &ul->ul_flags);
+		ul->ul_new_le.le_trans->tr_num_ida++;
+		break;
+	default:
+		gfs_assert(sdp, FALSE,);
+		break;
+	}
+
+	return ul;
+}
+
+/**
+ * gfs_trans_add_quota - Add quota changes to a transaction
+ * @sdp: the filesystem
+ * @change: The number of blocks allocated (positive) or freed (negative)
+ * @uid: the user ID doing the change
+ * @gid: the group ID doing the change
+ *
+ */
+
+void
+gfs_trans_add_quota(struct gfs_sbd *sdp, int64_t change,
+		    uint32_t uid, uint32_t gid)
+{
+	struct gfs_trans *tr;
+	struct list_head *tmp, *head, *next;
+	struct gfs_log_element *le;
+	struct gfs_quota_le *ql;
+	int found_uid, found_gid;
+	int error;
+
+	if (!gfs_tune_get(sdp, gt_quota_account))
+		return;
+	if (gfs_assert_warn(sdp, change))
+		return;
+
+	found_uid = (uid == NO_QUOTA_CHANGE);
+	found_gid = (gid == NO_QUOTA_CHANGE);
+
+	if (gfs_assert_warn(sdp, !found_uid || !found_gid))
+		return;
+
+	tr = current_transaction;
+	gfs_assert(sdp, tr,);
+
+	for (head = &tr->tr_elements, tmp = head->next, next = tmp->next;
+	     tmp != head;
+	     tmp = next, next = next->next) {
+		le = list_entry(tmp, struct gfs_log_element, le_list);
+		if (le->le_ops != &gfs_quota_lops)
+			continue;
+
+		ql = container_of(le, struct gfs_quota_le, ql_le);
+
+		if (test_bit(QDF_USER, &ql->ql_data->qd_flags)) {
+			if (ql->ql_data->qd_id == uid) {
+				ql->ql_change += change;
+
+				spin_lock(&sdp->sd_quota_lock);
+				ql->ql_data->qd_change_new += change;
+				spin_unlock(&sdp->sd_quota_lock);
+
+				list_del(&le->le_list);
+
+				if (ql->ql_change)
+					list_add(&le->le_list,
+						 &tr->tr_elements);
+				else {
+					gfs_quota_put(sdp, ql->ql_data);
+					kfree(ql);
+					tr->tr_num_q--;
+				}
+
+				gfs_assert(sdp, !found_uid,);
+				found_uid = TRUE;
+				if (found_gid)
+					break;
+			}
+		} else {
+			if (ql->ql_data->qd_id == gid) {
+				ql->ql_change += change;
+
+				spin_lock(&sdp->sd_quota_lock);
+				ql->ql_data->qd_change_new += change;
+				spin_unlock(&sdp->sd_quota_lock);
+
+				list_del(&le->le_list);
+
+				if (ql->ql_change)
+					list_add(&le->le_list,
+						 &tr->tr_elements);
+				else {
+					gfs_quota_put(sdp, ql->ql_data);
+					kfree(ql);
+					tr->tr_num_q--;
+				}
+
+				gfs_assert(sdp, !found_gid,);
+				found_gid = TRUE;
+				if (found_uid)
+					break;
+			}
+		}
+	}
+
+	while (!found_uid || !found_gid) {
+		ql = gmalloc(sizeof(struct gfs_quota_le));
+		memset(ql, 0, sizeof(struct gfs_quota_le));
+
+		INIT_LE(&ql->ql_le, &gfs_quota_lops);
+
+		if (found_uid) {
+			error = gfs_quota_get(sdp, FALSE, gid,
+					      NO_CREATE,
+					      &ql->ql_data);
+			found_gid = TRUE;
+		} else {
+			error = gfs_quota_get(sdp, TRUE, uid,
+					      NO_CREATE,
+					      &ql->ql_data);
+			found_uid = TRUE;
+		}
+
+		gfs_assert(sdp, !error && ql->ql_data,);
+
+		ql->ql_change = change;
+
+		spin_lock(&sdp->sd_quota_lock);
+		ql->ql_data->qd_change_new += change;
+		spin_unlock(&sdp->sd_quota_lock);
+
+		LO_ADD(sdp, &ql->ql_le);
+		tr->tr_num_q++;
+	}
+}
diff -pruN linux-2.6.9.orig/fs/gfs/trans.h linux-2.6.9.debug/fs/gfs/trans.h
--- linux-2.6.9.orig/fs/gfs/trans.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/trans.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,37 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __TRANS_DOT_H__
+#define __TRANS_DOT_H__
+
+#define TRANS_IS_NEW            (53)
+#define TRANS_IS_INCORE         (54)
+void gfs_trans_print(struct gfs_sbd *sdp, struct gfs_trans *tr,
+		     unsigned int where);
+
+int gfs_trans_begin_i(struct gfs_sbd *sdp,
+		      unsigned int meta_blocks, unsigned int extra_blocks,
+		      char *file, unsigned int line);
+#define gfs_trans_begin(sdp, mb, eb) \
+gfs_trans_begin_i((sdp), (mb), (eb), __FILE__, __LINE__)
+
+void gfs_trans_end(struct gfs_sbd *sdp);
+
+void gfs_trans_add_gl(struct gfs_glock *gl);
+void gfs_trans_add_bh(struct gfs_glock *gl, struct buffer_head *bh);
+struct gfs_unlinked *gfs_trans_add_unlinked(struct gfs_sbd *sdp, unsigned int type,
+					    struct gfs_inum *inum);
+void gfs_trans_add_quota(struct gfs_sbd *sdp, int64_t change, uint32_t uid,
+			 uint32_t gid);
+
+#endif /* __TRANS_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/unlinked.c linux-2.6.9.debug/fs/gfs/unlinked.c
--- linux-2.6.9.orig/fs/gfs/unlinked.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/unlinked.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,444 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+
+#include "gfs.h"
+#include "inode.h"
+#include "log.h"
+#include "lops.h"
+#include "unlinked.h"
+
+/**
+ * gfs_unlinked_get - Get a structure to represent an unlinked inode
+ * @sdp: the filesystem
+ * @inum: identifies the inode that's unlinked
+ * @create: if TRUE, we're allowed to create the structure if we can't find it,
+ *      otherwise return NULL
+ *
+ * Returns: the structure, or NULL
+ *
+ * Search the filesystem's list of gfs_unlinked to find a match.
+ * If none found, create a new one and place on list.
+ */
+
+struct gfs_unlinked *
+gfs_unlinked_get(struct gfs_sbd *sdp, struct gfs_inum *inum, int create)
+{
+	struct gfs_unlinked *ul = NULL, *new_ul = NULL;
+	struct list_head *tmp, *head;
+
+	for (;;) {
+		spin_lock(&sdp->sd_unlinked_lock);
+
+		for (head = &sdp->sd_unlinked_list, tmp = head->next;
+		     tmp != head;
+		     tmp = tmp->next) {
+			ul = list_entry(tmp, struct gfs_unlinked, ul_list);
+			if (gfs_inum_equal(&ul->ul_inum, inum)) {
+				ul->ul_count++;
+				break;
+			}
+		}
+
+		if (tmp == head)
+			ul = NULL;
+
+		/* 2nd pass, still not there; add the new_ul we prepared */
+		if (!ul && new_ul) {
+			ul = new_ul;
+			list_add(&ul->ul_list, &sdp->sd_unlinked_list);
+			new_ul = NULL;
+		}
+
+		spin_unlock(&sdp->sd_unlinked_lock);
+
+		/* 1st pass; we found pre-existing, OR not allowed to create.
+		   2nd pass; another process added it, or we did */
+		if (ul || !create) {
+			if (new_ul)
+				/* someone beat us to it; forget our new_ul */
+				kfree(new_ul);
+			return ul;
+		}
+
+		/* No match on list, 1st time through loop.
+		   Prepare new_ul, then repeat loop to find out if another
+		   process has created or unlinked an inode and put its
+		   gfs_unlinked on list while we've been preparing this one. */
+		new_ul = gmalloc(sizeof(struct gfs_unlinked));
+		memset(new_ul, 0, sizeof(struct gfs_unlinked));
+
+		new_ul->ul_count = 1;
+		new_ul->ul_inum = *inum;
+
+		INIT_LE(&new_ul->ul_new_le, &gfs_unlinked_lops);
+		INIT_LE(&new_ul->ul_incore_le, &gfs_unlinked_lops);
+		INIT_LE(&new_ul->ul_ondisk_le, &gfs_unlinked_lops);
+	}
+}
+
+/**
+ * gfs_unlinked_hold - increment the usage count on a struct gfs_unlinked
+ * @sdp: the filesystem
+ * @ul: the structure
+ *
+ */
+
+void
+gfs_unlinked_hold(struct gfs_sbd *sdp, struct gfs_unlinked *ul)
+{
+	spin_lock(&sdp->sd_unlinked_lock);
+	gfs_assert(sdp, ul->ul_count,);
+	ul->ul_count++;
+	spin_unlock(&sdp->sd_unlinked_lock);
+}
+
+/**
+ * gfs_unlinked_put - decrement the usage count on a struct gfs_unlinked
+ * @sdp: the filesystem
+ * @ul: the structure
+ *
+ * Free the structure if its reference count hits zero.
+ *
+ */
+
+void
+gfs_unlinked_put(struct gfs_sbd *sdp, struct gfs_unlinked *ul)
+{
+	spin_lock(&sdp->sd_unlinked_lock);
+
+	gfs_assert(sdp, ul->ul_count,);
+	ul->ul_count--;
+
+	if (!ul->ul_count) {
+		gfs_assert_warn(sdp,
+				!test_bit(ULF_IC_LIST, &ul->ul_flags) &&
+				!test_bit(ULF_OD_LIST, &ul->ul_flags) &&
+				!test_bit(ULF_LOCK, &ul->ul_flags));
+		list_del(&ul->ul_list);
+		spin_unlock(&sdp->sd_unlinked_lock);
+		kfree(ul);
+	} else
+		spin_unlock(&sdp->sd_unlinked_lock);
+}
+
+/**
+ * unlinked_find - Find a inode to try to deallocate
+ * @sdp: the filesystem
+ *
+ * The returned structure is locked and needs to be unlocked
+ * with gfs_unlinked_unlock().
+ *
+ * Returns: A unlinked structure, or NULL
+ */
+
+struct gfs_unlinked *
+unlinked_find(struct gfs_sbd *sdp)
+{
+	struct list_head *tmp, *head;
+	struct gfs_unlinked *ul = NULL;
+
+	if (test_bit(SDF_ROFS, &sdp->sd_flags))
+		return NULL;
+
+	gfs_log_lock(sdp);
+	spin_lock(&sdp->sd_unlinked_lock);
+
+	if (!atomic_read(&sdp->sd_unlinked_ic_count))
+		goto out;
+
+	for (head = &sdp->sd_unlinked_list, tmp = head->next;
+	     tmp != head;
+	     tmp = tmp->next) {
+		ul = list_entry(tmp, struct gfs_unlinked, ul_list);
+
+		if (test_bit(ULF_LOCK, &ul->ul_flags))
+			continue;
+		if (!test_bit(ULF_IC_LIST, &ul->ul_flags))
+			continue;
+
+		list_move_tail(&ul->ul_list, &sdp->sd_unlinked_list);
+
+		set_bit(ULF_LOCK, &ul->ul_flags);
+		ul->ul_count++;
+
+		goto out;
+	}
+
+	ul = NULL;
+
+ out:
+	spin_unlock(&sdp->sd_unlinked_lock);
+	gfs_log_unlock(sdp);
+
+	return ul;
+}
+
+/**
+ * gfs_unlinked_lock - lock a unlinked structure
+ * @sdp: the filesystem
+ * @ul: the unlinked inode structure
+ *
+ */
+
+void
+gfs_unlinked_lock(struct gfs_sbd *sdp, struct gfs_unlinked *ul)
+{
+	spin_lock(&sdp->sd_unlinked_lock);
+
+	gfs_assert_warn(sdp, !test_bit(ULF_LOCK, &ul->ul_flags));
+	set_bit(ULF_LOCK, &ul->ul_flags);
+
+	ul->ul_count++;
+
+	spin_unlock(&sdp->sd_unlinked_lock);	
+}
+
+/**
+ * gfs_unlinked_unlock - drop a reference on a unlinked structure
+ * @sdp: the filesystem
+ * @ul: the unlinked inode structure
+ *
+ */
+
+void
+gfs_unlinked_unlock(struct gfs_sbd *sdp, struct gfs_unlinked *ul)
+{
+	spin_lock(&sdp->sd_unlinked_lock);
+
+	gfs_assert_warn(sdp, test_bit(ULF_LOCK, &ul->ul_flags));
+	clear_bit(ULF_LOCK, &ul->ul_flags);
+
+	gfs_assert(sdp, ul->ul_count,);
+	ul->ul_count--;
+
+	if (!ul->ul_count) {
+		gfs_assert_warn(sdp, !test_bit(ULF_IC_LIST, &ul->ul_flags) &&
+				!test_bit(ULF_OD_LIST, &ul->ul_flags));
+		list_del(&ul->ul_list);
+		spin_unlock(&sdp->sd_unlinked_lock);
+		kfree(ul);
+	} else
+		spin_unlock(&sdp->sd_unlinked_lock);
+}
+
+/**
+ * gfs_unlinked_merge - add/remove a unlinked inode from the in-memory list
+ * @sdp: the filesystem
+ * @type: is this a unlink tag or a dealloc tag
+ * @inum: the inode number
+ *
+ * Called during journal recovery.
+ */
+
+void
+gfs_unlinked_merge(struct gfs_sbd *sdp, unsigned int type,
+		   struct gfs_inum *inum)
+{
+	struct gfs_unlinked *ul;
+
+	gfs_assert(sdp, atomic_read(&sdp->sd_unlinked_ic_count) ==
+		   atomic_read(&sdp->sd_unlinked_od_count),);
+
+	ul = gfs_unlinked_get(sdp, inum, CREATE);
+
+	gfs_log_lock(sdp);
+
+	switch (type) {
+	case GFS_LOG_DESC_IUL:
+		gfs_unlinked_hold(sdp, ul);
+		gfs_unlinked_hold(sdp, ul);
+		gfs_assert(sdp, !test_bit(ULF_IC_LIST, &ul->ul_flags) &&
+			   !test_bit(ULF_OD_LIST, &ul->ul_flags),);
+		set_bit(ULF_IC_LIST, &ul->ul_flags);
+		set_bit(ULF_OD_LIST, &ul->ul_flags);
+		atomic_inc(&sdp->sd_unlinked_ic_count);
+		atomic_inc(&sdp->sd_unlinked_od_count);
+
+		break;
+
+	case GFS_LOG_DESC_IDA:
+		gfs_assert(sdp, test_bit(ULF_IC_LIST, &ul->ul_flags) &&
+			   test_bit(ULF_OD_LIST, &ul->ul_flags),);
+		clear_bit(ULF_IC_LIST, &ul->ul_flags);
+		clear_bit(ULF_OD_LIST, &ul->ul_flags);
+		gfs_unlinked_put(sdp, ul);
+		gfs_unlinked_put(sdp, ul);
+		gfs_assert(sdp, atomic_read(&sdp->sd_unlinked_ic_count) > 0,);
+		atomic_dec(&sdp->sd_unlinked_ic_count);
+		gfs_assert(sdp, atomic_read(&sdp->sd_unlinked_od_count) > 0,);
+		atomic_dec(&sdp->sd_unlinked_od_count);
+
+		break;
+	}
+
+	gfs_log_unlock(sdp);
+
+	gfs_unlinked_put(sdp, ul);
+}
+
+/**
+ * gfs_unlinked_cleanup - get rid of any extra struct gfs_unlinked structures
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_unlinked_cleanup(struct gfs_sbd *sdp)
+{
+	struct gfs_unlinked *ul;
+
+ restart:
+	gfs_log_lock(sdp);
+
+	gfs_assert(sdp, atomic_read(&sdp->sd_unlinked_ic_count) ==
+		   atomic_read(&sdp->sd_unlinked_od_count),);
+
+	spin_lock(&sdp->sd_unlinked_lock);
+
+	while (!list_empty(&sdp->sd_unlinked_list)) {
+		ul = list_entry(sdp->sd_unlinked_list.next,
+				struct gfs_unlinked, ul_list);
+
+		if (ul->ul_count > 2) {
+			spin_unlock(&sdp->sd_unlinked_lock);
+			gfs_log_unlock(sdp);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ);
+			goto restart;
+		}
+		gfs_assert(sdp, ul->ul_count == 2,);
+
+		gfs_assert_warn(sdp,
+				test_bit(ULF_IC_LIST, &ul->ul_flags) &&
+				test_bit(ULF_OD_LIST, &ul->ul_flags) &&
+				!test_bit(ULF_LOCK, &ul->ul_flags));
+
+		list_del(&ul->ul_list);
+
+		atomic_dec(&sdp->sd_unlinked_ic_count);
+		atomic_dec(&sdp->sd_unlinked_od_count);
+
+		spin_unlock(&sdp->sd_unlinked_lock);
+		kfree(ul);
+		spin_lock(&sdp->sd_unlinked_lock);
+	}
+
+	spin_unlock(&sdp->sd_unlinked_lock);
+
+	gfs_assert(sdp, !atomic_read(&sdp->sd_unlinked_ic_count) &&
+		   !atomic_read(&sdp->sd_unlinked_od_count),);
+
+	gfs_log_unlock(sdp);
+}
+
+/**
+ * gfs_unlinked_limit - limit the number of inodes waiting to be deallocated
+ * @sdp: the filesystem
+ *
+ * Returns: errno
+ */
+
+void
+gfs_unlinked_limit(struct gfs_sbd *sdp)
+{
+	unsigned int tries = 0, min = 0;
+	int error;
+
+	if (atomic_read(&sdp->sd_unlinked_ic_count) >=
+	    gfs_tune_get(sdp, gt_ilimit2)) {
+		tries = gfs_tune_get(sdp, gt_ilimit2_tries);
+		min = gfs_tune_get(sdp, gt_ilimit2_min);
+	} else if (atomic_read(&sdp->sd_unlinked_ic_count) >=
+		   gfs_tune_get(sdp, gt_ilimit1)) {
+		tries = gfs_tune_get(sdp, gt_ilimit1_tries);
+		min = gfs_tune_get(sdp, gt_ilimit1_min);
+	}
+
+	while (tries--) {
+		struct gfs_unlinked *ul = unlinked_find(sdp);
+		if (!ul)
+			break;
+
+		error = gfs_inode_dealloc(sdp, &ul->ul_inum);
+
+		gfs_unlinked_unlock(sdp, ul);
+
+		if (!error) {
+			if (!--min)
+				break;
+		} else if (error != 1)
+			break;
+	}
+}
+
+/**
+ * gfs_unlinked_dealloc - Go through the list of inodes to be deallocated
+ * @sdp: the filesystem
+ *
+ * Returns: errno
+ */
+
+void
+gfs_unlinked_dealloc(struct gfs_sbd *sdp)
+{
+	unsigned int hits, strikes;
+	int error;
+
+	for (;;) {
+		hits = 0;
+		strikes = 0;
+
+		for (;;) {
+			struct gfs_unlinked *ul = unlinked_find(sdp);
+			if (!ul)
+				return;
+
+			error = gfs_inode_dealloc(sdp, &ul->ul_inum);
+
+			gfs_unlinked_unlock(sdp, ul);
+
+			if (!error) {
+				hits++;
+				if (strikes)
+					strikes--;
+			} else if (error == 1) {
+				strikes++;
+				if (strikes >= atomic_read(&sdp->sd_unlinked_ic_count)) {
+					error = 0;
+					break;
+				}
+			} else
+				goto out;
+		}
+
+		if (!hits || !test_bit(SDF_INODED_RUN, &sdp->sd_flags))
+			break;
+
+		cond_resched();
+	}
+
+ out:
+	if (error &&
+	    error != -EROFS &&
+	    !test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
+		printk("GFS: fsid=%s: error deallocating inodes: %d\n",
+		       sdp->sd_fsname, error);
+}
diff -pruN linux-2.6.9.orig/fs/gfs/unlinked.h linux-2.6.9.debug/fs/gfs/unlinked.h
--- linux-2.6.9.orig/fs/gfs/unlinked.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/unlinked.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,32 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __UNLINKED_DOT_H__
+#define __UNLINKED_DOT_H__
+
+struct gfs_unlinked *gfs_unlinked_get(struct gfs_sbd *sdp,
+				      struct gfs_inum *inum, int create);
+void gfs_unlinked_hold(struct gfs_sbd *sdp, struct gfs_unlinked *ul);
+void gfs_unlinked_put(struct gfs_sbd *sdp, struct gfs_unlinked *ul);
+
+void gfs_unlinked_lock(struct gfs_sbd *sdp, struct gfs_unlinked *ul);
+void gfs_unlinked_unlock(struct gfs_sbd *sdp, struct gfs_unlinked *ul);
+
+void gfs_unlinked_merge(struct gfs_sbd *sdp, unsigned int type,
+			struct gfs_inum *inum);
+void gfs_unlinked_cleanup(struct gfs_sbd *sdp);
+
+void gfs_unlinked_limit(struct gfs_sbd *sdp);
+void gfs_unlinked_dealloc(struct gfs_sbd *sdp);
+
+#endif /* __UNLINKED_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs/util.c linux-2.6.9.debug/fs/gfs/util.c
--- linux-2.6.9.orig/fs/gfs/util.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/util.c	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,584 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/uaccess.h>
+
+#include "gfs.h"
+#include "glock.h"
+#include "lm.h"
+
+uint32_t gfs_random_number;
+
+kmem_cache_t *gfs_glock_cachep = NULL;
+kmem_cache_t *gfs_inode_cachep = NULL;
+kmem_cache_t *gfs_bufdata_cachep = NULL;
+kmem_cache_t *gfs_mhc_cachep = NULL;
+
+/**
+ * gfs_random - Generate a random 32-bit number
+ *
+ * Generate a semi-crappy 32-bit pseudo-random number without using
+ * floating point.
+ *
+ * The PRNG is from "Numerical Recipes in C" (second edition), page 284.
+ *
+ * Returns: a 32-bit random number
+ */
+
+uint32_t
+gfs_random(void)
+{
+	gfs_random_number = 0x0019660D * gfs_random_number + 0x3C6EF35F;
+	return gfs_random_number;
+}
+
+/**
+ * hash_more_internal - hash an array of data
+ * @data: the data to be hashed
+ * @len: the length of data to be hashed
+ * @hash: the hash from a previous call
+ *
+ * Take some data and convert it to a 32-bit hash.
+ *
+ * This is the 32-bit FNV-1a hash from:
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * Hash guts
+ *
+ * Returns: the hash
+ */
+
+static __inline__ uint32_t
+hash_more_internal(const void *data, unsigned int len, uint32_t hash)
+{
+	unsigned char *p = (unsigned char *)data;
+	unsigned char *e = p + len;
+	uint32_t h = hash;
+
+	while (p < e) {
+		h ^= (uint32_t)(*p++);
+		h *= 0x01000193;
+	}
+
+	return h;
+}
+
+/**
+ * gfs_hash - hash an array of data
+ * @data: the data to be hashed
+ * @len: the length of data to be hashed
+ *
+ * Take some data and convert it to a 32-bit hash.
+ *
+ * This is the 32-bit FNV-1a hash from:
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * Returns: the hash
+ */
+
+uint32_t
+gfs_hash(const void *data, unsigned int len)
+{
+	uint32_t h = 0x811C9DC5;
+	h = hash_more_internal(data, len, h);
+	return h;
+}
+
+/**
+ * gfs_hash_more - hash an array of data
+ * @data: the data to be hashed
+ * @len: the length of data to be hashed
+ * @hash: the hash from a previous call
+ *
+ * Take some data and convert it to a 32-bit hash.
+ *
+ * This is the 32-bit FNV-1a hash from:
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * This version let's you hash together discontinuous regions.
+ * For example, to compute the combined hash of the memory in
+ * (data1, len1), (data2, len2), and (data3, len3) you:
+ *
+ *   h = gfs_hash(data1, len1);
+ *   h = gfs_hash_more(data2, len2, h);
+ *   h = gfs_hash_more(data3, len3, h);
+ *
+ * Returns: the hash
+ */
+
+uint32_t
+gfs_hash_more(const void *data, unsigned int len, uint32_t hash)
+{
+	uint32_t h;
+	h = hash_more_internal(data, len, hash);
+	return h;
+}
+
+/* Byte-wise swap two items of size SIZE. */
+
+#define SWAP(a, b, size) \
+do { \
+	register size_t __size = (size); \
+        register char *__a = (a), *__b = (b); \
+        do { \
+		char __tmp = *__a; \
+		*__a++ = *__b; \
+		*__b++ = __tmp; \
+	} while (__size-- > 1); \
+} while (0)
+
+/**
+ * gfs_sort - Sort base array using shell sort algorithm
+ * @base: the input array
+ * @num_elem: number of elements in array
+ * @size: size of each element in array
+ * @compar: fxn to compare array elements (returns negative
+ *          for lt, 0 for eq, and positive for gt
+ *
+ * Sorts the array passed in using the compar fxn to compare elements using
+ * the shell sort algorithm
+ */
+
+void
+gfs_sort(void *base, unsigned int num_elem, unsigned int size,
+	 int (*compar) (const void *, const void *))
+{
+	register char *pbase = (char *)base;
+	int i, j, k, h;
+	static int cols[16] = {1391376, 463792, 198768, 86961,
+			       33936, 13776, 4592, 1968,
+			       861, 336, 112, 48,
+			       21, 7, 3, 1};
+	
+	for (k = 0; k < 16; k++) {
+		h = cols[k];
+		for (i = h; i < num_elem; i++) {
+			j = i;
+			while (j >= h &&
+			       (*compar)((void *)(pbase + size * (j - h)),
+					 (void *)(pbase + size * j)) > 0) {
+				SWAP(pbase + size * j,
+				     pbase + size * (j - h),
+				     size);
+				j = j - h;
+			}
+		}
+	}
+}
+
+/**
+ * gfs_assert_i - Cause the machine to panic if @assertion is false
+ * @sdp:
+ * @assertion:
+ * @function:
+ * @file:
+ * @line:
+ *
+ */
+
+void
+gfs_assert_i(struct gfs_sbd *sdp,
+	     char *assertion,
+	     const char *function,
+	     char *file, unsigned int line)
+{
+	if (sdp->sd_args.ar_oopses_ok) {
+		printk("GFS: fsid=%s: assertion \"%s\" failed\n"
+		       "GFS: fsid=%s:   function = %s\n"
+		       "GFS: fsid=%s:   file = %s, line = %u\n"
+		       "GFS: fsid=%s:   time = %lu\n",
+		       sdp->sd_fsname, assertion,
+		       sdp->sd_fsname, function,
+		       sdp->sd_fsname, file, line,
+		       sdp->sd_fsname, get_seconds());
+		BUG();
+	}
+	dump_stack();
+	panic("GFS: fsid=%s: assertion \"%s\" failed\n"
+	      "GFS: fsid=%s:   function = %s\n"
+	      "GFS: fsid=%s:   file = %s, line = %u\n"
+	      "GFS: fsid=%s:   time = %lu\n",
+	      sdp->sd_fsname, assertion,
+	      sdp->sd_fsname, function,
+	      sdp->sd_fsname, file, line,
+	      sdp->sd_fsname, get_seconds());
+}
+
+/**
+ * gfs_assert_withdraw_i - Cause the machine to withdraw if @assertion is false
+ * @sdp:
+ * @assertion:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          -2 if it was already withdrawn
+ */
+
+int
+gfs_assert_withdraw_i(struct gfs_sbd *sdp,
+		      char *assertion,
+		      const char *function,
+		      char *file, unsigned int line)
+{
+	int me;
+	me = gfs_lm_withdraw(sdp,
+			     "GFS: fsid=%s: fatal: assertion \"%s\" failed\n"
+			     "GFS: fsid=%s:   function = %s\n"
+			     "GFS: fsid=%s:   file = %s, line = %u\n"
+			     "GFS: fsid=%s:   time = %lu\n",
+			     sdp->sd_fsname, assertion,
+			     sdp->sd_fsname, function,
+			     sdp->sd_fsname, file, line,
+			     sdp->sd_fsname, get_seconds());
+	return (me) ? -1 : -2;
+}
+
+/**
+ * gfs_assert_warn_i - Print a message to the console if @assertion is false
+ * @sdp:
+ * @assertion:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if we printed something
+ *          -2 if we didn't
+ */
+
+int
+gfs_assert_warn_i(struct gfs_sbd *sdp,
+		  char *assertion,
+		  const char *function,
+		  char *file, unsigned int line)
+{
+	if (time_before(jiffies,
+			sdp->sd_last_warning +
+			gfs_tune_get(sdp, gt_complain_secs) * HZ))
+		return -2;
+
+	printk("GFS: fsid=%s: warning: assertion \"%s\" failed\n"
+	       "GFS: fsid=%s:   function = %s\n"
+	       "GFS: fsid=%s:   file = %s, line = %u\n"
+	       "GFS: fsid=%s:   time = %lu\n",
+	       sdp->sd_fsname, assertion,
+	       sdp->sd_fsname, function,
+	       sdp->sd_fsname, file, line,
+	       sdp->sd_fsname, get_seconds());
+
+	sdp->sd_last_warning = jiffies;
+	if (sdp->sd_args.ar_debug)
+		BUG();
+
+
+	return -1;
+}
+
+/**
+ * gfs_consist_i - Flag a filesystem consistency error and withdraw
+ * @sdp:
+ * @cluster_wide:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int
+gfs_consist_i(struct gfs_sbd *sdp, int cluster_wide,
+	      const char *function,
+	      char *file, unsigned int line)
+{
+	return gfs_lm_withdraw(sdp,
+			       "GFS: fsid=%s: fatal: filesystem consistency error\n"
+			       "GFS: fsid=%s:   function = %s\n"
+			       "GFS: fsid=%s:   file = %s, line = %u\n"
+			       "GFS: fsid=%s:   time = %lu\n",
+			       sdp->sd_fsname,
+			       sdp->sd_fsname, function,
+			       sdp->sd_fsname, file, line,
+			       sdp->sd_fsname, get_seconds());
+}
+
+/**
+ * gfs_consist_inode_i - Flag an inode consistency error and withdraw
+ * @ip:
+ * @cluster_wide:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int
+gfs_consist_inode_i(struct gfs_inode *ip, int cluster_wide,
+		    const char *function,
+		    char *file, unsigned int line)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+        return gfs_lm_withdraw(sdp,
+			       "GFS: fsid=%s: fatal: filesystem consistency error\n"
+			       "GFS: fsid=%s:   inode = %"PRIu64"/%"PRIu64"\n"
+			       "GFS: fsid=%s:   function = %s\n"
+			       "GFS: fsid=%s:   file = %s, line = %u\n"
+			       "GFS: fsid=%s:   time = %lu\n",
+			       sdp->sd_fsname,
+			       sdp->sd_fsname, ip->i_num.no_formal_ino, ip->i_num.no_addr,
+			       sdp->sd_fsname, function,
+			       sdp->sd_fsname, file, line,
+			       sdp->sd_fsname, get_seconds());
+}
+
+/**
+ * gfs_consist_rgrpd_i - Flag a RG consistency error and withdraw
+ * @rgd:
+ * @cluster_wide:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int
+gfs_consist_rgrpd_i(struct gfs_rgrpd *rgd, int cluster_wide,
+		    const char *function,
+		    char *file, unsigned int line)
+{
+        struct gfs_sbd *sdp = rgd->rd_sbd;
+        return gfs_lm_withdraw(sdp,
+			       "GFS: fsid=%s: fatal: filesystem consistency error\n"
+			       "GFS: fsid=%s:   RG = %"PRIu64"\n"
+			       "GFS: fsid=%s:   function = %s\n"
+			       "GFS: fsid=%s:   file = %s, line = %u\n"
+			       "GFS: fsid=%s:   time = %lu\n",
+			       sdp->sd_fsname,
+			       sdp->sd_fsname, rgd->rd_ri.ri_addr,
+			       sdp->sd_fsname, function,
+			       sdp->sd_fsname, file, line,
+			       sdp->sd_fsname, get_seconds());
+}
+
+/**
+ * gfs_meta_check_ii - Flag a magic number consistency error and withdraw
+ * @sdp:
+ * @bh:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          -2 if it was already withdrawn
+ */
+
+int
+gfs_meta_check_ii(struct gfs_sbd *sdp, struct buffer_head *bh,
+                  const char *function,
+                  char *file, unsigned int line)
+{
+	int me;
+        me = gfs_lm_withdraw(sdp,
+			     "GFS: fsid=%s: fatal: invalid metadata block\n"
+			     "GFS: fsid=%s:   bh = %"PRIu64" (magic)\n"
+			     "GFS: fsid=%s:   function = %s\n"
+			     "GFS: fsid=%s:   file = %s, line = %u\n"
+			     "GFS: fsid=%s:   time = %lu\n",
+			     sdp->sd_fsname,
+			     sdp->sd_fsname, (uint64_t)bh->b_blocknr,
+			     sdp->sd_fsname, function,
+			     sdp->sd_fsname, file, line,
+			     sdp->sd_fsname, get_seconds());
+	return (me) ? -1 : -2;
+}
+
+/**
+ * gfs_metatype_check_ii - Flag a metadata type consistency error and withdraw
+ * @sdp:
+ * @bh:
+ * @type:
+ * @t:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          -2 if it was already withdrawn
+ */
+
+int
+gfs_metatype_check_ii(struct gfs_sbd *sdp, struct buffer_head *bh,
+		      uint32_t type, uint32_t t,
+		      const char *function,
+		      char *file, unsigned int line)
+{
+	int me;
+        me = gfs_lm_withdraw(sdp,
+			     "GFS: fsid=%s: fatal: invalid metadata block\n"
+			     "GFS: fsid=%s:   bh = %"PRIu64" (type: exp=%u, found=%u)\n"
+			     "GFS: fsid=%s:   function = %s\n"
+			     "GFS: fsid=%s:   file = %s, line = %u\n"
+			     "GFS: fsid=%s:   time = %lu\n",
+			     sdp->sd_fsname,
+			     sdp->sd_fsname, (uint64_t)bh->b_blocknr, type, t,
+			     sdp->sd_fsname, function,
+			     sdp->sd_fsname, file, line,
+			     sdp->sd_fsname, get_seconds());
+	return (me) ? -1 : -2;
+}
+
+/**
+ * gfs_io_error_i - Flag an I/O error and withdraw
+ * @sdp:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int
+gfs_io_error_i(struct gfs_sbd *sdp,
+	       const char *function,
+	       char *file, unsigned int line)
+{
+        return gfs_lm_withdraw(sdp,
+			       "GFS: fsid=%s: fatal: I/O error\n"
+			       "GFS: fsid=%s:   function = %s\n"
+			       "GFS: fsid=%s:   file = %s, line = %u\n"
+			       "GFS: fsid=%s:   time = %lu\n",
+			       sdp->sd_fsname,
+			       sdp->sd_fsname, function,
+			       sdp->sd_fsname, file, line,
+			       sdp->sd_fsname, get_seconds());
+}
+
+/**
+ * gfs_io_error_inode_i - Flag an inode I/O error and withdraw
+ * @ip:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int
+gfs_io_error_inode_i(struct gfs_inode *ip,
+		     const char *function,
+		     char *file, unsigned int line)
+{
+	struct gfs_sbd *sdp = ip->i_sbd;
+        return gfs_lm_withdraw(sdp,
+			       "GFS: fsid=%s: fatal: I/O error\n"
+			       "GFS: fsid=%s:   inode = %"PRIu64"/%"PRIu64"\n"
+			       "GFS: fsid=%s:   function = %s\n"
+			       "GFS: fsid=%s:   file = %s, line = %u\n"
+			       "GFS: fsid=%s:   time = %lu\n",
+			       sdp->sd_fsname,
+			       sdp->sd_fsname, ip->i_num.no_formal_ino, ip->i_num.no_addr,
+			       sdp->sd_fsname, function,
+			       sdp->sd_fsname, file, line,
+			       sdp->sd_fsname, get_seconds());
+}
+
+/**
+ * gfs_io_error_bh_i - Flag a buffer I/O error and withdraw
+ * @sdp:
+ * @bh:
+ * @function:
+ * @file:
+ * @line:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int
+gfs_io_error_bh_i(struct gfs_sbd *sdp, struct buffer_head *bh,
+		  const char *function,
+		  char *file, unsigned int line)
+{
+        return gfs_lm_withdraw(sdp,
+			       "GFS: fsid=%s: fatal: I/O error\n"
+			       "GFS: fsid=%s:   block = %"PRIu64"\n"
+			       "GFS: fsid=%s:   function = %s\n"
+			       "GFS: fsid=%s:   file = %s, line = %u\n"
+			       "GFS: fsid=%s:   time = %lu\n",
+			       sdp->sd_fsname,
+			       sdp->sd_fsname, (uint64_t)bh->b_blocknr,
+			       sdp->sd_fsname, function,
+			       sdp->sd_fsname, file, line,
+			       sdp->sd_fsname, get_seconds());
+}
+
+/**
+ * gmalloc - malloc a small amount of memory
+ * @size: the number of bytes to malloc
+ *
+ * Returns: the memory
+ */
+
+void *
+gmalloc(unsigned int size)
+{
+	void *p;
+	RETRY_MALLOC(p = kmalloc(size, GFP_KERNEL), p);
+	return p;
+}
+
+/**
+ * gfs_add_bh_to_ub - copy a buffer up to user space
+ * @ub: the structure representing where to copy
+ * @bh: the buffer
+ *
+ * Returns: errno
+ */
+
+int
+gfs_add_bh_to_ub(struct gfs_user_buffer *ub, struct buffer_head *bh)
+{
+	uint64_t blkno = bh->b_blocknr;
+
+	if (ub->ub_count + sizeof(uint64_t) + bh->b_size > ub->ub_size)
+		return -ENOMEM;
+
+	if (copy_to_user(ub->ub_data + ub->ub_count,
+			  &blkno,
+			  sizeof(uint64_t)))
+		return -EFAULT;
+	ub->ub_count += sizeof(uint64_t);
+
+	if (copy_to_user(ub->ub_data + ub->ub_count,
+			  bh->b_data,
+			  bh->b_size))
+		return -EFAULT;
+	ub->ub_count += bh->b_size;
+
+	return 0;
+}
+
diff -pruN linux-2.6.9.orig/fs/gfs/util.h linux-2.6.9.debug/fs/gfs/util.h
--- linux-2.6.9.orig/fs/gfs/util.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs/util.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,343 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __UTIL_DOT_H__
+#define __UTIL_DOT_H__
+
+
+/* Utility functions */
+
+extern uint32_t gfs_random_number;
+uint32_t gfs_random(void);
+
+uint32_t gfs_hash(const void *data, unsigned int len);
+uint32_t gfs_hash_more(const void *data, unsigned int len, uint32_t hash);
+
+void gfs_sort(void *base, unsigned int num_elem, unsigned int size,
+	      int (*compar) (const void *, const void *));
+
+
+/* Error handling */
+
+/**
+ * gfs_assert - Cause the machine to panic if @assertion is false
+ * @sdp:
+ * @assertion:
+ * @todo:
+ *
+ */
+
+void gfs_assert_i(struct gfs_sbd *sdp,
+                  char *assertion,
+                  const char *function,
+                  char *file, unsigned int line)
+__attribute__ ((noreturn));
+#define gfs_assert(sdp, assertion, todo) \
+do { \
+	if (unlikely(!(assertion))) { \
+		{todo} \
+		gfs_assert_i((sdp), #assertion, \
+			     __FUNCTION__, __FILE__, __LINE__); \
+	} \
+} while (0)
+
+/**
+ * gfs_assert_withdraw - Cause the machine to withdraw if @assertion is false
+ * @sdp:
+ * @assertion:
+ *
+ * Returns: 0 if things are ok,
+ *          -1 if this call withdrew the machine,
+ *          -2 if it was already withdrawn
+ */
+
+int gfs_assert_withdraw_i(struct gfs_sbd *sdp,
+			  char *assertion,
+			  const char *function,
+			  char *file, unsigned int line);
+#define gfs_assert_withdraw(sdp, assertion) \
+((likely(assertion)) ? 0 : \
+ gfs_assert_withdraw_i((sdp), #assertion, \
+		       __FUNCTION__, __FILE__, __LINE__))
+
+/**
+ * gfs_assert_warn - Print a message to the console if @assertion is false
+ * @sdp:
+ * @assertion:
+ *
+ * Returns: 0 if things are ok,
+ *          -1 if we printed something
+ *          -2 if we didn't
+ */
+
+int gfs_assert_warn_i(struct gfs_sbd *sdp,
+		      char *assertion,
+		      const char *function,
+		      char *file, unsigned int line);
+#define gfs_assert_warn(sdp, assertion) \
+((likely(assertion)) ? 0 : \
+ gfs_assert_warn_i((sdp), #assertion, \
+		   __FUNCTION__, __FILE__, __LINE__))
+
+/**
+ * gfs_consist - Flag a filesystem consistency error and withdraw
+ * gfs_cconsist - Flag a filesystem consistency error and withdraw cluster
+ * @sdp:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int gfs_consist_i(struct gfs_sbd *sdp, int cluster_wide,
+		  const char *function,
+		  char *file, unsigned int line);
+#define gfs_consist(sdp)\
+gfs_consist_i((sdp), FALSE, __FUNCTION__, __FILE__, __LINE__)
+#define gfs_cconsist(sdp)\
+gfs_consist_i((sdp), TRUE, __FUNCTION__, __FILE__, __LINE__)
+
+/**
+ * gfs_consist_inode - Flag an inode consistency error and withdraw
+ * gfs_cconsist_inode - Flag an inode consistency error and withdraw cluster
+ * @ip:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int gfs_consist_inode_i(struct gfs_inode *ip, int cluster_wide,
+			const char *function,
+			char *file, unsigned int line);
+#define gfs_consist_inode(ip) \
+gfs_consist_inode_i((ip), FALSE, __FUNCTION__, __FILE__, __LINE__)
+#define gfs_cconsist_inode(ip) \
+gfs_consist_inode_i((ip), TRUE, __FUNCTION__, __FILE__, __LINE__)
+
+/**
+ * gfs_consist_rgrpd - Flag a RG consistency error and withdraw
+ * gfs_cconsist_rgrpd - Flag a RG consistency error and withdraw cluster
+ * @rgd:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int gfs_consist_rgrpd_i(struct gfs_rgrpd *rgd, int cluster_wide,
+			const char *function,
+			char *file, unsigned int line);
+#define gfs_consist_rgrpd(rgd) \
+gfs_consist_rgrpd_i((rgd), FALSE, __FUNCTION__, __FILE__, __LINE__)
+#define gfs_cconsist_rgrpd(rgd) \
+gfs_consist_rgrpd_i((rgd), TRUE, __FUNCTION__, __FILE__, __LINE__)
+
+/**
+ * gfs_meta_check - Flag a magic number consistency error and withdraw
+ * @sdp:
+ * @bh:
+ *
+ * Returns: 0 if things are ok,
+ *          -1 if this call withdrew the machine,
+ *          -2 if it was already withdrawn
+ */
+
+int gfs_meta_check_ii(struct gfs_sbd *sdp, struct buffer_head *bh,
+		      const char *function,
+		      char *file, unsigned int line);
+static __inline__ int
+gfs_meta_check_i(struct gfs_sbd *sdp, struct buffer_head *bh,
+		 const char *function,
+		 char *file, unsigned int line)
+{
+	uint32_t magic;
+	magic = ((struct gfs_meta_header *)(bh)->b_data)->mh_magic;
+	magic = gfs32_to_cpu(magic);
+	if (likely(magic == GFS_MAGIC))
+		return 0;
+	return gfs_meta_check_ii(sdp, bh, function, file, line);
+}
+#define gfs_meta_check(sdp, bh) \
+gfs_meta_check_i((sdp), (bh), \
+		 __FUNCTION__, __FILE__, __LINE__)
+
+/**
+ * gfs_metatype_check - Flag a metadata type consistency error and withdraw
+ * @sdp:
+ * @bh:
+ * @type:
+ *
+ * Returns: 0 if things are ok,
+ *          -1 if this call withdrew the machine,
+ *          -2 if it was already withdrawn
+ */
+
+int gfs_metatype_check_ii(struct gfs_sbd *sdp, struct buffer_head *bh,
+			  uint32_t type, uint32_t t,
+			  const char *function,
+			  char *file, unsigned int line);
+static __inline__ int
+gfs_metatype_check_i(struct gfs_sbd *sdp, struct buffer_head *bh,
+		     uint32_t type,
+		     const char *function,
+		     char *file, unsigned int line)
+{
+        uint32_t magic, t;
+        magic = ((struct gfs_meta_header *)(bh)->b_data)->mh_magic;
+        magic = gfs32_to_cpu(magic);
+	if (unlikely(magic != GFS_MAGIC))
+		return gfs_meta_check_ii(sdp, bh, function, file, line);
+	t = ((struct gfs_meta_header *)(bh)->b_data)->mh_type;
+	t = gfs32_to_cpu(t);
+        if (unlikely(t != type))
+		return gfs_metatype_check_ii(sdp, bh, type, t, function, file, line);
+	return 0;
+}
+#define gfs_metatype_check(sdp, bh, type) \
+gfs_metatype_check_i((sdp), (bh), (type), \
+		     __FUNCTION__, __FILE__, __LINE__)
+
+/**
+ * gfs_metatype_check2 - Flag a metadata type consistency error and withdraw
+ * @sdp:
+ * @bh:
+ * @type1:
+ * @type2:
+ *
+ * Returns: 0 if things are ok,
+ *          -1 if this call withdrew the machine,
+ *          -2 if it was already withdrawn
+ */
+
+static __inline__ int
+gfs_metatype_check2_i(struct gfs_sbd *sdp, struct buffer_head *bh,
+		      uint32_t type1, uint32_t type2,
+		      const char *function,
+		      char *file, unsigned int line)
+{
+        uint32_t magic, t;
+        magic = ((struct gfs_meta_header *)(bh)->b_data)->mh_magic;
+        magic = gfs32_to_cpu(magic);
+        if (unlikely(magic != GFS_MAGIC))
+                return gfs_meta_check_ii(sdp, bh, function, file, line);
+        t = ((struct gfs_meta_header *)(bh)->b_data)->mh_type;
+        t = gfs32_to_cpu(t);
+        if (unlikely(t != type1 && t != type2))
+                return gfs_metatype_check_ii(sdp, bh, type1, t, function, file, line);
+        return 0;
+}
+#define gfs_metatype_check2(sdp, bh, type1, type2) \
+gfs_metatype_check2_i((sdp), (bh), (type1), (type2), \
+                     __FUNCTION__, __FILE__, __LINE__)
+
+/**
+ * gfs_metatype_set - set the metadata type on a buffer
+ * @bh:
+ * @type:
+ * @format:
+ *
+ */
+
+static __inline__ void
+gfs_metatype_set(struct buffer_head *bh, uint32_t type, uint32_t format)
+{
+	struct gfs_meta_header *mh;
+	mh = (struct gfs_meta_header *)bh->b_data;
+	mh->mh_type = cpu_to_gfs32(type);
+	mh->mh_format = cpu_to_gfs32(format);
+}
+
+/**
+ * gfs_io_error - Flag an I/O error and withdraw
+ * @sdp:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int gfs_io_error_i(struct gfs_sbd *sdp,
+		   const char *function,
+		   char *file, unsigned int line);
+#define gfs_io_error(sdp) \
+gfs_io_error_i((sdp), __FUNCTION__, __FILE__, __LINE__);
+
+/**
+ * gfs_io_error_inode - Flag an inode I/O error and withdraw
+ * @ip:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int gfs_io_error_inode_i(struct gfs_inode *ip,
+			 const char *function,
+			 char *file, unsigned int line);
+#define gfs_io_error_inode(ip) \
+gfs_io_error_inode_i((ip), __FUNCTION__, __FILE__, __LINE__);
+
+/**
+ * gfs_io_error_bh - Flag a buffer I/O error and withdraw
+ * @sdp:
+ * @bh:
+ *
+ * Returns: -1 if this call withdrew the machine,
+ *          0 if it was already withdrawn
+ */
+
+int gfs_io_error_bh_i(struct gfs_sbd *sdp, struct buffer_head *bh,
+		      const char *function,
+		      char *file, unsigned int line);
+#define gfs_io_error_bh(sdp, bh) \
+gfs_io_error_bh_i((sdp), (bh), __FUNCTION__, __FILE__, __LINE__);
+
+
+/* Memory stuff */
+
+#define RETRY_MALLOC(do_this, until_this) \
+for (;;) { \
+	{ do_this; } \
+	if (until_this) \
+		break; \
+	printk("GFS: out of memory: %s, %u\n", __FILE__, __LINE__); \
+	dump_stack(); \
+	yield(); \
+}
+
+extern kmem_cache_t *gfs_glock_cachep;
+extern kmem_cache_t *gfs_inode_cachep;
+extern kmem_cache_t *gfs_bufdata_cachep;
+extern kmem_cache_t *gfs_mhc_cachep;
+
+void *gmalloc(unsigned int size);
+
+
+struct gfs_user_buffer {
+	char *ub_data;
+	unsigned int ub_size;
+	unsigned int ub_count;
+};
+int gfs_add_bh_to_ub(struct gfs_user_buffer *ub, struct buffer_head *bh);
+
+
+static __inline__ unsigned int
+gfs_tune_get_i(struct gfs_tune *gt, unsigned int *p)
+{
+	unsigned int x;
+	spin_lock(&gt->gt_spin);
+	x = *p;
+	spin_unlock(&gt->gt_spin);
+	return x;
+}
+#define gfs_tune_get(sdp, field) \
+gfs_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field)
+
+
+#endif /* __UTIL_DOT_H__ */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/Makefile linux-2.6.9.debug/fs/gfs_locking/Makefile
--- linux-2.6.9.orig/fs/gfs_locking/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/Makefile	2006-12-20 17:07:36.000000000 +0300
@@ -0,0 +1,17 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+##
+##  This copyrighted material is made available to anyone wishing to use,
+##  modify, copy, or redistribute it subject to the terms and conditions
+##  of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+obj-$(CONFIG_LOCK_HARNESS)	+= lock_harness/
+obj-$(CONFIG_LOCK_NOLOCK)	+= lock_nolock/
+obj-$(CONFIG_LOCK_DLM)		+= lock_dlm/
+obj-$(CONFIG_LOCK_GULM)		+= lock_gulm/
+
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_dlm/Makefile linux-2.6.9.debug/fs/gfs_locking/lock_dlm/Makefile
--- linux-2.6.9.orig/fs/gfs_locking/lock_dlm/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_dlm/Makefile	2006-12-20 17:07:30.000000000 +0300
@@ -0,0 +1,16 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+##
+##  This copyrighted material is made available to anyone wishing to use,
+##  modify, copy, or redistribute it subject to the terms and conditions
+##  of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+obj-$(CONFIG_LOCK_DLM) += lock_dlm.o
+
+lock_dlm-y	:= main.o group.o lock.o mount.o thread.o plock.o
+
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_dlm/group.c linux-2.6.9.debug/fs/gfs_locking/lock_dlm/group.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_dlm/group.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_dlm/group.c	2006-12-20 17:07:48.000000000 +0300
@@ -0,0 +1,880 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**  
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "lock_dlm.h"
+
+
+struct kcl_service_ops mg_ops;
+
+/*
+ * Get the node struct for a given nodeid.
+ */
+
+static dlm_node_t *find_node_by_nodeid(dlm_t *dlm, uint32_t nodeid)
+{
+	dlm_node_t *node;
+
+	list_for_each_entry(node, &dlm->mg_nodes, list) {
+		if (node->nodeid == nodeid)
+			return node;
+	}
+	return NULL;
+}
+
+/*
+ * Get the node struct for a given journalid.
+ */
+
+static dlm_node_t *find_node_by_jid(dlm_t *dlm, uint32_t jid)
+{
+	dlm_node_t *node;
+
+	list_for_each_entry(node, &dlm->mg_nodes, list) {
+		if (node->jid == jid)
+			return node;
+	}
+	return NULL;
+}
+
+/*
+ * If the given ID is clear, get it, setting to the given VALUE.  The ID is a
+ * journalid, the VALUE is our nodeid.  When successful, the held ID-lock is
+ * returned (in shared mode).  As long as this ID-lock is held, the journalid
+ * is owned.
+ */
+
+static int id_test_and_set(dlm_t *dlm, uint32_t id, uint32_t val,
+			   dlm_lock_t **lp_set)
+{
+	dlm_lock_t *lp = NULL;
+	struct lm_lockname name;
+	lm_lock_t *lock;
+	char *lvb;
+	uint32_t exist_val, beval;
+	int error;
+
+	name.ln_type = LM_TYPE_JID;
+	name.ln_number = id;
+
+	error = lm_dlm_get_lock(dlm, &name, &lock);
+	if (error)
+		goto fail;
+
+	lp = (dlm_lock_t *) lock;
+
+	error = dlm_add_lvb(lp);
+	if (error)
+		goto fail_put;
+
+	lvb = lp->lvb;
+	set_bit(LFL_INLOCK, &lp->flags);
+	set_bit(LFL_NOBAST, &lp->flags);
+
+ retry:
+
+	error = lm_dlm_lock_sync(lock, LM_ST_UNLOCKED, LM_ST_SHARED,
+			         LM_FLAG_TRY | LM_FLAG_NOEXP);
+	if (error == -EAGAIN) {
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(HZ);
+		goto retry;
+	}
+	if (error)
+		goto fail_lvb;
+
+	memcpy(&beval, lvb, sizeof(beval));
+	exist_val = be32_to_cpu(beval);
+
+	if (!exist_val) {
+		/*
+		 * This id is unused.  Attempt to claim it by getting EX mode
+		 * and writing our nodeid into the lvb.
+		 */
+		error = lm_dlm_lock_sync(lock, LM_ST_SHARED, LM_ST_EXCLUSIVE,
+				         LM_FLAG_TRY | LM_FLAG_NOEXP);
+		if (error == -EAGAIN) {
+			lm_dlm_unlock_sync(lock, LM_ST_SHARED);
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(HZ);
+			goto retry;
+		}
+		if (error)
+			goto fail_unlock;
+
+		beval = cpu_to_be32(val);
+		memcpy(lvb, &beval, sizeof(beval));
+
+		error = lm_dlm_lock_sync(lock, LM_ST_EXCLUSIVE, LM_ST_SHARED,
+				         LM_FLAG_NOEXP);
+		DLM_ASSERT(!error,);
+
+		*lp_set = lp;
+		error = 0;
+	} else {
+		/*
+		 * This id is already used. It has a non-zero nodeid in the lvb
+		 */
+		lm_dlm_unlock_sync(lock, LM_ST_SHARED);
+		dlm_del_lvb(lp);
+		lm_dlm_put_lock(lock);
+		error = exist_val;
+	}
+
+	return error;
+
+ fail_unlock:
+	lm_dlm_unlock_sync(lock, LM_ST_SHARED);
+
+ fail_lvb:
+	dlm_del_lvb(lp);
+
+ fail_put:
+	lm_dlm_put_lock(lock);
+
+ fail:
+	return error;
+}
+
+/*
+ * Release a held ID-lock clearing its VALUE.  We have to acquire the lock in
+ * EX again so we can write out a zeroed lvb.
+ */
+
+static void id_clear(dlm_t *dlm, dlm_lock_t *lp)
+{
+	lm_lock_t *lock = (lm_lock_t *) lp;
+	int error;
+
+	/*
+	 * This flag means that DLM_LKF_CONVDEADLK should not be used.
+	 */
+	set_bit(LFL_FORCE_PROMOTE, &lp->flags);
+
+ retry:
+
+	error = lm_dlm_lock_sync(lock, LM_ST_SHARED, LM_ST_EXCLUSIVE,
+			         LM_FLAG_TRY | LM_FLAG_NOEXP);
+	if (error == -EAGAIN) {
+		schedule();
+		goto retry;
+	}
+	if (error)
+		goto end;
+
+	memset(lp->lvb, 0, DLM_LVB_LEN);
+	lm_dlm_unlock_sync(lock, LM_ST_EXCLUSIVE);
+
+ end:
+	dlm_del_lvb(lp);
+	lm_dlm_put_lock(lock);
+}
+
+/*
+ * Get the VALUE for a given ID.  The ID is a journalid, the VALUE is a nodeid.
+ */
+
+static int id_value(dlm_t *dlm, uint32_t id, uint32_t *val)
+{
+	dlm_lock_t *lp = NULL;
+	struct lm_lockname name;
+	lm_lock_t *lock;
+	char *lvb;
+	uint32_t beval;
+	int error;
+
+	name.ln_type = LM_TYPE_JID;
+	name.ln_number = id;
+
+	error = lm_dlm_get_lock(dlm, &name, &lock);
+	if (error)
+		goto out;
+
+	lp = (dlm_lock_t *) lock;
+
+	error = dlm_add_lvb(lp);
+	if (error)
+		goto out_put;
+
+	lvb = lp->lvb;
+	set_bit(LFL_INLOCK, &lp->flags);
+	set_bit(LFL_NOBAST, &lp->flags);
+
+ retry:
+
+	error = lm_dlm_lock_sync(lock, LM_ST_UNLOCKED, LM_ST_SHARED,
+			         LM_FLAG_TRY | LM_FLAG_NOEXP);
+	if (error == -EAGAIN) {
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(HZ);
+		goto retry;
+	}
+	if (error)
+		goto out_lvb;
+
+	memcpy(&beval, lvb, sizeof(beval));
+	*val = be32_to_cpu(beval);
+
+	lm_dlm_unlock_sync(lock, LM_ST_SHARED);
+
+	error = 0;
+
+ out_lvb:
+	dlm_del_lvb(lp);
+
+ out_put:
+	lm_dlm_put_lock(lock);
+
+ out:
+	return error;
+}
+
+/*
+ * Find an ID with a given VALUE.  The ID is a journalid, the VALUE is a
+ * nodeid.
+ */
+
+static int id_find(dlm_t *dlm, uint32_t value, uint32_t *id_out)
+{
+	uint32_t val, id;
+	int error = 0, found = FALSE;
+
+	for (id = 0; id < dlm->max_nodes; id++) {
+		error = id_value(dlm, id, &val);
+		if (error)
+			break;
+
+		if (val == value) {
+			*id_out = id;
+			error = 0;
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (!error && !found)
+		error = -ENOENT;
+
+	return error;
+}
+
+/*
+ * Get a journalid to use.  The journalid must be owned exclusively as long as
+ * this fs is mounted.  Other nodes must be able to discover our nodeid as the
+ * owner of the journalid.  The journalid we claim should have the lowest value
+ * of all unused journalids.
+ */
+
+static int claim_jid(dlm_t *dlm)
+{
+	dlm_node_t *node;
+	uint32_t id;
+	int error = 0;
+
+	DLM_ASSERT(dlm->our_nodeid,);
+
+	/*
+	 * Search an arbitrary number (8) past max nodes so we're sure to find
+	 * one so we can let the GFS handle the "too big jid" error and fail
+	 * the mount.
+	 */
+
+	for (id = 0; id < dlm->max_nodes + 8; id++) {
+		error = id_test_and_set(dlm, id, dlm->our_nodeid, &dlm->jid_lp);
+		if (error < 0)
+			break;
+		if (error > 0)
+			continue;
+
+		dlm->jid = id;
+		node = find_node_by_nodeid(dlm, dlm->our_nodeid);
+		node->jid = id;
+		set_bit(NFL_HAVE_JID, &node->flags);
+		break;
+	}
+
+	/*
+	 * If we have a problem getting a jid, pick a bogus one which should
+	 * cause GFS to complain and fail to mount.
+	 */
+
+	if (error) {
+		printk("lock_dlm: %s: no journal id available (%d)\n",
+		       dlm->fsname, error);
+		dlm->jid = dlm->max_nodes + dlm->our_nodeid;
+	}
+
+	log_debug("claim_jid %u", dlm->jid);
+	return 0;
+}
+
+/*
+ * Release our journalid, allowing it to be used by a node subsequently
+ * mounting the fs.  When withdrawing, we've already left the lockspace.
+ */
+
+static void release_jid(dlm_t *dlm)
+{
+	if (test_bit(DFL_WITHDRAW, &dlm->flags)) {
+		dlm_del_lvb(dlm->jid_lp);
+		delete_lp(dlm->jid_lp);
+	} else
+		id_clear(dlm, dlm->jid_lp);
+	dlm->jid_lp = NULL;
+}
+
+/*
+ * For all nodes in the mountgroup, find the journalid being used by each.
+ */
+
+static int discover_jids(dlm_t *dlm)
+{
+	dlm_node_t *node;
+	uint32_t id;
+	int error, notfound = 0;
+
+	list_for_each_entry(node, &dlm->mg_nodes, list) {
+		if (test_bit(NFL_HAVE_JID, &node->flags))
+			continue;
+
+		error = id_find(dlm, node->nodeid, &id);
+		if (error) {
+			log_debug("jid for node %d not found", node->nodeid);
+			notfound++;
+			continue;
+		}
+
+		node->jid = id;
+		set_bit(NFL_HAVE_JID, &node->flags);
+	}
+
+	return notfound;
+}
+
+/*
+ * Discover the nodeid that we've been assigned by the cluster manager.
+ */
+
+static int get_our_nodeid(dlm_t *dlm)
+{
+	LIST_HEAD(cur_memb);
+	struct kcl_cluster_node *cur_node;
+
+	kcl_get_members(&cur_memb);
+
+	list_for_each_entry(cur_node, &cur_memb, list) {
+		if (cur_node->us) {
+			dlm->our_nodeid = cur_node->node_id;
+			break;
+		}
+	}
+
+	while (!list_empty(&cur_memb)) {
+		cur_node = list_entry(cur_memb.next, struct kcl_cluster_node,
+				      list);
+		list_del(&cur_node->list);
+		kfree(cur_node);
+	}
+
+	return 0;
+}
+
+static void release_mg_nodes(dlm_t *dlm)
+{
+	dlm_node_t *node, *safe;
+
+	list_for_each_entry_safe(node, safe, &dlm->mg_nodes, list) {
+		list_del(&node->list);
+		lm_dlm_release_withdraw(dlm, node);
+		kfree(node);
+	}
+}
+
+/* 
+ * Run in dlm_async thread
+ */
+
+void process_start(dlm_t *dlm, dlm_start_t *ds)
+{
+	dlm_node_t *node;
+	uint32_t nodeid;
+	int last_stop, last_start, last_finish;
+	int error, i, new = FALSE, found;
+
+	down(&dlm->unmount_lock);
+
+	/*
+	 * when the initial sequence of callbacks is start/stop/start the
+	 * second start has the same event id (the first doesn't count)
+	 */
+
+	spin_lock(&dlm->async_lock);
+	last_stop = dlm->mg_last_stop;
+	last_start = dlm->mg_last_start;
+	last_finish = dlm->mg_last_finish;
+
+	if (!last_finish && last_stop) {
+		log_debug("pr_start reset stop %d start %d finish %d",
+			  last_stop, last_start, last_finish);
+		dlm->mg_last_stop = 0;
+		last_stop = 0;
+	}
+	spin_unlock(&dlm->async_lock);
+
+	log_debug("pr_start last_stop %d last_start %d last_finish %d",
+		  last_stop, last_start, last_finish);
+	log_debug("pr_start count %d type %d event %d flags %lx",
+		  ds->count, ds->type, ds->event_id, dlm->flags);
+
+	/*
+	 * gfs won't do journal recoveries once it's sent us an unmount or
+	 * withdraw
+	 */
+
+	if (test_bit(DFL_UMOUNT, &dlm->flags) ||
+	    test_bit(DFL_WITHDRAW, &dlm->flags)) {
+		log_debug("pr_start %d skip for umount/wd", ds->event_id);
+		kcl_start_done(dlm->mg_local_id, ds->event_id);
+		goto out;
+	}
+
+	/*
+	 * a couple special things to take care of on the first start (mount)
+	 */
+
+	if (!test_and_set_bit(DFL_GOT_NODEID, &dlm->flags))
+		get_our_nodeid(dlm);
+
+	if (test_bit(DFL_MOUNT, &dlm->flags) && (ds->count == 1))
+		set_bit(DFL_FIRST_MOUNT, &dlm->flags);
+
+	down(&dlm->mg_nodes_lock);
+
+	/*
+	 * while mounting, cancelled starts are discarded
+	 * (normally, (uninterrupted starts) mg_nodes is empty at this point)
+	 */
+
+	if (test_bit(DFL_MOUNT, &dlm->flags))
+		release_mg_nodes(dlm);
+
+	/*
+	 * find nodes which are gone
+	 */
+
+	list_for_each_entry(node, &dlm->mg_nodes, list) {
+		found = FALSE;
+		for (i = 0; i < ds->count; i++) {
+			if (node->nodeid != ds->nodeids[i])
+				continue;
+			found = TRUE;
+			break;
+		}
+		
+		/* node is still a member */
+		if (found)
+			continue;
+
+		set_bit(NFL_NOT_MEMBER, &node->flags);
+
+		/* failed and withdrawing nodes need journal recovery */
+		if (ds->type != SERVICE_NODE_FAILED &&
+		    !test_bit(NFL_WITHDRAW, &node->flags))
+			continue;
+
+		/* callbacks sent only for nodes in last completed MG */
+		if (!test_bit(NFL_LAST_FINISH, &node->flags))
+			continue;
+
+		/* only send a single callback per node */
+		if (test_and_set_bit(NFL_SENT_CB, &node->flags))
+			continue;
+
+		dlm->fscb(dlm->fsdata, LM_CB_NEED_RECOVERY, &node->jid);
+		set_bit(DFL_NEED_STARTDONE, &dlm->flags);
+		log_debug("pr_start cb jid %u id %u", node->jid, node->nodeid);
+	}
+
+	/*
+	 * add new nodes
+	 */
+
+	for (i = 0; i < ds->count; i++) {
+		nodeid = ds->nodeids[i];
+
+		node = find_node_by_nodeid(dlm, nodeid);
+		if (node)
+			continue;
+
+		DLM_RETRY(node = kmalloc(sizeof(dlm_node_t), GFP_KERNEL), node);
+		memset(node, 0, sizeof(dlm_node_t));
+
+		node->nodeid = nodeid;
+		list_add(&node->list, &dlm->mg_nodes);
+		new = TRUE;
+	}
+
+	up(&dlm->mg_nodes_lock);
+
+	/*
+	 * get a jid for ourself when started for first time
+	 */
+
+	if (!test_and_set_bit(DFL_HAVE_JID, &dlm->flags))
+		claim_jid(dlm);
+	else if (new) {
+		/* give new nodes a little time to claim a jid */
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ);
+	}
+
+	/* 
+	 * find jid's of new nodes
+	 */
+
+	for (;;) {
+		/* we don't need to do these jid lookups if this start has been
+		   followed by a stop event (and thus cancelled) */
+
+		spin_lock(&dlm->async_lock);
+		last_stop = dlm->mg_last_stop;
+		last_start = dlm->mg_last_start;
+		spin_unlock(&dlm->async_lock);
+
+		if (last_stop >= ds->event_id) {
+			log_debug("pr_start %d abort discover", ds->event_id);
+			break;
+		}
+
+		error = discover_jids(dlm);
+		if (error) {
+			/* Not all jids were found.  Wait for a time to let all
+			   new nodes claim_jid, then try to scan for jids
+			   again. */
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(HZ);
+			continue;
+		}
+		break;
+	}
+
+	/*
+	 * Hold withdraw locks for all nodes in PR.  We're notified by a bast
+	 * if one of them withdraws.  We promote ours to EX to withdraw
+	 * ourself.
+	 */
+
+	lm_dlm_hold_withdraw(dlm);
+
+	/*
+	 * tell SM we're done if there are no GFS recoveries to wait for
+	 */
+
+	if (last_start > last_stop) {
+		error = 0;
+		down(&dlm->mg_nodes_lock);
+
+		list_for_each_entry(node, &dlm->mg_nodes, list) {
+			if (!test_bit(NFL_SENT_CB, &node->flags))
+				continue;
+			error = 1;
+			break;
+		}
+		up(&dlm->mg_nodes_lock);
+
+		/* We were the first mounter and haven't gotten omm yet
+		   so we can't let this second node complete its mount.
+		   We set WAIT_OMM so that the eventual call to omm will
+		   do the kcl_start_done(). */
+
+		if (test_bit(DFL_FIRST_MOUNT, &dlm->flags) &&
+		    !test_bit(DFL_OTHERSMAYMOUNT, &dlm->flags) &&
+		    !test_bit(DFL_MOUNT, &dlm->flags)) {
+			log_debug("pr_start delay done before omm %lx",
+				  dlm->flags);
+			set_bit(DFL_WAIT_OTHERSMAYMOUNT, &dlm->flags);
+			error = 1;
+		}
+
+		log_debug("pr_start %d done %d", ds->event_id, !error);
+
+		if (!error)
+			kcl_start_done(dlm->mg_local_id, ds->event_id);
+	} else
+		log_debug("pr_start %d stopped %d", ds->event_id, last_stop);
+
+ out:
+	clear_bit(DFL_RECOVER, &dlm->flags);
+	kfree(ds->nodeids);
+	kfree(ds);
+	up(&dlm->unmount_lock);
+}
+
+void process_finish(dlm_t *dlm)
+{
+	struct list_head *tmp, *tmpsafe;
+	dlm_node_t *node;
+	dlm_lock_t *lp;
+	int leave_blocked = FALSE;
+
+	log_debug("pr_finish flags %lx", dlm->flags);
+
+	down(&dlm->mg_nodes_lock);
+	list_for_each_safe(tmp, tmpsafe, &dlm->mg_nodes) {
+		node = list_entry(tmp, dlm_node_t, list);
+
+		if (test_bit(NFL_NOT_MEMBER, &node->flags)) {
+			list_del(&node->list);
+			lm_dlm_release_withdraw(dlm, node);
+			kfree(node);
+		} else {
+			set_bit(NFL_LAST_FINISH, &node->flags);
+
+			/* If there are still withdrawing nodes that haven't
+			   left the MG (had their journals recovered), we need
+			   to keep lock requests blocked */
+
+			if (test_bit(NFL_WITHDRAW, &node->flags)) {
+				log_debug("pr_finish leave blocked for %d",
+					node->nodeid);
+				leave_blocked = TRUE;
+			}
+		}
+	}
+	up(&dlm->mg_nodes_lock);
+
+	if (leave_blocked)
+		goto out;
+
+	spin_lock(&dlm->async_lock);
+	clear_bit(DFL_BLOCK_LOCKS, &dlm->flags);
+
+	list_for_each_safe(tmp, tmpsafe, &dlm->delayed) {
+		lp = list_entry(tmp, dlm_lock_t, dlist);
+
+		if (lp->type != QUEUE_LOCKS_BLOCKED)
+			continue;
+
+		lp->type = 0;
+		list_del(&lp->dlist);
+		list_add_tail(&lp->slist, &dlm->submit);
+
+		clear_bit(LFL_DLIST, &lp->flags);
+		set_bit(LFL_SLIST, &lp->flags);
+	}
+	spin_unlock(&dlm->async_lock);
+
+ out:
+	clear_bit(DFL_MOUNT, &dlm->flags);
+	wake_up(&dlm->wait);
+}
+
+/*
+ * Run in user process
+ */
+
+int init_mountgroup(dlm_t *dlm)
+{
+	int error;
+	int id;
+
+	error = kcl_register_service(dlm->fsname, dlm->fnlen, SERVICE_LEVEL_GFS,
+				     &mg_ops, TRUE, (void *) dlm, &id);
+	if (error)
+		goto out;
+
+	dlm->mg_local_id = id;
+
+	/* MOUNT and BLOCK_LOCKS are cleared when the join is finished */
+	set_bit(DFL_BLOCK_LOCKS, &dlm->flags);
+	set_bit(DFL_MOUNT, &dlm->flags);
+
+	error = kcl_join_service(id);
+	if (error)
+		goto out_unreg;
+
+	if (test_bit(DFL_START_ERROR, &dlm->flags))
+		goto out_leave;
+
+	return 0;
+
+ out_leave:
+	kcl_leave_service(dlm->mg_local_id);
+
+ out_unreg:
+	kcl_unregister_service(id);
+
+ out:
+	printk("lock_dlm: service error %d\n", error);
+	return error;
+}
+
+void release_mountgroup(dlm_t *dlm)
+{
+	int last_start, last_stop;
+
+	down(&dlm->unmount_lock);
+
+	/* this flag causes a kcl_start_done() to be sent right away for
+	   any start callbacks we get from SM */
+
+	log_debug("release_mountgroup flags %lx", dlm->flags);
+	set_bit(DFL_UMOUNT, &dlm->flags);
+
+	/* gfs has done a unmount and will not call jid_recovery_done()
+	   any longer so make necessary kcl_start_done() calls so
+	   kcl_leave_service() will complete */
+
+	spin_lock(&dlm->async_lock);
+	last_start = dlm->mg_last_start;
+	last_stop = dlm->mg_last_stop;
+	spin_unlock(&dlm->async_lock);
+
+	if ((last_start > last_stop) &&
+	    test_and_clear_bit(DFL_NEED_STARTDONE, &dlm->flags)) {
+		log_debug("umount doing start_done %d", last_start);
+		kcl_start_done(dlm->mg_local_id, last_start);
+	}
+
+	up(&dlm->unmount_lock);
+
+	kcl_leave_service(dlm->mg_local_id);
+	kcl_unregister_service(dlm->mg_local_id);
+
+	release_jid(dlm);
+	release_mg_nodes(dlm);
+}
+
+/*
+ * Run in GFS thread
+ */
+
+void jid_recovery_done(dlm_t *dlm, unsigned int jid, unsigned int message)
+{
+	dlm_node_t *node;
+	int last_start, last_stop;
+	int remain = 0;
+
+	log_debug("recovery_done jid %u msg %u %lx", jid, message, dlm->flags);
+
+	node = find_node_by_jid(dlm, jid);
+	if (!node)
+		goto out;
+
+	log_debug("recovery_done nodeid %u flg %lx", node->nodeid, node->flags);
+
+	if (!test_bit(NFL_SENT_CB, &node->flags))
+		goto out;
+
+	if (!test_bit(NFL_NOT_MEMBER, &node->flags))
+		goto out;
+
+	set_bit(NFL_RECOVERY_DONE, &node->flags);
+
+	/* 
+	 * when recovery is done for all nodes, we're done with the start
+	 */
+
+	down(&dlm->mg_nodes_lock);
+
+	list_for_each_entry(node, &dlm->mg_nodes, list) {
+		if (test_bit(NFL_SENT_CB, &node->flags) &&
+		    !test_bit(NFL_RECOVERY_DONE, &node->flags))
+			remain++;
+	}
+	up(&dlm->mg_nodes_lock);
+
+	if (!remain) {
+		/* don't send a start_done if there's since been a stop which
+		 * cancels this start */
+
+		spin_lock(&dlm->async_lock);
+		last_start = dlm->mg_last_start;
+		last_stop = dlm->mg_last_stop;
+		spin_unlock(&dlm->async_lock);
+
+		if (last_start > last_stop) {
+			log_debug("recovery_done start_done %d", last_start);
+			kcl_start_done(dlm->mg_local_id, last_start);
+			clear_bit(DFL_NEED_STARTDONE, &dlm->flags);
+		}
+	}
+
+ out:
+	return;
+}
+
+/* 
+ * Run in CMAN SM thread
+ */
+
+static void queue_start(dlm_t *dlm, uint32_t *nodeids, int count,
+			int event_id, int type)
+{
+	dlm_start_t *ds;
+
+	DLM_RETRY(ds = kmalloc(sizeof(dlm_start_t), GFP_KERNEL), ds);
+
+	memset(ds, 0, sizeof(dlm_start_t));
+
+	ds->nodeids = nodeids;
+	ds->count = count;
+	ds->event_id = event_id;
+	ds->type = type;
+
+	spin_lock(&dlm->async_lock);
+	dlm->mg_last_start = event_id;
+	list_add_tail(&ds->list, &dlm->starts);
+	spin_unlock(&dlm->async_lock);
+
+	wake_up(&dlm->wait);
+}
+
+static int mg_stop(void *data)
+{
+	dlm_t *dlm = (dlm_t *) data;
+
+	spin_lock(&dlm->async_lock);
+	set_bit(DFL_BLOCK_LOCKS, &dlm->flags);
+	dlm->mg_last_stop = dlm->mg_last_start;
+	spin_unlock(&dlm->async_lock);
+
+	return 0;
+}
+
+static int mg_start(void *data, uint32_t *nodeids, int count, int event_id,
+		    int type)
+{
+	dlm_t *dlm = (dlm_t *) data;
+
+	queue_start(dlm, nodeids, count, event_id, type);
+
+	return 0;
+}
+
+static void mg_finish(void *data, int event_id)
+{
+	dlm_t *dlm = (dlm_t *) data;
+
+	spin_lock(&dlm->async_lock);
+	dlm->mg_last_finish = event_id;
+	set_bit(DFL_MG_FINISH, &dlm->flags);
+	spin_unlock(&dlm->async_lock);
+
+	wake_up(&dlm->wait);
+}
+
+struct kcl_service_ops mg_ops = {
+	.stop = mg_stop,
+	.start = mg_start,
+	.finish = mg_finish
+};
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_dlm/lock.c linux-2.6.9.debug/fs/gfs_locking/lock_dlm/lock.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_dlm/lock.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_dlm/lock.c	2006-12-20 17:07:48.000000000 +0300
@@ -0,0 +1,754 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "lock_dlm.h"
+
+static char junk_lvb[DLM_LVB_SIZE];
+
+
+/*
+ * Run in DLM thread
+ */
+
+static void queue_complete(dlm_lock_t *lp)
+{
+	dlm_t *dlm = lp->dlm;
+
+	if (!test_bit(LFL_WAIT_COMPLETE, &lp->flags)) {
+		log_all("extra completion %x,%"PRIx64" %d,%d id %x flags %lx",
+		        lp->lockname.ln_type, lp->lockname.ln_number,
+		        lp->cur, lp->req, lp->lksb.sb_lkid, lp->flags);
+		return;
+	}
+
+	clear_bit(LFL_WAIT_COMPLETE, &lp->flags);
+
+	/*
+	log_debug("qc %x,%"PRIx64" %d,%d id %x sts %d %x",
+		  lp->lockname.ln_type, lp->lockname.ln_number,
+		  lp->cur, lp->req, lp->lksb.sb_lkid, lp->lksb.sb_status,
+		  lp->lksb.sb_flags);
+	*/
+
+	spin_lock(&dlm->async_lock);
+	list_add_tail(&lp->clist, &dlm->complete);
+	set_bit(LFL_CLIST, &lp->flags);
+	spin_unlock(&dlm->async_lock);
+	wake_up(&dlm->wait);
+}
+
+static void queue_blocking(dlm_lock_t *lp, int mode)
+{
+	dlm_t *dlm = lp->dlm;
+
+	/* We often get basts for EX while we're promoting from SH to EX */
+	/*
+	if (test_bit(LFL_WAIT_COMPLETE, &lp->flags)) {
+		log_debug("bast during wait %x,%"PRIx64" %d-%d %x bmode %d",
+			  lp->lockname.ln_type, lp->lockname.ln_number,
+			  lp->cur, lp->req, lp->lkf, mode);
+	}
+	*/
+
+	if (!mode) {
+		printk("lock_dlm: bast mode zero %x,%"PRIx64"\n",
+			lp->lockname.ln_type, lp->lockname.ln_number);
+		return;
+	}
+
+	spin_lock(&dlm->async_lock);
+
+	if (!lp->bast_mode) {
+		list_add_tail(&lp->blist, &dlm->blocking);
+		set_bit(LFL_BLIST, &lp->flags);
+		lp->bast_mode = mode;
+	} else if (lp->bast_mode < mode)
+		lp->bast_mode = mode;
+
+	spin_unlock(&dlm->async_lock);
+	wake_up(&dlm->wait);
+}
+
+static __inline__ void lock_ast(void *astarg)
+{
+	queue_complete((dlm_lock_t *) astarg);
+}
+
+static __inline__ void lock_bast(void *astarg, int mode)
+{
+	queue_blocking((dlm_lock_t *) astarg, mode);
+}
+
+/*
+ * Run in GFS or user thread
+ */
+
+/**
+ * queue_delayed - add request to queue to be submitted later
+ * @lp: DLM lock
+ * @type: the reason the lock is blocked
+ *
+ * Queue of locks which need submitting sometime later.  Locks here
+ * due to BLOCKED_LOCKS are moved to request queue when recovery is
+ * done.  Locks here due to an ERROR are moved to request queue after
+ * some delay.  This could also be called from dlm_async thread.
+ */
+
+void queue_delayed(dlm_lock_t *lp, int type)
+{
+	dlm_t *dlm = lp->dlm;
+
+	lp->type = type;
+
+	spin_lock(&dlm->async_lock);
+	list_add_tail(&lp->dlist, &dlm->delayed);
+	set_bit(LFL_DLIST, &lp->flags);
+	spin_unlock(&dlm->async_lock);
+}
+
+/**
+ * make_mode - convert to DLM_LOCK_
+ * @lmstate: GFS lock state
+ *
+ * Returns: DLM lock mode
+ */
+
+static int16_t make_mode(int16_t lmstate)
+{
+	switch (lmstate) {
+	case LM_ST_UNLOCKED:
+		return DLM_LOCK_NL;
+	case LM_ST_EXCLUSIVE:
+		return DLM_LOCK_EX;
+	case LM_ST_DEFERRED:
+		return DLM_LOCK_CW;
+	case LM_ST_SHARED:
+		return DLM_LOCK_PR;
+	default:
+		DLM_ASSERT(0, printk("unknown LM state %d\n", lmstate););
+	}
+}
+
+/**
+ * make_lmstate - convert to LM_ST_
+ * @dlmmode: DLM lock mode 
+ *
+ * Returns: GFS lock state 
+ */
+
+int16_t make_lmstate(int16_t dlmmode)
+{
+	switch (dlmmode) {
+	case DLM_LOCK_IV:
+	case DLM_LOCK_NL:
+		return LM_ST_UNLOCKED;
+	case DLM_LOCK_EX:
+		return LM_ST_EXCLUSIVE;
+	case DLM_LOCK_CW:
+		return LM_ST_DEFERRED;
+	case DLM_LOCK_PR:
+		return LM_ST_SHARED;
+	default:
+		DLM_ASSERT(0, printk("unknown DLM mode %d\n", dlmmode););
+	}
+}
+
+/**
+ * check_cur_state - verify agreement with GFS on the current lock state
+ * @lp: the DLM lock 
+ * @cur_state: the current lock state from GFS
+ *
+ * NB: DLM_LOCK_NL and DLM_LOCK_IV are both considered 
+ * LM_ST_UNLOCKED by GFS.
+ *
+ */
+
+static void check_cur_state(dlm_lock_t *lp, unsigned int cur_state)
+{
+	int16_t cur = make_mode(cur_state);
+	if (lp->cur != DLM_LOCK_IV)
+		DLM_ASSERT(lp->cur == cur, printk("%d, %d\n", lp->cur, cur););
+}
+
+/**
+ * make_flags - put together necessary DLM flags
+ * @lp: DLM lock
+ * @gfs_flags: GFS flags
+ * @cur: current DLM lock mode
+ * @req: requested DLM lock mode
+ *
+ * Returns: DLM flags
+ */
+
+static unsigned int make_flags(dlm_lock_t *lp, unsigned int gfs_flags,
+			       int16_t cur, int16_t req)
+{
+	unsigned int lkf = 0;
+
+	if (gfs_flags & LM_FLAG_TRY)
+		lkf |= DLM_LKF_NOQUEUE;
+
+	if (gfs_flags & LM_FLAG_TRY_1CB) {
+		lkf |= DLM_LKF_NOQUEUE;
+		lkf |= DLM_LKF_NOQUEUEBAST;
+	}
+
+	if (gfs_flags & LM_FLAG_PRIORITY) {
+		lkf |= DLM_LKF_NOORDER;
+		lkf |= DLM_LKF_HEADQUE;
+	}
+
+	if (gfs_flags & LM_FLAG_ANY) {
+		if (req == DLM_LOCK_PR)
+			lkf |= DLM_LKF_ALTCW;
+		else if (req == DLM_LOCK_CW)
+			lkf |= DLM_LKF_ALTPR;
+	}
+
+	if (lp->lksb.sb_lkid != 0) {
+		lkf |= DLM_LKF_CONVERT;
+
+		/* Conversion deadlock avoidance by DLM */
+
+		if (!test_bit(LFL_FORCE_PROMOTE, &lp->flags) &&
+		    !(lkf & DLM_LKF_NOQUEUE) &&
+		    cur > DLM_LOCK_NL && req > DLM_LOCK_NL && cur != req)
+			lkf |= DLM_LKF_CONVDEADLK;
+	}
+
+	if (lp->lvb)
+		lkf |= DLM_LKF_VALBLK;
+
+	return lkf;
+}
+
+/**
+ * make_strname - convert GFS lock numbers to string
+ * @lockname: the lock type/number 
+ * @str: the lock string/length
+ *
+ */
+
+static __inline__ void make_strname(struct lm_lockname *lockname,
+				    strname_t *str)
+{
+	sprintf(str->name, "%8x%16"PRIx64, lockname->ln_type,
+		lockname->ln_number);
+	str->namelen = LOCK_DLM_STRNAME_BYTES;
+}
+
+int create_lp(dlm_t *dlm, struct lm_lockname *name, dlm_lock_t **lpp)
+{
+	dlm_lock_t *lp;
+
+	lp = kmalloc(sizeof(dlm_lock_t), GFP_KERNEL);
+	if (!lp)
+		return -ENOMEM;
+
+	memset(lp, 0, sizeof(dlm_lock_t));
+	lp->lockname = *name;
+	lp->dlm = dlm;
+	lp->cur = DLM_LOCK_IV;
+	lp->lvb = NULL;
+	lp->hold_null = NULL;
+	init_completion(&lp->uast_wait);
+
+	*lpp = lp;
+	return 0;
+}
+
+void delete_lp(dlm_lock_t *lp)
+{
+	dlm_t *dlm = lp->dlm;
+
+	spin_lock(&dlm->async_lock);
+	if (test_bit(LFL_CLIST, &lp->flags)) {
+		printk("lock_dlm: dlm_put_lock lp on clist num=%x,%"PRIx64"\n",
+		       lp->lockname.ln_type, lp->lockname.ln_number);
+		list_del(&lp->clist);
+	}
+	if (test_bit(LFL_BLIST, &lp->flags)) {
+		/*
+		printk("lock_dlm: dlm_put_lock lp on blist num=%x,%"PRIx64"\n",
+		       lp->lockname.ln_type, lp->lockname.ln_number);
+		*/
+		list_del(&lp->blist);
+	}
+	if (test_bit(LFL_DLIST, &lp->flags)) {
+		printk("lock_dlm: dlm_put_lock lp on dlist num=%x,%"PRIx64"\n",
+		       lp->lockname.ln_type, lp->lockname.ln_number);
+		list_del(&lp->dlist);
+	}
+	if (test_bit(LFL_SLIST, &lp->flags)) {
+		printk("lock_dlm: dlm_put_lock lp on slist num=%x,%"PRIx64"\n",
+		       lp->lockname.ln_type, lp->lockname.ln_number);
+		list_del(&lp->slist);
+	}
+	spin_unlock(&dlm->async_lock);
+
+	kfree(lp);
+}
+
+/**
+ * dlm_get_lock - get a lm_lock_t given a descripton of the lock
+ * @lockspace: the lockspace the lock lives in
+ * @name: the name of the lock
+ * @lockp: return the lm_lock_t here
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int lm_dlm_get_lock(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		    lm_lock_t **lockp)
+{
+	dlm_lock_t *lp;
+	int error;
+
+	error = create_lp((dlm_t *) lockspace, name, &lp);
+
+	*lockp = (lm_lock_t *) lp;
+	return error;
+}
+
+/**
+ * dlm_put_lock - get rid of a lock structure
+ * @lock: the lock to throw away
+ *
+ */
+
+void lm_dlm_put_lock(lm_lock_t *lock)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+	DLM_ASSERT(!lp->lvb,);
+	delete_lp(lp);
+}
+
+void do_dlm_unlock(dlm_lock_t *lp)
+{
+	unsigned int lkf = 0;
+	int error;
+
+	set_bit(LFL_DLM_UNLOCK, &lp->flags);
+	set_bit(LFL_WAIT_COMPLETE, &lp->flags);
+
+	if (lp->lvb)
+		lkf = DLM_LKF_VALBLK;
+
+	/*
+	log_debug("un %x,%"PRIx64" %x %d %x", lp->lockname.ln_type,
+		  lp->lockname.ln_number, lp->lksb.sb_lkid, lp->cur, lkf);
+	*/
+
+	error = dlm_unlock(lp->dlm->gdlm_lsp, lp->lksb.sb_lkid, lkf, NULL, lp);
+
+	DLM_ASSERT(!error,
+		   printk("%s: error=%d num=%x,%"PRIx64" lkf=%x flags=%lx\n",
+			  lp->dlm->fsname, error, lp->lockname.ln_type,
+			  lp->lockname.ln_number, lp->lkf, lp->flags););
+}
+
+void do_dlm_unlock_sync(dlm_lock_t *lp)
+{
+	set_bit(LFL_UNLOCK_SYNC, &lp->flags);
+	init_completion(&lp->uast_wait);
+	do_dlm_unlock(lp);
+	wait_for_completion(&lp->uast_wait);
+}
+
+void do_dlm_lock(dlm_lock_t *lp, struct dlm_range *range)
+{
+	dlm_t *dlm = lp->dlm;
+	strname_t str;
+	int error, bast = 1;
+
+	/*
+	 * Don't block any locks (NOEXP or not) from first mounter until it
+	 * calls others_may_mount().  This solves an end case where a second
+	 * mounter results in BLOCK_LOCKS while the first mounter is still
+	 * doing the mount.
+	 */
+
+	if (test_bit(DFL_FIRST_MOUNT, &dlm->flags) &&
+	    !test_bit(DFL_OTHERSMAYMOUNT, &dlm->flags))
+		set_bit(LFL_NOBLOCK, &lp->flags);
+
+	/*
+	 * When recovery is in progress, delay lock requests for submission
+	 * once recovery is done.  Requests for recovery (NOEXP) and unlocks
+	 * can pass.
+	 */
+
+	if (test_bit(DFL_BLOCK_LOCKS, &dlm->flags) &&
+	    !test_bit(LFL_NOBLOCK, &lp->flags) && lp->req != DLM_LOCK_NL) {
+		queue_delayed(lp, QUEUE_LOCKS_BLOCKED);
+		return;
+	}
+
+	/*
+	 * Submit the actual lock request.
+	 */
+
+	if (lp->posix || test_bit(LFL_NOBAST, &lp->flags))
+		bast = 0;
+
+	make_strname(&lp->lockname, &str);
+
+	set_bit(LFL_WAIT_COMPLETE, &lp->flags);
+
+	/*
+	log_debug("lk %x,%"PRIx64" id %x %d,%d %x", lp->lockname.ln_type,
+		  lp->lockname.ln_number, lp->lksb.sb_lkid,
+		  lp->cur, lp->req, lp->lkf);
+	*/
+
+	error = dlm_lock(dlm->gdlm_lsp, lp->req, &lp->lksb, lp->lkf, str.name,
+			  str.namelen, 0, lock_ast, (void *) lp,
+			  bast ? lock_bast : NULL, range);
+
+	if ((error == -EAGAIN) && (lp->lkf & DLM_LKF_NOQUEUE)) {
+		lp->lksb.sb_status = -EAGAIN;
+		queue_complete(lp);
+		error = 0;
+	}
+
+	DLM_ASSERT(!error,
+		   printk("%s: num=%x,%"PRIx64" err=%d cur=%d req=%d lkf=%x\n",
+			  dlm->fsname, lp->lockname.ln_type,
+			  lp->lockname.ln_number, error, lp->cur, lp->req,
+			  lp->lkf););
+}
+
+int do_dlm_lock_sync(dlm_lock_t *lp, struct dlm_range *range)
+{
+	init_completion(&lp->uast_wait);
+	do_dlm_lock(lp, range);
+	wait_for_completion(&lp->uast_wait);
+
+	return lp->lksb.sb_status;
+}
+
+/**
+ * lm_dlm_lock - acquire a lock
+ * @lock: the lock to manipulate
+ * @cur_state: the current state
+ * @req_state: the requested state
+ * @flags: modifier flags
+ *
+ * Returns: A bitmap of LM_OUT_* on success, -EXXX on failure
+ */
+
+unsigned int lm_dlm_lock(lm_lock_t *lock, unsigned int cur_state,
+			 unsigned int req_state, unsigned int flags)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+
+	clear_bit(LFL_DLM_CANCEL, &lp->flags);
+	if (flags & LM_FLAG_NOEXP)
+		set_bit(LFL_NOBLOCK, &lp->flags);
+
+	check_cur_state(lp, cur_state);
+	lp->req = make_mode(req_state);
+	lp->lkf = make_flags(lp, flags, lp->cur, lp->req);
+
+	do_dlm_lock(lp, NULL);
+	return LM_OUT_ASYNC;
+}
+
+int lm_dlm_lock_sync(lm_lock_t *lock, unsigned int cur_state,
+		     unsigned int req_state, unsigned int flags)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+
+	init_completion(&lp->uast_wait);
+	lm_dlm_lock(lock, cur_state, req_state, flags);
+	wait_for_completion(&lp->uast_wait);
+
+	return lp->lksb.sb_status;
+}
+
+/**
+ * lm_dlm_unlock - unlock a lock
+ * @lock: the lock to manipulate
+ * @cur_state: the current state
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+unsigned int lm_dlm_unlock(lm_lock_t *lock, unsigned int cur_state)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+
+	clear_bit(LFL_DLM_CANCEL, &lp->flags);
+	if (lp->cur == DLM_LOCK_IV)
+		return 0;
+	do_dlm_unlock(lp);
+	return LM_OUT_ASYNC;
+}
+
+void lm_dlm_unlock_sync(lm_lock_t *lock, unsigned int cur_state)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+
+	init_completion(&lp->uast_wait);
+	lm_dlm_unlock(lock, cur_state);
+	wait_for_completion(&lp->uast_wait);
+}
+
+/**
+ * dlm_cancel - cancel a request that is blocked due to DFL_BLOCK_LOCKS
+ * @lock: the lock to cancel request for
+ *
+ */
+
+void lm_dlm_cancel(lm_lock_t *lock)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+	int error, dlist = FALSE;
+
+	if (test_bit(LFL_DLM_CANCEL, &lp->flags))
+		return;
+
+	log_all("lm_dlm_cancel %x,%"PRIx64" flags %lx",
+		lp->lockname.ln_type, lp->lockname.ln_number, lp->flags);
+
+	spin_lock(&lp->dlm->async_lock);
+	if (test_and_clear_bit(LFL_DLIST, &lp->flags)) {
+		list_del(&lp->dlist);
+		lp->type = 0;
+		dlist = TRUE;
+	}
+	spin_unlock(&lp->dlm->async_lock);
+
+	if (dlist) {
+		set_bit(LFL_CANCEL, &lp->flags);
+		set_bit(LFL_WAIT_COMPLETE, &lp->flags);
+		queue_complete(lp);
+		return;
+	}
+
+	if (!test_bit(LFL_WAIT_COMPLETE, &lp->flags) ||
+	    test_bit(LFL_DLM_UNLOCK, &lp->flags))	{
+		log_all("lm_dlm_cancel skip %x,%"PRIx64" flags %lx",
+			lp->lockname.ln_type, lp->lockname.ln_number,
+			lp->flags);
+		return;
+	}
+
+	/* the lock is blocked in the dlm */
+
+	set_bit(LFL_DLM_CANCEL, &lp->flags);
+	set_bit(LFL_WAIT_COMPLETE, &lp->flags);
+
+	error = dlm_unlock(lp->dlm->gdlm_lsp, lp->lksb.sb_lkid, DLM_LKF_CANCEL,
+			   NULL, lp);
+
+	log_all("lm_dlm_cancel rv %d %x,%"PRIx64" flags %lx", error,
+	        lp->lockname.ln_type, lp->lockname.ln_number, lp->flags);
+
+	if (error == -EBUSY)
+		clear_bit(LFL_DLM_CANCEL, &lp->flags);
+}
+
+int dlm_add_lvb(dlm_lock_t *lp)
+{
+	char *lvb;
+
+	lvb = kmalloc(DLM_LVB_SIZE, GFP_KERNEL);
+	if (!lvb)
+		return -ENOMEM;
+
+	memset(lvb, 0, DLM_LVB_SIZE);
+
+	lp->lksb.sb_lvbptr = lvb;
+	lp->lvb = lvb;
+	return 0;
+}
+
+void dlm_del_lvb(dlm_lock_t *lp)
+{
+	kfree(lp->lvb);
+	lp->lvb = NULL;
+	lp->lksb.sb_lvbptr = NULL;
+}
+
+/**
+ * hold_null_lock - add a NL lock to the resource
+ * @lp: represents the resource
+ *
+ * This can do a synchronous dlm request (requiring a lock_dlm thread to
+ * get the completion) because gfs won't call hold_lvb() during a
+ * callback (from the context of a lock_dlm thread).
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+static int hold_null_lock(dlm_lock_t *lp)
+{
+	dlm_lock_t *lpn = NULL;
+	int error;
+
+	if (lp->hold_null) {
+		printk("lock_dlm: lvb already held\n");
+		return 0;
+	}
+
+	error = create_lp(lp->dlm, &lp->lockname, &lpn);
+	if (error)
+		goto out;
+
+	lpn->lksb.sb_lvbptr = junk_lvb;
+	lpn->lvb = junk_lvb;
+
+	lpn->req = DLM_LOCK_NL;
+	lpn->lkf = DLM_LKF_VALBLK | DLM_LKF_EXPEDITE;
+	set_bit(LFL_NOBAST, &lpn->flags);
+	set_bit(LFL_INLOCK, &lpn->flags);
+
+	error = do_dlm_lock_sync(lpn, NULL);
+	if (error) {
+		delete_lp(lpn);
+		lpn = NULL;
+	}
+
+ out:
+	lp->hold_null = lpn;
+	return error;
+}
+
+/**
+ * unhold_null_lock - remove the NL lock from the resource
+ * @lp: represents the resource
+ *
+ * This cannot do a synchronous dlm request (requiring a lock_dlm thread to
+ * get the completion) because gfs may call unhold_lvb() during a
+ * callback (from the context of a lock_dlm thread) which could cause a
+ * deadlock since the other lock_dlm thread could be engaged in recovery.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+static void unhold_null_lock(dlm_lock_t *lp)
+{
+	dlm_lock_t *lpn = lp->hold_null;
+
+	lpn->lksb.sb_lvbptr = NULL;
+	lpn->lvb = NULL;
+	set_bit(LFL_UNLOCK_DELETE, &lpn->flags);
+	do_dlm_unlock(lpn);
+	lp->hold_null = NULL;
+}
+
+/**
+ * dlm_hold_lvb - hold on to a lock value block
+ * @lock: the lock the LVB is associated with
+ * @lvbp: return the lvb memory here
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int lm_dlm_hold_lvb(lm_lock_t *lock, char **lvbp)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+	int error;
+
+	error = dlm_add_lvb(lp);
+	if (error)
+		return error;
+
+	*lvbp = lp->lvb;
+
+	/* Acquire a NL lock because gfs requires the value block to remain
+	   intact on the resource while the lvb is "held" even if it's holding
+	   no locks on the resource. */
+      
+	error = hold_null_lock(lp);
+	if (error)
+		dlm_del_lvb(lp);
+
+	return error;
+}
+
+/**
+ * dlm_unhold_lvb - release a LVB
+ * @lock: the lock the LVB is associated with
+ * @lvb: the lock value block
+ *
+ */
+
+void lm_dlm_unhold_lvb(lm_lock_t *lock, char *lvb)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+
+	unhold_null_lock(lp);
+	dlm_del_lvb(lp);
+}
+
+/**
+ * dlm_sync_lvb - sync out the value of a lvb
+ * @lock: the lock the LVB is associated with
+ * @lvb: the lock value block
+ *
+ */
+
+void lm_dlm_sync_lvb(lm_lock_t *lock, char *lvb)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) lock;
+
+	if (lp->cur != DLM_LOCK_EX)
+		return;
+
+	init_completion(&lp->uast_wait);
+	set_bit(LFL_SYNC_LVB, &lp->flags);
+
+	lp->req = DLM_LOCK_EX;
+	lp->lkf = make_flags(lp, 0, lp->cur, lp->req);
+
+	do_dlm_lock(lp, NULL);
+	wait_for_completion(&lp->uast_wait);
+}
+
+/**
+ * dlm_recovery_done - reset the expired locks for a given jid
+ * @lockspace: the lockspace
+ * @jid: the jid
+ *
+ */
+
+void lm_dlm_recovery_done(lm_lockspace_t *lockspace, unsigned int jid,
+			  unsigned int message)
+{
+	jid_recovery_done((dlm_t *) lockspace, jid, message);
+}
+
+/*
+ * Run in dlm_async
+ */
+
+/**
+ * process_submit - make DLM lock requests from dlm_async thread
+ * @lp: DLM Lock
+ *
+ */
+
+void process_submit(dlm_lock_t *lp)
+{
+	struct dlm_range range, *r = NULL;
+
+	if (lp->posix) {
+		range.ra_start = lp->posix->start;
+		range.ra_end = lp->posix->end;
+		r = &range;
+	}
+
+	do_dlm_lock(lp, r);
+}
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_dlm/lock_dlm.h linux-2.6.9.debug/fs/gfs_locking/lock_dlm/lock_dlm.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_dlm/lock_dlm.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_dlm/lock_dlm.h	2006-12-20 17:07:48.000000000 +0300
@@ -0,0 +1,386 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef LOCK_DLM_DOT_H
+#define LOCK_DLM_DOT_H
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+#include <linux/lm_interface.h>
+#include <cluster/cnxman.h>
+#include <cluster/service.h>
+#include <cluster/dlm.h>
+
+/* We take a shortcut and use lm_lockname structs for internal locks.  This
+   means we must be careful to keep these types different from those used in
+   lm_interface.h. */
+
+#define LM_TYPE_JID		(0x10)
+#define LM_TYPE_PLOCK_UPDATE	(0x11)
+
+#define DLM_LVB_SIZE		(DLM_LVB_LEN)
+
+/* GFS uses 12 bytes to identify a resource (32 bit type + 64 bit number).
+   We sprintf these numbers into a 24 byte string of hex values to make them
+   human-readable (to make debugging simpler.) */
+
+#define LOCK_DLM_STRNAME_BYTES	(24)
+
+#define LOCK_DLM_MAX_NODES	(128)
+
+#define DROP_LOCKS_COUNT	(50000)
+#define DROP_LOCKS_PERIOD	(60)
+#define SHRINK_CACHE_COUNT	(100)
+#define SHRINK_CACHE_MAX	(1000)
+#define SHRINK_CACHE_TIME	(30)
+
+struct dlm;
+struct dlm_lock;
+struct dlm_node;
+struct dlm_start;
+struct strname;
+
+typedef struct dlm dlm_t;
+typedef struct dlm_lock dlm_lock_t;
+typedef struct dlm_node dlm_node_t;
+typedef struct dlm_start dlm_start_t;
+typedef struct strname strname_t;
+
+#define DFL_FIRST_MOUNT         0
+#define DFL_GOT_NODEID          1
+#define DFL_MG_FINISH           2
+#define DFL_HAVE_JID            3
+#define DFL_BLOCK_LOCKS         4
+#define DFL_START_ERROR         5
+#define DFL_MOUNT		6
+#define DFL_UMOUNT		7
+#define DFL_NEED_STARTDONE	8
+#define DFL_RECOVER		9
+#define DFL_WITHDRAW		10
+#define DFL_OTHERSMAYMOUNT	11
+#define DFL_WAIT_OTHERSMAYMOUNT	12
+
+struct dlm {
+	uint32_t		jid;
+	uint32_t		our_nodeid;
+	unsigned long		flags;
+
+	int			cnlen;
+	char *			clustername;
+	int			fnlen;
+	char *			fsname;
+	int			max_nodes;
+
+	dlm_lockspace_t *	gdlm_lsp;
+
+	lm_callback_t		fscb;
+	lm_fsdata_t *		fsdata;
+	dlm_lock_t *		jid_lp;
+
+	spinlock_t		async_lock;
+	struct list_head	complete;
+	struct list_head	blocking;
+	struct list_head	delayed;
+	struct list_head	submit;
+	struct list_head	starts;
+
+	wait_queue_head_t	wait;
+	struct task_struct *	thread1;
+	struct task_struct *	thread2;
+	atomic_t		lock_count;
+	unsigned long		drop_time;
+	unsigned long		shrink_time;
+
+	int			drop_locks_count;
+	int			drop_locks_period;
+
+	int			mg_local_id;
+	int			mg_last_start;
+	int			mg_last_stop;
+	int			mg_last_finish;
+	struct list_head	mg_nodes;
+	struct semaphore	mg_nodes_lock;
+	struct semaphore	unmount_lock;
+
+	struct list_head	resources;
+	struct semaphore	res_lock;
+	struct list_head	null_cache;
+	spinlock_t		null_cache_spin;
+	uint32_t		null_count;
+};
+
+struct dlm_resource {
+	dlm_t *                 dlm;
+	struct list_head        list;           /* list of resources */
+	struct lm_lockname      name;           /* the resource name */
+	struct semaphore        sema;
+	struct list_head        locks;          /* one lock for each range */
+	int                     count;
+	dlm_lock_t *		update;
+	struct list_head	async_locks;
+	spinlock_t		async_spin;
+	wait_queue_head_t	waiters;
+};
+
+struct posix_lock {
+	struct list_head        list;           /* resource locks list */
+	struct list_head	async_list;	/* resource async_locks list */
+	struct dlm_resource *   resource;
+	dlm_lock_t *            lp;
+	unsigned long           owner;
+	unsigned int            pid;
+	uint64_t                start;
+	uint64_t                end;
+	int                     count;
+	int                     ex;
+};
+
+#define LFL_NOBLOCK             0
+#define LFL_NOCACHE             1
+#define LFL_DLM_UNLOCK          2
+#define LFL_TRYFAILED           3
+#define LFL_SYNC_LVB            4
+#define LFL_FORCE_PROMOTE       5
+#define LFL_REREQUEST           6
+#define LFL_WAIT_COMPLETE       7
+#define LFL_CLIST               8
+#define LFL_BLIST               9
+#define LFL_DLIST               10
+#define LFL_SLIST               11
+#define LFL_INLOCK              12
+#define LFL_CANCEL              13
+#define LFL_UNLOCK_SYNC         14
+#define LFL_NOBAST              15
+#define LFL_HEADQUE             16
+#define LFL_UNLOCK_DELETE       17
+#define LFL_DLM_CANCEL          18
+
+struct dlm_lock {
+	dlm_t *			dlm;
+	struct lm_lockname	lockname;
+	char *			lvb;
+	struct dlm_lksb		lksb;
+
+	int16_t			cur;
+	int16_t			req;
+	int16_t			prev_req;
+	unsigned int		lkf;
+	unsigned int		type;
+	unsigned long		flags;
+
+	int			bast_mode;	/* protected by async_lock */
+	struct completion	uast_wait;
+
+	struct list_head	clist;		/* complete */
+	struct list_head	blist;		/* blocking */
+	struct list_head	dlist;		/* delayed */
+	struct list_head	slist;		/* submit */
+
+	struct dlm_lock *	hold_null;	/* NL lock for hold_lvb */
+	struct posix_lock *	posix;
+	struct list_head	null_list;	/* NL lock cache for plocks */
+};
+
+#define NFL_SENT_CB             0
+#define NFL_NOT_MEMBER          1
+#define NFL_RECOVERY_DONE       2
+#define NFL_LAST_FINISH         3
+#define NFL_HAVE_JID            4
+#define NFL_WITHDRAW            5
+
+struct dlm_node {
+	uint32_t		nodeid;
+	uint32_t		jid;
+	unsigned long		flags;
+	dlm_lock_t *		withdraw_lp;
+	struct list_head	list;
+};
+
+#define QUEUE_LOCKS_BLOCKED     1
+#define QUEUE_ERROR_UNLOCK      2
+#define QUEUE_ERROR_LOCK        3
+#define QUEUE_ERROR_RETRY       4
+
+struct strname {
+	unsigned char		name[LOCK_DLM_STRNAME_BYTES];
+	unsigned short		namelen;
+};
+
+struct dlm_start {
+	uint32_t *		nodeids;
+	int			count;
+	int			type;
+	int			event_id;
+	struct list_head	list;
+};
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#if (BITS_PER_LONG == 64)
+#define PRIu64 "lu"
+#define PRId64 "ld"
+#define PRIo64 "lo"
+#define PRIx64 "lx"
+#define PRIX64 "lX"
+#define SCNu64 "lu"
+#define SCNd64 "ld"
+#define SCNo64 "lo"
+#define SCNx64 "lx"
+#define SCNX64 "lX"
+#else
+#define PRIu64 "Lu"
+#define PRId64 "Ld"
+#define PRIo64 "Lo"
+#define PRIx64 "Lx"
+#define PRIX64 "LX"
+#define SCNu64 "Lu"
+#define SCNd64 "Ld"
+#define SCNo64 "Lo"
+#define SCNx64 "Lx"
+#define SCNX64 "LX"
+#endif
+
+extern struct lm_lockops lock_dlm_ops;
+
+/* group.c */
+
+int init_mountgroup(dlm_t *dlm);
+void release_mountgroup(dlm_t *dlm);
+void process_start(dlm_t *dlm, dlm_start_t *ds);
+void process_finish(dlm_t *dlm);
+void jid_recovery_done(dlm_t *dlm, unsigned int jid, unsigned int message);
+
+/* mount.c */
+
+void lm_dlm_hold_withdraw(dlm_t *dlm);
+void lm_dlm_release_withdraw(dlm_t *dlm, dlm_node_t *node);
+
+/* thread.c */
+
+int init_async_thread(dlm_t *dlm);
+void release_async_thread(dlm_t *dlm);
+
+/* lock.c */
+
+int16_t make_lmstate(int16_t dlmmode);
+void queue_delayed(dlm_lock_t *lp, int type);
+void process_submit(dlm_lock_t *lp);
+int create_lp(dlm_t *dlm, struct lm_lockname *name, dlm_lock_t **lpp);
+void delete_lp(dlm_lock_t *lp);
+int dlm_add_lvb(dlm_lock_t *lp);
+void dlm_del_lvb(dlm_lock_t *lp);
+
+int lm_dlm_get_lock(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		    lm_lock_t **lockp);
+void lm_dlm_put_lock(lm_lock_t *lock);
+
+void do_dlm_lock(dlm_lock_t *lp, struct dlm_range *range);
+int do_dlm_lock_sync(dlm_lock_t *lp, struct dlm_range *range);
+void do_dlm_unlock(dlm_lock_t *lp);
+void do_dlm_unlock_sync(dlm_lock_t *lp);
+
+unsigned int lm_dlm_lock(lm_lock_t *lock, unsigned int cur_state,
+			 unsigned int req_state, unsigned int flags);
+int lm_dlm_lock_sync(lm_lock_t *lock, unsigned int cur_state,
+		     unsigned int req_state, unsigned int flags);
+unsigned int lm_dlm_unlock(lm_lock_t *lock, unsigned int cur_state);
+void lm_dlm_unlock_sync(lm_lock_t *lock, unsigned int cur_state);
+
+void lm_dlm_cancel(lm_lock_t *lock);
+int lm_dlm_hold_lvb(lm_lock_t *lock, char **lvbp);
+void lm_dlm_unhold_lvb(lm_lock_t *lock, char *lvb);
+void lm_dlm_sync_lvb(lm_lock_t *lock, char *lvb);
+void lm_dlm_recovery_done(lm_lockspace_t *lockspace, unsigned int jid,
+		          unsigned int message);
+
+/* plock.c */
+
+int lm_dlm_plock_get(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		     struct file *file, struct file_lock *fl);
+int lm_dlm_plock(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		 struct file *file, int cmd, struct file_lock *fl);
+int lm_dlm_punlock(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		   struct file *file, struct file_lock *fl);
+void clear_null_cache(dlm_t *dlm);
+void shrink_null_cache(dlm_t *dlm);
+
+/* main.c */
+
+void lock_dlm_debug_log(const char *fmt, ...);
+void lock_dlm_debug_dump(void);
+
+
+#define LOCK_DLM_DEBUG
+
+#ifdef LOCK_DLM_DEBUG
+#define log_debug(fmt, args...) lock_dlm_debug_log(fmt, ##args)
+#else
+#define log_debug(fmt, args...)
+#endif
+
+#define log_all(fmt, args...) \
+	do { \
+		printk("lock_dlm: " fmt "\n", ##args); \
+		lock_dlm_debug_log(fmt, ##args); \
+	} while (0)
+
+#define log_error log_all
+
+
+static inline int check_timeout(unsigned long stamp, unsigned int seconds)
+{
+    return time_after(jiffies, stamp + seconds * HZ);
+}
+
+#define DLM_ASSERT(x, do) \
+{ \
+  if (!(x)) \
+  { \
+    dlm_debug_dump(); \
+    lock_dlm_debug_dump(); \
+    printk("\nlock_dlm:  Assertion failed on line %d of file %s\n" \
+           "lock_dlm:  assertion:  \"%s\"\n" \
+	   "lock_dlm:  time = %lu\n", \
+	   __LINE__, __FILE__, #x, jiffies); \
+    {do} \
+    printk("\n"); \
+    BUG(); \
+    panic("lock_dlm:  Record message above and reboot.\n"); \
+  } \
+}
+
+#define DLM_RETRY(do_this, until_this) \
+for (;;) \
+{ \
+  do { do_this; } while (0); \
+  if (until_this) \
+    break; \
+  printk("lock_dlm:  out of memory:  %s, %u\n", __FILE__, __LINE__); \
+  schedule();\
+}
+
+#endif
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_dlm/main.c linux-2.6.9.debug/fs/gfs_locking/lock_dlm/main.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_dlm/main.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_dlm/main.c	2006-12-20 17:07:48.000000000 +0300
@@ -0,0 +1,330 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "lock_dlm.h"
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#if defined(LOCK_DLM_DEBUG)
+#define LOCK_DLM_DEBUG_SIZE     (4096)
+#define MAX_DEBUG_MSG_LEN       (80)
+#else
+#define LOCK_DLM_DEBUG_SIZE     (0)
+#define MAX_DEBUG_MSG_LEN       (0)
+#endif
+#define MAX_PROC_STRING		(16)
+
+int				lock_dlm_max_nodes;
+int				lock_dlm_drop_count;
+int				lock_dlm_drop_period;
+
+static char *                   debug_buf;
+static unsigned int             debug_size;
+static unsigned int             debug_point;
+static int                      debug_wrap;
+static spinlock_t               debug_lock;
+static struct proc_dir_entry *	proc_dir = NULL;
+static char			proc_str[MAX_PROC_STRING + 1];
+
+
+void lock_dlm_debug_log(const char *fmt, ...)
+{
+	va_list va;
+	int i, n, size, len;
+	char buf[MAX_DEBUG_MSG_LEN+1];
+
+	spin_lock(&debug_lock);
+
+	if (!debug_buf)
+		goto out;
+
+	size = MAX_DEBUG_MSG_LEN;
+	memset(buf, 0, size+1);
+
+	n = 0;
+	/* n = snprintf(buf, size, "%s ", dlm->fsname); */
+	n = snprintf(buf, size, "%u ", current->pid);
+	size -= n;
+
+	va_start(va, fmt);
+	vsnprintf(buf+n, size, fmt, va);
+	va_end(va);
+
+	len = strlen(buf);
+	if (len > MAX_DEBUG_MSG_LEN-1)
+		len = MAX_DEBUG_MSG_LEN-1;
+	buf[len] = '\n';
+	buf[len+1] = '\0';
+
+	for (i = 0; i < strlen(buf); i++) {
+		debug_buf[debug_point++] = buf[i];
+
+		if (debug_point == debug_size) {
+			debug_point = 0;
+			debug_wrap = 1;
+		}
+	}
+ out:
+	spin_unlock(&debug_lock);
+}
+
+static void debug_setup(int size)
+{
+	char *b = NULL;
+
+	if (size > PAGE_SIZE)
+		size = PAGE_SIZE;
+	if (size)
+		b = kmalloc(size, GFP_KERNEL);
+
+	spin_lock(&debug_lock);
+	if (debug_buf)
+		kfree(debug_buf);
+	if (!size || !b)
+		goto out;
+	debug_size = size;
+	debug_point = 0;
+	debug_wrap = 0;
+	debug_buf = b;
+	memset(debug_buf, 0, debug_size);
+ out:
+	spin_unlock(&debug_lock);
+}
+
+static void debug_init(void)
+{
+	debug_buf = NULL;
+	debug_size = 0;
+	debug_point = 0;
+	debug_wrap = 0;
+	spin_lock_init(&debug_lock);
+	debug_setup(LOCK_DLM_DEBUG_SIZE);
+}
+
+void lock_dlm_debug_dump(void)
+{
+	int i;
+
+	spin_lock(&debug_lock);
+
+	if (debug_wrap) {
+		for (i = debug_point; i < debug_size; i++)
+			printk("%c", debug_buf[i]);
+	}
+	for (i = 0; i < debug_point; i++)
+		printk("%c", debug_buf[i]);
+
+	spin_unlock(&debug_lock);
+}
+
+EXPORT_SYMBOL(lock_dlm_debug_dump);
+
+#ifdef CONFIG_PROC_FS
+static int debug_info(char *b, char **start, off_t offset, int length)
+{
+	int i, n = 0;
+
+	spin_lock(&debug_lock);
+
+	if (debug_wrap) {
+		for (i = debug_point; i < debug_size; i++)
+			n += sprintf(b + n, "%c", debug_buf[i]);
+	}
+	for (i = 0; i < debug_point; i++)
+		n += sprintf(b + n, "%c", debug_buf[i]);
+
+	spin_unlock(&debug_lock);
+
+	return n;
+}
+
+static int max_nodes_info(char *b, char **start, off_t offset, int length)
+{
+	return sprintf(b, "%d\n", lock_dlm_max_nodes);
+}
+
+static int drop_count_info(char *b, char **start, off_t offset, int length)
+{
+	return sprintf(b, "%d\n", lock_dlm_drop_count);
+}
+
+static int drop_period_info(char *b, char **start, off_t offset, int length)
+{
+	return sprintf(b, "%d\n", lock_dlm_drop_period);
+}
+
+static int copy_string(const char *buffer, unsigned long count)
+{
+	int len;
+
+	if (count > MAX_PROC_STRING)
+		len = MAX_PROC_STRING;
+	else
+		len = count;
+
+	if (copy_from_user(proc_str, buffer, len))
+		return -EFAULT;
+	proc_str[len] = '\0';
+	return len;
+}
+
+static int max_nodes_write(struct file *file, const char *buffer,
+			   unsigned long count, void *data)
+{
+	int rv = copy_string(buffer, count);
+	if (rv < 0)
+		return rv;
+	lock_dlm_max_nodes = (int) simple_strtol(proc_str, NULL, 0);
+	return rv;
+}
+
+static int drop_count_write(struct file *file, const char *buffer,
+			    unsigned long count, void *data)
+{
+	int rv = copy_string(buffer, count);
+	if (rv < 0)
+		return rv;
+	lock_dlm_drop_count = (int) simple_strtol(proc_str, NULL, 0);
+	return rv;
+}
+
+static int drop_period_write(struct file *file, const char *buffer,
+			    unsigned long count, void *data)
+{
+	int rv = copy_string(buffer, count);
+	if (rv < 0)
+		return rv;
+	lock_dlm_drop_period = (int) simple_strtol(proc_str, NULL, 0);
+	return rv;
+}
+
+static void create_proc_entries(void)
+{
+	struct proc_dir_entry *p, *debug, *drop_count, *drop_period, *max_nodes;
+
+	debug = drop_count = drop_period = max_nodes = NULL;
+
+	proc_dir = proc_mkdir("cluster/lock_dlm", 0);
+	if (!proc_dir)
+		return;
+	proc_dir->owner = THIS_MODULE;
+
+	p = create_proc_entry("max_nodes", 0666, proc_dir);
+	if (!p)
+		goto out;
+	p->owner = THIS_MODULE;
+	p->get_info = max_nodes_info;
+	p->write_proc = max_nodes_write;
+	max_nodes = p;
+
+	p = create_proc_entry("debug", 0444, proc_dir);
+	if (!p)
+		goto out;
+	p->get_info = debug_info;
+	p->owner = THIS_MODULE;
+	debug = p;
+
+	p = create_proc_entry("drop_count", 0666, proc_dir);
+	if (!p)
+		goto out;
+	p->owner = THIS_MODULE;
+	p->get_info = drop_count_info;
+	p->write_proc = drop_count_write;
+	drop_count = p;
+
+	p = create_proc_entry("drop_period", 0666, proc_dir);
+	if (!p)
+		goto out;
+	p->owner = THIS_MODULE;
+	p->get_info = drop_period_info;
+	p->write_proc = drop_period_write;
+	drop_period = p;
+
+	return;
+
+ out:
+	if (drop_period)
+		remove_proc_entry("drop_period", proc_dir);
+	if (drop_count)
+		remove_proc_entry("drop_count", proc_dir);
+	if (debug)
+		remove_proc_entry("debug", proc_dir);
+	if (max_nodes)
+		remove_proc_entry("max_nodes", proc_dir);
+
+	remove_proc_entry("cluster/lock_dlm", NULL);
+	proc_dir = NULL;
+}
+
+static void remove_proc_entries(void)
+{
+	if (proc_dir) {
+		remove_proc_entry("max_nodes", proc_dir);
+		remove_proc_entry("debug", proc_dir);
+		remove_proc_entry("drop_period", proc_dir);
+		remove_proc_entry("drop_count", proc_dir);
+		remove_proc_entry("cluster/lock_dlm", NULL);
+		proc_dir = NULL;
+	}
+}
+#endif
+
+/**
+ * init_dlm - Initialize the dlm module
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int __init init_lock_dlm(void)
+{
+	int error;
+
+	error = lm_register_proto(&lock_dlm_ops);
+	if (error) {
+		printk("lock_dlm:  can't register protocol: (%d)\n", error);
+		return error;
+	}
+
+	lock_dlm_max_nodes = LOCK_DLM_MAX_NODES;
+	lock_dlm_drop_count = DROP_LOCKS_COUNT;
+	lock_dlm_drop_period = DROP_LOCKS_PERIOD;
+
+#ifdef CONFIG_PROC_FS
+	create_proc_entries();
+#endif
+	debug_init();
+
+	printk("Lock_DLM (built %s %s) installed\n", __DATE__, __TIME__);
+	return 0;
+}
+
+/**
+ * exit_dlm - cleanup the dlm module
+ *
+ */
+
+void __exit exit_lock_dlm(void)
+{
+	lm_unregister_proto(&lock_dlm_ops);
+#ifdef CONFIG_PROC_FS
+	remove_proc_entries();
+#endif
+	debug_setup(0);
+}
+
+module_init(init_lock_dlm);
+module_exit(exit_lock_dlm);
+
+MODULE_DESCRIPTION("GFS DLM Locking Module");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_dlm/mount.c linux-2.6.9.debug/fs/gfs_locking/lock_dlm/mount.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_dlm/mount.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_dlm/mount.c	2006-12-20 17:07:48.000000000 +0300
@@ -0,0 +1,588 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/socket.h>
+#include <net/sock.h>
+#include <linux/delay.h>
+
+#include "lock_dlm.h"
+#include <cluster/cnxman.h>
+#include <cluster/service.h>
+
+extern int lock_dlm_max_nodes;
+extern int lock_dlm_drop_count;
+extern int lock_dlm_drop_period;
+
+
+static int init_cman(dlm_t *dlm)
+{
+	int error = -1;
+	char *name = NULL;
+
+	if (!dlm->clustername)
+		goto fail;
+
+	error = kcl_addref_cluster();
+	if (error) {
+		printk("lock_dlm: cannot get cman reference %d\n", error);
+		goto fail;
+	}
+
+	error = kcl_cluster_name(&name);
+	if (error) {
+		printk("lock_dlm: cannot get cman cluster name %d\n", error);
+		goto fail_ref;
+	}
+
+	if (strcmp(name, dlm->clustername)) {
+		error = -1;
+		printk("lock_dlm: cman cluster name \"%s\" does not match "
+		       "file system cluster name \"%s\"\n",
+		       name, dlm->clustername);
+		goto fail_ref;
+	}
+
+	kfree(name);
+	return 0;
+
+ fail_ref:
+	kcl_releaseref_cluster();
+ fail:
+	if (name)
+		kfree(name);
+	return error;
+}
+
+static int release_cman(dlm_t *dlm)
+{
+	return kcl_releaseref_cluster();
+}
+
+static int init_cluster(dlm_t *dlm, char *table_name)
+{
+	char *buf, *c, *clname, *fsname;
+	int len, error = -1;
+
+	/*  
+	 * Parse superblock lock table <clustername>:<fsname>  
+	 */
+
+	len = strlen(table_name) + 1;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf)
+		goto out;
+	memset(buf, 0, len);
+	memcpy(buf, table_name, strlen(table_name));
+
+	c = strstr(buf, ":");
+	if (!c)
+		goto out_buf;
+
+	*c = '\0';
+	clname = buf;
+	fsname = ++c;
+
+	dlm->max_nodes = lock_dlm_max_nodes;
+
+	len = strlen(clname) + 1;
+	c = kmalloc(len, GFP_KERNEL);
+	if (!c)
+		goto out_buf;
+	memset(c, 0, len);
+	memcpy(c, clname, len-1);
+	dlm->cnlen = len-1;
+	dlm->clustername = c;
+
+	len = strlen(fsname) + 1;
+	c = kmalloc(len, GFP_KERNEL);
+	if (!c)
+		goto out_cn;
+	memset(c, 0, len);
+	memcpy(c, fsname, len-1);
+	dlm->fnlen = len-1;
+	dlm->fsname = c;
+
+	error = init_cman(dlm);
+	if (error)
+		goto out_fn;
+
+	kfree(buf);
+	return 0;
+
+ out_fn:
+	kfree(dlm->fsname);
+ out_cn:
+	kfree(dlm->clustername);
+ out_buf:
+	kfree(buf);
+ out:
+	printk("lock_dlm: init_cluster error %d\n", error);
+	return error;
+}
+
+static int release_cluster(dlm_t *dlm)
+{
+	release_cman(dlm);
+	kfree(dlm->clustername);
+	kfree(dlm->fsname);
+	return 0;
+}
+
+static int init_fence(dlm_t *dlm)
+{
+	LIST_HEAD(head);
+	struct kcl_service *s, *safe;
+	int error, found = FALSE;
+
+	error = kcl_get_services(&head, SERVICE_LEVEL_FENCE);
+	if (error < 0)
+		goto out;
+
+	list_for_each_entry_safe(s, safe, &head, list) {
+		list_del(&s->list);
+		if (!found && !strcmp(s->name, "default"))
+			found = TRUE;
+		kfree(s);
+	}
+
+	if (found)
+		return 0;
+
+	error = -1;
+ out:
+	printk("lock_dlm: fence domain not found; check fenced\n");
+	return error;
+}
+
+static int release_fence(dlm_t *dlm)
+{
+	return 0;
+}
+
+static int init_gdlm(dlm_t *dlm)
+{
+	int error;
+
+	error = dlm_new_lockspace(dlm->fsname, dlm->fnlen, &dlm->gdlm_lsp,
+				   DLM_LSF_NOTIMERS);
+	if (error)
+		printk("lock_dlm: new lockspace error %d\n", error);
+
+	return error;
+}
+
+static int release_gdlm(dlm_t *dlm)
+{
+	dlm_release_lockspace(dlm->gdlm_lsp, 2);
+	return 0;
+}
+
+static dlm_t *init_dlm(lm_callback_t cb, lm_fsdata_t *fsdata)
+{
+	dlm_t *dlm;
+
+	dlm = kmalloc(sizeof(dlm_t), GFP_KERNEL);
+	if (!dlm)
+		return NULL;
+
+	memset(dlm, 0, sizeof(dlm_t));
+
+	dlm->drop_locks_count = lock_dlm_drop_count;
+	dlm->drop_locks_period = lock_dlm_drop_period;
+
+	dlm->fscb = cb;
+	dlm->fsdata = fsdata;
+
+	spin_lock_init(&dlm->async_lock);
+
+	INIT_LIST_HEAD(&dlm->complete);
+	INIT_LIST_HEAD(&dlm->blocking);
+	INIT_LIST_HEAD(&dlm->delayed);
+	INIT_LIST_HEAD(&dlm->submit);
+	INIT_LIST_HEAD(&dlm->starts);
+	INIT_LIST_HEAD(&dlm->resources);
+	INIT_LIST_HEAD(&dlm->null_cache);
+
+	init_waitqueue_head(&dlm->wait);
+	dlm->thread1 = NULL;
+	dlm->thread2 = NULL;
+	atomic_set(&dlm->lock_count, 0);
+	dlm->drop_time = jiffies;
+	dlm->shrink_time = jiffies;
+
+	INIT_LIST_HEAD(&dlm->mg_nodes);
+	init_MUTEX(&dlm->mg_nodes_lock);
+	init_MUTEX(&dlm->unmount_lock);
+	init_MUTEX(&dlm->res_lock);
+
+	dlm->null_count = 0;
+	spin_lock_init(&dlm->null_cache_spin);
+
+	return dlm;
+}
+
+/**
+ * dlm_mount - mount a dlm lockspace
+ * @table_name: the name of the space to mount
+ * @host_data: host specific data
+ * @cb: the callback
+ * @lockstruct: the structure of crap to fill in
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+static int lm_dlm_mount(char *table_name, char *host_data,
+			lm_callback_t cb, lm_fsdata_t *fsdata,
+			unsigned int min_lvb_size,
+			struct lm_lockstruct *lockstruct)
+{
+	dlm_t *dlm;
+	int error = -ENOMEM;
+
+	if (min_lvb_size > DLM_LVB_SIZE)
+		goto out;
+
+	dlm = init_dlm(cb, fsdata);
+	if (!dlm)
+		goto out;
+
+	error = init_cluster(dlm, table_name);
+	if (error)
+		goto out_free;
+
+	error = init_fence(dlm);
+	if (error)
+		goto out_cluster;
+
+	error = init_gdlm(dlm);
+	if (error)
+		goto out_fence;
+
+	error = init_async_thread(dlm);
+	if (error)
+		goto out_gdlm;
+
+	error = init_mountgroup(dlm);
+	if (error)
+		goto out_thread;
+
+	lockstruct->ls_jid = dlm->jid;
+	lockstruct->ls_first = test_bit(DFL_FIRST_MOUNT, &dlm->flags);
+	lockstruct->ls_lockspace = dlm;
+	lockstruct->ls_ops = &lock_dlm_ops;
+	lockstruct->ls_flags = 0;
+	lockstruct->ls_lvb_size = DLM_LVB_SIZE;
+	return 0;
+
+ out_thread:
+	release_async_thread(dlm);
+ out_gdlm:
+	release_gdlm(dlm);
+ out_fence:
+	release_fence(dlm);
+ out_cluster:
+	release_cluster(dlm);
+ out_free:
+	kfree(dlm);
+ out:
+	return error;
+}
+
+/**
+ * dlm_others_may_mount
+ * @lockspace: the lockspace to unmount
+ *
+ */
+
+static void lm_dlm_others_may_mount(lm_lockspace_t *lockspace)
+{
+	dlm_t *dlm = (dlm_t *) lockspace;
+	int last_start;
+
+	log_debug("others_may_mount %lx", dlm->flags);
+
+	if (!test_bit(DFL_FIRST_MOUNT, &dlm->flags)) {
+		log_all("others_may_mount not first mounter %lx", dlm->flags);
+		return;
+	}
+
+	spin_lock(&dlm->async_lock);
+	last_start = dlm->mg_last_start;
+	spin_unlock(&dlm->async_lock);
+
+	down(&dlm->unmount_lock);
+	set_bit(DFL_OTHERSMAYMOUNT, &dlm->flags);
+
+	/* There's been a start to add a second node while we've been
+	   doing first-mount recovery.  We skipped the kcl_start_done() when
+	   processing the start to keep the second node from mounting until we
+	   allow others.  Now we can ack the start and allow the second node be
+	   complete its mount. */
+
+	if (test_bit(DFL_WAIT_OTHERSMAYMOUNT, &dlm->flags)) {
+		log_debug("others_may_mount start_done %d %lx",
+			  last_start, dlm->flags);
+		kcl_start_done(dlm->mg_local_id, last_start);
+	}
+	up(&dlm->unmount_lock);
+}
+
+/**
+ * dlm_unmount - unmount a lock space
+ * @lockspace: the lockspace to unmount
+ *
+ */
+
+static void lm_dlm_unmount(lm_lockspace_t *lockspace)
+{
+	dlm_t *dlm = (dlm_t *) lockspace;
+
+	log_debug("unmount flags %lx", dlm->flags);
+	if (test_bit(DFL_WITHDRAW, &dlm->flags))
+		goto out;
+	release_mountgroup(dlm);
+	release_async_thread(dlm);
+	release_gdlm(dlm);
+	release_fence(dlm);
+	release_cluster(dlm);
+	clear_null_cache(dlm);
+ out:
+	kfree(dlm);
+}
+
+static void wd_ast(void *arg)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) arg;
+	complete(&lp->uast_wait);
+}
+
+static void wd_bast(void *arg, int mode)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) arg;
+	dlm_t *dlm = lp->dlm;
+	dlm_node_t *node;
+	int error;
+
+	if (lp->cur == DLM_LOCK_NL) {
+		log_all("withdraw bast cur NL arg %d", mode);
+		return;
+	}
+
+	if (lp->cur != DLM_LOCK_PR) {
+		log_all("withdraw bast cur %d arg %d", lp->cur, mode);
+		return;
+	}
+
+	if (mode != DLM_LOCK_EX) {
+		log_all("withdraw bast cur %d arg %d", lp->cur, mode);
+		return;
+	}
+
+	set_bit(DFL_BLOCK_LOCKS, &dlm->flags);
+
+	down(&dlm->mg_nodes_lock);
+	list_for_each_entry(node, &dlm->mg_nodes, list) {
+		if (node->withdraw_lp == lp) {
+			log_debug("wd_bast node %d withdraw", node->nodeid);
+			set_bit(NFL_WITHDRAW, &node->flags);
+			break;
+		}
+	}
+	up(&dlm->mg_nodes_lock);
+
+	set_bit(LFL_UNLOCK_DELETE, &lp->flags);
+
+	error = dlm_unlock(dlm->gdlm_lsp, lp->lksb.sb_lkid, 0, NULL, lp);
+
+	DLM_ASSERT(!error, printk("error %d\n", error););
+
+	node->withdraw_lp = NULL;
+}
+
+void lm_dlm_hold_withdraw(dlm_t *dlm)
+{
+	char name[16];
+	dlm_node_t *node;
+	dlm_lock_t *lp;
+	int error;
+
+	down(&dlm->mg_nodes_lock);
+	list_for_each_entry(node, &dlm->mg_nodes, list) {
+		if (test_bit(NFL_WITHDRAW, &node->flags))
+			continue;
+
+		lp = node->withdraw_lp;
+
+		/* if we have the lp it should always be in PR */
+		if (lp) {
+			if (lp->cur != DLM_LOCK_PR)
+				log_all("hold_withdraw cur %d", lp->cur);
+			continue;
+		}
+
+		lp = kmalloc(sizeof(dlm_lock_t), GFP_KERNEL);
+		if (!lp)
+			continue;
+		memset(lp, 0, sizeof(dlm_lock_t));
+		init_completion(&lp->uast_wait);
+		lp->dlm = dlm;
+		node->withdraw_lp = lp;
+
+		memset(name, 0, sizeof(name));
+		snprintf(name, sizeof(name), "withdraw %u", node->nodeid);
+
+		error = dlm_lock(dlm->gdlm_lsp, DLM_LOCK_PR, &lp->lksb,
+				 DLM_LKF_NOQUEUE, name, sizeof(name), 0,
+				 wd_ast, (void *) lp, wd_bast, NULL);
+
+		DLM_ASSERT(!error, printk("error %d\n", error););
+
+		wait_for_completion(&lp->uast_wait);
+
+		DLM_ASSERT(lp->lksb.sb_status == 0,
+			   printk("status %d\n", lp->lksb.sb_status););
+
+		lp->cur = DLM_LOCK_PR;
+	}
+	up(&dlm->mg_nodes_lock);
+}
+
+static void do_withdraw(dlm_t *dlm)
+{
+	char name[16];
+	dlm_node_t *node;
+	dlm_lock_t *lp;
+	int error;
+
+	down(&dlm->mg_nodes_lock);
+	list_for_each_entry(node, &dlm->mg_nodes, list) {
+		if (node->nodeid == dlm->our_nodeid)
+			break;
+	}
+	up(&dlm->mg_nodes_lock);
+
+	if (!node) {
+		log_all("node not found for %d", dlm->our_nodeid);
+		return;
+	}
+
+	lp = node->withdraw_lp;
+	if (!lp) {
+		log_all("no withdraw lock for self");
+		return;
+	}
+
+	if (lp->cur != DLM_LOCK_PR) {
+		log_all("our withdraw lock in mode %d", lp->cur);
+		return;
+	}
+
+	log_debug("do_withdraw");
+	memset(name, 0, sizeof(name));
+	snprintf(name, sizeof(name), "withdraw %u", dlm->our_nodeid);
+
+	error = dlm_lock(dlm->gdlm_lsp, DLM_LOCK_EX, &lp->lksb,
+			 DLM_LKF_CONVERT, name, sizeof(name), 0, wd_ast,
+			 (void *) lp, wd_bast, NULL);
+
+	DLM_ASSERT(!error, printk("error %d\n", error););
+
+	wait_for_completion(&lp->uast_wait);
+
+	DLM_ASSERT(lp->lksb.sb_status == 0,
+		   printk("status %d\n", lp->lksb.sb_status););
+
+	lp->cur = DLM_LOCK_EX;
+}
+
+/* Release the withdraw lock for this node.  If the node was removed because it
+   withdrew, then we already released the lock (in wd_bast).  If the node has
+   failed or unmounted, then we still hold its withdraw lock and need to unlock
+   it.  If _we're_ withdrawing, then we've already left the lockspace so we
+   don't unlock, just free. */
+
+void lm_dlm_release_withdraw(dlm_t *dlm, dlm_node_t *node)
+{
+	dlm_lock_t *lp;
+	int error;
+
+	lp = node->withdraw_lp;
+	if (!lp)
+		return;
+
+	if (test_bit(DFL_WITHDRAW, &dlm->flags)) {
+		kfree(lp);
+		goto out;
+	}
+
+	/* the lp is freed by the async thread when it gets the comp ast */
+	set_bit(LFL_UNLOCK_DELETE, &lp->flags);
+
+	error = dlm_unlock(dlm->gdlm_lsp, lp->lksb.sb_lkid, 0, NULL, lp);
+
+	DLM_ASSERT(!error, printk("error %d\n", error););
+
+ out:
+	node->withdraw_lp = NULL;
+}
+
+/**
+ * dlm_withdraw - withdraw from a lock space
+ * @lockspace: the lockspace to withdraw from
+ *
+ * Holding the withdraw lock in EX means all gfs locks are blocked on other
+ * nodes and we can safely leave the lockspace.
+ *
+ */
+
+static void lm_dlm_withdraw(lm_lockspace_t *lockspace)
+{
+	dlm_t *dlm = (dlm_t *) lockspace;
+
+	log_debug("withdraw flags %lx", dlm->flags);
+	set_bit(DFL_WITHDRAW, &dlm->flags);
+
+	/* process_start uses the dlm so leaving the ls while it's running
+	   can hang it; this waits for it to complete. */
+	down(&dlm->unmount_lock);
+	up(&dlm->unmount_lock);
+
+	do_withdraw(dlm);
+	release_gdlm(dlm);
+	release_mountgroup(dlm);
+	release_cluster(dlm);
+
+	/* FIXME: free all outstanding memory */
+	log_all("withdraw abandoned memory");
+}
+
+struct lm_lockops lock_dlm_ops = {
+	lm_proto_name:"lock_dlm",
+	lm_mount:lm_dlm_mount,
+	lm_others_may_mount:lm_dlm_others_may_mount,
+	lm_unmount:lm_dlm_unmount,
+	lm_withdraw:lm_dlm_withdraw,
+	lm_get_lock:lm_dlm_get_lock,
+	lm_put_lock:lm_dlm_put_lock,
+	lm_lock:lm_dlm_lock,
+	lm_unlock:lm_dlm_unlock,
+	lm_plock:lm_dlm_plock,
+	lm_punlock:lm_dlm_punlock,
+	lm_plock_get:lm_dlm_plock_get,
+	lm_cancel:lm_dlm_cancel,
+	lm_hold_lvb:lm_dlm_hold_lvb,
+	lm_unhold_lvb:lm_dlm_unhold_lvb,
+	lm_sync_lvb:lm_dlm_sync_lvb,
+	lm_recovery_done:lm_dlm_recovery_done,
+	lm_owner:THIS_MODULE,
+};
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_dlm/plock.c linux-2.6.9.debug/fs/gfs_locking/lock_dlm/plock.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_dlm/plock.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_dlm/plock.c	2006-12-20 17:07:48.000000000 +0300
@@ -0,0 +1,1254 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "lock_dlm.h"
+#include <linux/fcntl.h>
+
+#define MIN(a,b) ((a) <= (b)) ? (a) : (b)
+#define MAX(a,b) ((a) >= (b)) ? (a) : (b)
+
+#define CREATE    1
+#define NO_CREATE 0
+
+#define WAIT      1
+#define NO_WAIT   0
+#define X_WAIT   -1
+
+#define EX        1
+#define NO_EX     0
+#define SH        NO_EX
+
+#define HEAD      1
+
+static int local_conflict(dlm_t *dlm, struct dlm_resource *r,
+			  struct lm_lockname *name, unsigned long owner,
+			  uint64_t start, uint64_t end, int ex);
+
+static int global_conflict(dlm_t *dlm, struct lm_lockname *name,
+			   unsigned long owner, uint64_t start, uint64_t end,
+			   int ex);
+
+/* remove lru lp from end of list, null_cache_spin must be held */
+
+static dlm_lock_t *lru_null(dlm_t *dlm)
+{
+	dlm_lock_t *lp;
+
+	lp = list_entry(dlm->null_cache.next, dlm_lock_t, null_list);
+	list_del(&lp->null_list);
+	dlm->null_count--;
+
+	return lp;
+}
+
+/* It's important that the lock_dlm thread not block doing any synchronous
+   dlm operations because a recovery event (which makes sync requests) can
+   happen during this.  If both lock_dlm threads do sync requests they deadlock
+   since one is required to process asts.  We also break out early if there's a
+   recovery so it doesn't have to wait. */
+
+void shrink_null_cache(dlm_t *dlm)
+{
+	dlm_lock_t *lp;
+
+	while (1) {
+		spin_lock(&dlm->null_cache_spin);
+		if (dlm->null_count <= SHRINK_CACHE_COUNT ||
+		    test_bit(DFL_RECOVER, &dlm->flags)) {
+			spin_unlock(&dlm->null_cache_spin);
+			break;
+		}
+
+		lp = lru_null(dlm);
+		spin_unlock(&dlm->null_cache_spin);
+
+		set_bit(LFL_UNLOCK_DELETE, &lp->flags);
+		do_dlm_unlock(lp);
+		schedule();
+	}
+}
+
+void clear_null_cache(dlm_t *dlm)
+{
+	dlm_lock_t *lp, *safe;
+
+	spin_lock(&dlm->null_cache_spin);
+	list_for_each_entry_safe(lp, safe, &dlm->null_cache, null_list) {
+		list_del(&lp->null_list);
+		dlm->null_count--;
+		delete_lp(lp);
+	}
+	spin_unlock(&dlm->null_cache_spin);
+}
+
+static void keep_null_lock(dlm_t *dlm, dlm_lock_t *lp)
+{
+	dlm_lock_t *lp2 = NULL;
+
+	spin_lock(&dlm->null_cache_spin);
+	/* add to front of list wrt list_add_tail/list_for_each */
+	list_add_tail(&lp->null_list, &dlm->null_cache);
+	dlm->null_count++;
+
+	/* help to shrink cache if too many null locks are piling up */
+	if (dlm->null_count > SHRINK_CACHE_MAX)
+		lp2 = lru_null(dlm);
+	spin_unlock(&dlm->null_cache_spin);
+
+	if (lp2) {
+		set_bit(LFL_UNLOCK_DELETE, &lp2->flags);
+		do_dlm_unlock(lp2);
+	}
+}
+
+static dlm_lock_t *find_null_lock(dlm_t *dlm, struct lm_lockname *name)
+{
+	dlm_lock_t *lp;
+	int found = FALSE;
+
+	spin_lock(&dlm->null_cache_spin);
+	list_for_each_entry(lp, &dlm->null_cache, null_list) {
+		if (lm_name_equal(&lp->lockname, name)) {
+			list_del(&lp->null_list);
+			dlm->null_count--;
+			found = TRUE;
+			break;
+		}
+	}
+	spin_unlock(&dlm->null_cache_spin);
+
+	if (!found)
+		lp = NULL;
+	return lp;
+}
+
+static int lock_resource(struct dlm_resource *r)
+{
+	dlm_lock_t *lp;
+	struct lm_lockname name;
+	int error;
+
+	name.ln_type = LM_TYPE_PLOCK_UPDATE;
+	name.ln_number = r->name.ln_number;
+
+	lp = find_null_lock(r->dlm, &name);
+	if (!lp) {
+		error = create_lp(r->dlm, &name, &lp);
+		if (error)
+			return error;
+		set_bit(LFL_NOBAST, &lp->flags);
+		set_bit(LFL_INLOCK, &lp->flags);
+	} else
+		lp->lkf = DLM_LKF_CONVERT;
+
+	lp->req = DLM_LOCK_EX;
+	error = do_dlm_lock_sync(lp, NULL);
+	if (error) {
+		delete_lp(lp);
+		lp = NULL;
+	}
+
+	r->update = lp;
+	return error;
+}
+
+static void unlock_resource(struct dlm_resource *r)
+{
+	dlm_lock_t *lp = r->update;
+
+	set_bit(LFL_NOBAST, &lp->flags);
+	set_bit(LFL_INLOCK, &lp->flags);
+	lp->req = DLM_LOCK_NL;
+	lp->lkf = DLM_LKF_CONVERT;
+	do_dlm_lock_sync(lp, NULL);
+	keep_null_lock(r->dlm, lp);
+	r->update = NULL;
+}
+
+static struct dlm_resource *search_resource(dlm_t *dlm, struct lm_lockname *name)
+{
+	struct dlm_resource *r;
+
+	list_for_each_entry(r, &dlm->resources, list) {
+		if (lm_name_equal(&r->name, name))
+			return r;
+	}
+	return NULL;
+}
+
+static int get_resource(dlm_t *dlm, struct lm_lockname *name, int create,
+			struct dlm_resource **res)
+{
+	struct dlm_resource *r, *r2;
+	int error = -ENOMEM;
+
+	down(&dlm->res_lock);
+	r = search_resource(dlm, name);
+	if (r)
+		r->count++;
+	up(&dlm->res_lock);
+
+	if (r)
+		goto out;
+
+	if (create == NO_CREATE) {
+		error = -ENOENT;
+		goto fail;
+	}
+
+	r = kmalloc(sizeof(struct dlm_resource), GFP_KERNEL);
+	if (!r)
+		goto fail;
+
+	memset(r, 0, sizeof(struct dlm_resource));
+	r->dlm = dlm;
+	r->name = *name;
+	r->count = 1;
+	INIT_LIST_HEAD(&r->locks);
+	INIT_LIST_HEAD(&r->async_locks);
+	init_MUTEX(&r->sema);
+	spin_lock_init(&r->async_spin);
+	init_waitqueue_head(&r->waiters);
+
+	down(&dlm->res_lock);
+	r2 = search_resource(dlm, name);
+	if (r2) {
+		r2->count++;
+		up(&dlm->res_lock);
+		kfree(r);
+		r = r2;
+		goto out;
+	}
+
+	list_add_tail(&r->list, &dlm->resources);
+	up(&dlm->res_lock);
+
+ out:
+	*res = r;
+	return 0;
+ fail:
+	return error;
+}
+
+static void put_resource(struct dlm_resource *r)
+{
+	dlm_t *dlm = r->dlm;
+
+	down(&dlm->res_lock);
+	r->count--;
+	if (r->count == 0) {
+		DLM_ASSERT(list_empty(&r->locks), );
+		DLM_ASSERT(list_empty(&r->async_locks), );
+		list_del(&r->list);
+		kfree(r);
+	}
+	up(&dlm->res_lock);
+}
+
+static inline void hold_resource(struct dlm_resource *r)
+{
+	down(&r->dlm->res_lock);
+	r->count++;
+	up(&r->dlm->res_lock);
+}
+
+static inline int ranges_overlap(uint64_t start1, uint64_t end1,
+				 uint64_t start2, uint64_t end2)
+{
+	if (end1 < start2 || start1 > end2)
+		return FALSE;
+	return TRUE;
+}
+
+/**
+ * overlap_type - returns a value based on the type of overlap
+ * @s1 - start of new lock range
+ * @e1 - end of new lock range
+ * @s2 - start of existing lock range
+ * @e2 - end of existing lock range
+ *
+ */
+
+static int overlap_type(uint64_t s1, uint64_t e1, uint64_t s2, uint64_t e2)
+{
+	int ret;
+
+	/*
+	 * ---r1---
+	 * ---r2---
+	 */
+
+	if (s1 == s2 && e1 == e2)
+		ret = 0;
+
+	/*
+	 * --r1--
+	 * ---r2---
+	 */
+
+	else if (s1 == s2 && e1 < e2)
+		ret = 1;
+
+	/*
+	 *   --r1--
+	 * ---r2---
+	 */
+
+	else if (s1 > s2 && e1 == e2)
+		ret = 1;
+
+	/*
+	 *  --r1--
+	 * ---r2---
+	 */
+
+	else if (s1 > s2 && e1 < e2)
+		ret = 2;
+
+	/*
+	 * ---r1---  or  ---r1---  or  ---r1---
+	 * --r2--          --r2--       --r2--
+	 */
+
+	else if (s1 <= s2 && e1 >= e2)
+		ret = 3;
+
+	/*
+	 *   ---r1---
+	 * ---r2---
+	 */
+
+	else if (s1 > s2 && e1 > e2)
+		ret = 4;
+
+	/*
+	 * ---r1---
+	 *   ---r2---
+	 */
+
+	else if (s1 < s2 && e1 < e2)
+		ret = 4;
+
+	else
+		ret = -1;
+
+	return ret;
+}
+
+/* shrink the range start2:end2 by the partially overlapping start:end */
+
+static int shrink_range2(uint64_t *start2, uint64_t *end2,
+			 uint64_t start, uint64_t end)
+{
+	int error = 0;
+
+	if (*start2 < start)
+		*end2 = start - 1;
+	else if (*end2 > end)
+		*start2 =  end + 1;
+	else
+		error = -1;
+	return error;
+}
+
+static int shrink_range(struct posix_lock *po, uint64_t start, uint64_t end)
+{
+	return shrink_range2(&po->start, &po->end, start, end);
+}
+
+static void put_lock(dlm_lock_t *lp)
+{
+	struct posix_lock *po = lp->posix;
+
+	po->count--;
+	if (po->count == 0) {
+		kfree(po);
+		delete_lp(lp);
+	}
+}
+
+static int create_lock(struct dlm_resource *r, unsigned long owner, 
+		       unsigned int pid, int ex, uint64_t start, 
+		       uint64_t end, dlm_lock_t **lpp)
+{
+	dlm_lock_t *lp;
+	struct posix_lock *po;
+	int error;
+
+	error = create_lp(r->dlm, &r->name, &lp);
+	if (error)
+		return error;
+
+	po = kmalloc(sizeof(struct posix_lock), GFP_KERNEL);
+	if (!po) {
+		kfree(lp);
+		return -ENOMEM;
+	}
+	memset(po, 0, sizeof(struct posix_lock));
+
+	lp->posix = po;
+	po->lp = lp;
+	po->resource = r;
+	po->count = 1;
+	po->start = start;
+	po->end = end;
+	po->owner = owner;
+	po->pid = pid;
+	po->ex = ex;
+	list_add_tail(&po->list, &r->locks);
+
+	*lpp = lp;
+	return 0;
+}
+
+static unsigned int make_flags_posix(dlm_lock_t *lp, int wait)
+{
+	unsigned int lkf = DLM_LKF_NOORDER;
+
+	if (test_and_clear_bit(LFL_HEADQUE, &lp->flags))
+		lkf |= DLM_LKF_HEADQUE;
+
+	if (wait == NO_WAIT || wait == X_WAIT)
+		lkf |= DLM_LKF_NOQUEUE;
+
+	if (lp->lksb.sb_lkid != 0)
+		lkf |= DLM_LKF_CONVERT;
+
+	return lkf;
+}
+
+static void do_range_lock(dlm_lock_t *lp)
+{
+	struct dlm_range range = { lp->posix->start, lp->posix->end };
+	do_dlm_lock(lp, &range);
+}
+
+static void request_lock(dlm_lock_t *lp, int wait)
+{
+	set_bit(LFL_INLOCK, &lp->flags);
+	lp->req = lp->posix->ex ? DLM_LOCK_EX : DLM_LOCK_PR;
+	lp->lkf = make_flags_posix(lp, wait);
+
+	log_debug("req %x,%"PRIx64" %s %"PRIx64"-%"PRIx64" lkf %x wait %u",
+		  lp->lockname.ln_type, lp->lockname.ln_number,
+		  lp->posix->ex ? "ex" : "sh", lp->posix->start,
+		  lp->posix->end, lp->lkf, wait);
+
+	do_range_lock(lp);
+}
+
+static void add_async(struct posix_lock *po, struct dlm_resource *r)
+{
+	spin_lock(&r->async_spin);
+	list_add_tail(&po->async_list, &r->async_locks);
+	spin_unlock(&r->async_spin);
+}
+
+static void del_async(struct posix_lock *po, struct dlm_resource *r)
+{
+	spin_lock(&r->async_spin);
+	list_del(&po->async_list);
+	spin_unlock(&r->async_spin);
+}
+
+static int wait_async(dlm_lock_t *lp)
+{
+	wait_for_completion(&lp->uast_wait);
+	del_async(lp->posix, lp->posix->resource);
+	return lp->lksb.sb_status;
+}
+
+static void wait_async_list(struct dlm_resource *r, unsigned long owner)
+{
+	struct posix_lock *po;
+	int error, found;
+
+ restart:
+	found = FALSE;
+	spin_lock(&r->async_spin);
+	list_for_each_entry(po, &r->async_locks, async_list) {
+		if (po->owner != owner)
+			continue;
+		found = TRUE;
+		break;
+	}
+	spin_unlock(&r->async_spin);
+
+	if (found) {
+		DLM_ASSERT(po->lp, );
+		error = wait_async(po->lp);
+		DLM_ASSERT(!error, );
+		goto restart;
+	}
+}
+
+static void update_lock(dlm_lock_t *lp, int wait)
+{
+	request_lock(lp, wait);
+	add_async(lp->posix, lp->posix->resource);
+
+	if (wait == NO_WAIT || wait == X_WAIT) {
+		int error = wait_async(lp);
+		DLM_ASSERT(!error, printk("error=%d\n", error););
+	}
+}
+
+static void add_lock(struct dlm_resource *r, unsigned long owner, 
+		     unsigned int pid, int wait, int ex, uint64_t start, 
+		     uint64_t end, int head)
+{
+	dlm_lock_t *lp;
+	int error;
+
+	error = create_lock(r, owner, pid, ex, start, end, &lp);
+	DLM_ASSERT(!error, );
+	if (head == HEAD)
+		set_bit(LFL_HEADQUE, &lp->flags);
+
+	hold_resource(r);
+	update_lock(lp, wait);
+}
+
+static int remove_lock(dlm_lock_t *lp)
+{
+	struct dlm_resource *r = lp->posix->resource;
+
+	log_debug("remove %x,%"PRIx64"", r->name.ln_type, r->name.ln_number);
+
+	do_dlm_unlock_sync(lp);
+	put_lock(lp);
+	put_resource(r);
+	return 0;
+}
+
+/* RN within RE (and starts or ends on RE boundary)
+   1. add new lock for non-overlap area of RE, orig mode
+   2. convert RE to RN range and mode */
+
+static int lock_case1(struct posix_lock *po, struct dlm_resource *r,
+		      unsigned long owner, unsigned int pid, int wait, 
+		      int ex, uint64_t start, uint64_t end)
+{
+	uint64_t start2, end2;
+
+	/* non-overlapping area start2:end2 */
+	start2 = po->start;
+	end2 = po->end;
+	shrink_range2(&start2, &end2, start, end);
+
+	po->start = start;
+	po->end = end;
+	po->ex = ex;
+
+	if (ex) {
+		add_lock(r, owner, pid, X_WAIT, SH, start2, end2, HEAD);
+		update_lock(po->lp, wait);
+	} else {
+		add_lock(r, owner, pid, WAIT, EX, start2, end2, HEAD);
+		update_lock(po->lp, X_WAIT);
+	}
+	return 0;
+}
+
+/* RN within RE (RE overlaps RN on both sides)
+   1. add new lock for front fragment, orig mode
+   2. add new lock for back fragment, orig mode
+   3. convert RE to RN range and mode */
+			 
+static int lock_case2(struct posix_lock *po, struct dlm_resource *r,
+		      unsigned long owner, unsigned int pid, int wait, 
+		      int ex, uint64_t start, uint64_t end)
+{
+	if (ex) {
+		add_lock(r, owner, pid, X_WAIT, SH, po->start, start-1, HEAD);
+		add_lock(r, owner, pid, X_WAIT, SH, end+1, po->end, HEAD);
+
+		po->start = start;
+		po->end = end;
+		po->ex = ex;
+
+		update_lock(po->lp, wait);
+	} else {
+		add_lock(r, owner, pid, WAIT, EX, po->start, start-1, HEAD);
+		add_lock(r, owner, pid, WAIT, EX, end+1, po->end, HEAD);
+
+		po->start = start;
+		po->end = end;
+		po->ex = ex;
+
+		update_lock(po->lp, X_WAIT);
+	}
+	return 0;
+}
+
+/* returns ranges from exist list in order of their start values */
+
+static int next_exist(struct list_head *exist, uint64_t *start, uint64_t *end)
+{
+	struct posix_lock *po;
+	int first = TRUE, first_call = FALSE;
+
+	if (!*start && !*end)
+		first_call = TRUE;
+
+	list_for_each_entry(po, exist, list) {
+		if (!first_call && (po->start <= *start))
+			continue;
+
+		if (first) {
+			*start = po->start;
+			*end = po->end;
+			first = FALSE;
+		} else if (po->start < *start) {
+			*start = po->start;
+			*end = po->end;
+		}
+	}
+
+	return (first ? -1 : 0);
+}
+
+/* adds locks in gaps between existing locks from start to end */
+
+static int fill_gaps(struct list_head *exist, struct dlm_resource *r,
+		     unsigned long owner, unsigned int pid, int wait, int ex, uint64_t start,
+		     uint64_t end)
+{
+	uint64_t exist_start = 0, exist_end = 0;
+
+	/* cover gaps in front of each existing lock */
+	for (;;) {
+		if (next_exist(exist, &exist_start, &exist_end))
+			break;
+		if (start < exist_start)
+			add_lock(r, owner, pid, wait, ex, start, exist_start-1, 0);
+		start = exist_end + 1;
+	}
+
+	/* cover gap after last existing lock */
+	if (exist_end < end)
+		add_lock(r, owner, pid, wait, ex, exist_end+1, end, 0);
+
+	return 0;
+}
+
+/* RE within RN (possibly more than one RE lock, all within RN) */
+
+static int lock_case3(struct list_head *exist, struct dlm_resource *r,
+		      unsigned long owner, unsigned int pid, int wait, 
+		      int ex, uint64_t start, uint64_t end)
+{
+	struct posix_lock *po, *safe;
+
+	fill_gaps(exist, r, owner, pid, wait, ex, start, end);
+
+	if (!ex)
+		wait = X_WAIT;
+
+	/* update existing locks to new mode and put back in locks list */
+	list_for_each_entry_safe(po, safe, exist, list) {
+		list_move_tail(&po->list, &r->locks);
+		if (po->ex == ex)
+			continue;
+		po->ex = ex;
+		update_lock(po->lp, wait);
+	}
+
+	return 0;
+}
+
+/* RE within RN (possibly more than one RE lock, one RE partially overlaps RN)
+   1. add new locks with new mode for RN gaps not covered by RE's
+   2. convert RE locks' mode to new mode
+   other steps deal with the partial-overlap fragment and depend on whether
+   the request is sh->ex or ex->sh */
+
+static int lock_case4(struct posix_lock *opo, struct list_head *exist,
+		      struct dlm_resource *r, unsigned long owner, 
+		      unsigned int pid, int wait, int ex, uint64_t start, 
+		      uint64_t end)
+{
+	struct posix_lock *po, *safe;
+	uint64_t over_start = 0, over_end = 0;
+	uint64_t frag_start = 0, frag_end = 0;
+
+	/* fragment (non-overlap) range of opo */
+	if (opo->start < start) {
+		frag_start = opo->start;
+		frag_end = start - 1;
+	} else {
+		frag_start = end + 1;
+		frag_end = opo->end;
+	}
+
+	/* overlap range of opo */
+	if (opo->start < start) {
+		over_start = start;
+		over_end = opo->end;
+	} else {
+		over_start = opo->start;
+		opo->end = end;
+	}
+
+	/* cut off the non-overlap portion of opo so fill_gaps will work */
+	opo->start = over_start;
+	opo->end = over_end;
+
+	fill_gaps(exist, r, owner, pid, wait, ex, start, end);
+
+	/* update existing locks to new mode and put back in locks list */
+	list_for_each_entry_safe(po, safe, exist, list) {
+		list_move_tail(&po->list, &r->locks);
+		if (po == opo)
+			continue;
+		if (po->ex == ex)
+			continue;
+		po->ex = ex;
+		update_lock(po->lp, wait);
+	}
+
+	/* deal with the RE that partially overlaps the requested range */
+
+	if (ex == opo->ex)
+		return 0;
+
+	if (ex) {
+		/* 1. add a shared lock in the non-overlap range
+		   2. convert RE to overlap range and requested mode */
+
+		add_lock(r, owner, pid, X_WAIT, SH, frag_start, frag_end, HEAD);
+
+		opo->start = over_start;
+		opo->end = over_end;
+		opo->ex = ex;
+
+		update_lock(opo->lp, wait);
+	} else {
+		/* 1. request a shared lock in the overlap range
+		   2. convert RE to non-overlap range
+		   3. wait for shared lock to complete */
+
+		add_lock(r, owner, pid, WAIT, SH, over_start, over_end, HEAD);
+
+		opo->start = frag_start;
+		opo->end = frag_end;
+
+		update_lock(opo->lp, X_WAIT);
+	}
+
+	return 0;
+}
+
+/* go through r->locks to find what needs to be done to extend,
+   shrink, shift, split, etc existing locks (this often involves adding new
+   locks in addition to modifying existing locks. */
+
+static int plock_internal(struct dlm_resource *r, unsigned long owner, 
+			  unsigned int pid, int wait, int ex, uint64_t start, 
+			  uint64_t end)
+{
+	LIST_HEAD(exist);
+	struct posix_lock *po, *safe, *case4_po = NULL;
+	int error = 0;
+
+	list_for_each_entry_safe(po, safe, &r->locks, list) {
+		if (po->owner != owner)
+			continue;
+		if (!ranges_overlap(po->start, po->end, start, end))
+			continue;
+
+		/* existing range (RE) overlaps new range (RN) */
+
+		switch(overlap_type(start, end, po->start, po->end)) {
+
+		case 0:
+			if (po->ex == ex)
+				goto out;
+
+			/* ranges the same - just update the existing lock */
+			po->ex = ex;
+			update_lock(po->lp, wait);
+			goto out;
+
+		case 1:
+			if (po->ex == ex)
+				goto out;
+
+			error = lock_case1(po, r, owner, pid, wait, ex, start, end);
+			goto out;
+
+		case 2:
+			if (po->ex == ex)
+				goto out;
+
+			error = lock_case2(po, r, owner, pid, wait, ex, start, end);
+			goto out;
+
+		case 3:
+			list_move_tail(&po->list, &exist);
+			break;
+
+		case 4:
+			DLM_ASSERT(!case4_po, );
+			case4_po = po;
+			list_move_tail(&po->list, &exist);
+			break;
+
+		default:
+			error = -1;
+			goto out;
+		}
+	}
+
+	if (case4_po)
+		error = lock_case4(case4_po, &exist, r, owner, pid, wait, ex,
+				   start, end);
+	else if (!list_empty(&exist))
+		error = lock_case3(&exist, r, owner, pid, wait, ex, start, end);
+	else
+		add_lock(r, owner, pid, wait, ex, start, end, 0);
+
+ out:
+	return error;
+}
+
+static int punlock_internal(struct dlm_resource *r, unsigned long owner,
+			    unsigned int pid, uint64_t start, uint64_t end)
+{
+	struct posix_lock *po, *safe;
+	int error = 0;
+
+	list_for_each_entry_safe(po, safe, &r->locks, list) {
+		if (po->owner != owner)
+			continue;
+		if (!ranges_overlap(po->start, po->end, start, end))
+			continue;
+
+		/* existing range (RE) overlaps new range (RN) */
+
+		switch(overlap_type(start, end, po->start, po->end)) {
+
+		case 0:
+			/* ranges the same - just remove the existing lock */
+
+			list_del(&po->list);
+			remove_lock(po->lp);
+			goto out;
+
+		case 1:
+			/* RN within RE and starts or ends on RE boundary -
+			 * shrink and update RE */
+
+			shrink_range(po, start, end);
+			update_lock(po->lp, X_WAIT);
+			goto out;
+
+		case 2:
+			/* RN within RE - shrink and update RE to be front
+			 * fragment, and add a new lock for back fragment */
+
+			add_lock(r, owner, pid, po->ex ? WAIT : X_WAIT, po->ex,
+				 end+1, po->end, HEAD);
+
+			po->end = start - 1;
+			update_lock(po->lp, X_WAIT);
+			goto out;
+
+		case 3:
+			/* RE within RN - remove RE, then continue checking
+			 * because RN could cover other locks */
+
+			list_del(&po->list);
+			remove_lock(po->lp);
+			continue;
+
+		case 4:
+			/* front of RE in RN, or end of RE in RN - shrink and
+			 * update RE, then continue because RN could cover
+			 * other locks */
+
+			shrink_range(po, start, end);
+			update_lock(po->lp, X_WAIT);
+			continue;
+
+		default:
+			error = -1;
+			goto out;
+		}
+	}
+
+ out:
+	return error;
+}
+
+static int wait_local(struct dlm_resource *r, unsigned long owner,
+		      uint64_t start, uint64_t end, int ex)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int error = 0;
+
+	add_wait_queue(&r->waiters, &wait);
+
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (!local_conflict(r->dlm, r, &r->name, owner, start, end, ex))
+			break;
+
+		if (signal_pending(current)) {
+			up(&r->sema);
+			error = -EINTR;
+			break;
+		}
+
+		up(&r->sema);
+		schedule();
+		down(&r->sema);
+	}
+
+	remove_wait_queue(&r->waiters, &wait);
+	set_current_state(TASK_RUNNING);
+	return error;
+}
+
+int lm_dlm_plock(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		 struct file *file, int cmd, struct file_lock *fl)
+{
+	dlm_t *dlm = (dlm_t *) lockspace;
+	unsigned long owner = (unsigned long) fl->fl_owner;
+	unsigned int pid = fl->fl_pid;
+	int wait = IS_SETLKW(cmd);
+	int ex = (fl->fl_type == F_WRLCK);
+	uint64_t start = fl->fl_start, end = fl->fl_end;
+	struct dlm_resource *r;
+	int error;
+
+	log_debug("en plock %x,%"PRIx64"", name->ln_type, name->ln_number);
+
+	error = get_resource(dlm, name, CREATE, &r);
+	if (error)
+		goto out;
+
+	error = down_interruptible(&r->sema);
+	if (error)
+		goto out_put;
+
+	/* We wait here until we aren't blocked by any other local locks.
+	   Then we can request the lock from the dlm, request the vfs lock
+	   (without it blocking) and release r->sema before waiting on the dlm
+	   request.  [We can't release the semaphore between the dlm and vfs
+	   requests, but we must release it before waiting for the ast.] */
+
+	error = local_conflict(dlm, r, name, owner, start, end, ex);
+	if (error) {
+		if (!wait) {
+			error = -EAGAIN;
+			goto out_up;
+		}
+		error = wait_local(r, owner, start, end, ex);
+		if (error)
+			goto out_put;
+		/* wait_local returns with r->sema held if no error */
+	}
+
+	error = lock_resource(r);
+	if (error)
+		goto out_up;
+
+	if (!wait && global_conflict(dlm, name, owner, start, end, ex)) {
+		error = -EAGAIN;
+		unlock_resource(r);
+		goto out_up;
+	}
+
+	/* If NO_WAIT all requests should return immediately.
+	   If WAIT all requests go on r->async_locks which we wait on in
+	   wait_async_locks().  This means DLM should not return -EAGAIN and we
+	   should never block waiting for a plock to be released until we call
+	   wait_async_list(). */
+
+	error = plock_internal(r, owner, pid, wait, ex, start, end);
+	unlock_resource(r);
+
+	if (!error) {
+		/* this won't block due to wait_local above and not yet
+		   having released r->sema */
+		if (posix_lock_file_wait(file, fl) < 0)
+			log_error("lm_dlm_plock: vfs lock error %x,%"PRIx64"",
+				  name->ln_type, name->ln_number);
+	}
+
+ out_up:
+	up(&r->sema);
+	wait_async_list(r, owner);
+	wake_up_all(&r->waiters);
+ out_put:
+	put_resource(r);
+ out:
+	log_debug("ex plock %d", error);
+	return error;
+}
+
+int lm_dlm_punlock(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		   struct file *file, struct file_lock *fl)
+{
+	dlm_t *dlm = (dlm_t *) lockspace;
+	unsigned long owner = (unsigned long) fl->fl_owner;
+	unsigned int pid = fl->fl_pid;
+	uint64_t start = fl->fl_start, end = fl->fl_end;
+	struct dlm_resource *r;
+	int error;
+
+	log_debug("en punlock %x,%"PRIx64"", name->ln_type, name->ln_number);
+
+	error = get_resource(dlm, name, NO_CREATE, &r);
+	if (error) {
+		if (error == -ENOENT)
+			error = 0;
+		goto out;
+	}
+
+	down(&r->sema);
+
+	if (posix_lock_file_wait(file, fl) < 0)
+		log_error("lm_dlm_punlock: vfs unlock error %x,%"PRIx64"",
+			  name->ln_type, name->ln_number);
+
+	error = lock_resource(r);
+	if (error)
+		goto out_up;
+
+	error = punlock_internal(r, owner, pid, start, end);
+	unlock_resource(r);
+
+ out_up:
+	up(&r->sema);
+	wait_async_list(r, owner);
+	wake_up_all(&r->waiters);
+	put_resource(r);
+ out:
+	log_debug("ex punlock %d", error);
+	return error;
+}
+
+static void query_ast(void *astargs)
+{
+	dlm_lock_t *lp = (dlm_lock_t *) astargs;;
+	complete(&lp->uast_wait);
+}
+
+static int get_global_conflict(dlm_t *dlm, struct lm_lockname *name,
+			       unsigned long owner, uint64_t *start,
+			       uint64_t *end, int *ex, unsigned long *rowner,
+			       unsigned int *rpid)
+{
+	dlm_lock_t *lp;
+	struct dlm_queryinfo qinfo;
+	struct dlm_lockinfo *lki;
+	int query = 0, s, error;
+
+	/* acquire a null lock on which to base the query */
+
+	lp = find_null_lock(dlm, name);
+	if (!lp) {
+		error = create_lp(dlm, name, &lp);
+		if (error)
+			goto ret;
+
+		lp->req = DLM_LOCK_NL;
+		lp->lkf = DLM_LKF_EXPEDITE;
+		set_bit(LFL_INLOCK, &lp->flags);
+		do_dlm_lock_sync(lp, NULL);
+	}
+
+	/* do query, repeating if insufficient space */
+
+	query = DLM_LOCK_THIS | DLM_QUERY_QUEUE_GRANTED |
+		DLM_QUERY_LOCKS_HIGHER;
+
+	for (s = 16; s < dlm->max_nodes + 1; s += 16) {
+
+		lki = kmalloc(s * sizeof(struct dlm_lockinfo), GFP_KERNEL);
+		if (!lki) {
+			error = -ENOMEM;
+			goto out;
+		}
+		memset(lki, 0, s * sizeof(struct dlm_lockinfo));
+		memset(&qinfo, 0, sizeof(qinfo));
+		qinfo.gqi_locksize = s;
+		qinfo.gqi_lockinfo = lki;
+
+		init_completion(&lp->uast_wait);
+		error = dlm_query(dlm->gdlm_lsp, &lp->lksb, query, &qinfo,
+			   	   query_ast, (void *) lp);
+		if (error) {
+			kfree(lki);
+			goto out;
+		}
+		wait_for_completion(&lp->uast_wait);
+		error = lp->lksb.sb_status;
+
+		if (!error)
+			break;
+		kfree(lki);
+		if (error != -E2BIG)
+			goto out;
+	}
+
+	/* check query results for blocking locks */
+
+	error = 0;
+
+	for (s = 0; s < qinfo.gqi_lockcount; s++) {
+
+		lki = &qinfo.gqi_lockinfo[s];
+
+		if (!ranges_overlap(*start, *end, lki->lki_grrange.ra_start,
+				    lki->lki_grrange.ra_end))
+			continue;
+
+		if (lki->lki_node == dlm->our_nodeid)
+			continue;
+
+		if (lki->lki_grmode == DLM_LOCK_EX || *ex) {
+			*start = lki->lki_grrange.ra_start;
+			*end = lki->lki_grrange.ra_end;
+			*ex = (lki->lki_grmode == DLM_LOCK_EX) ? 1 : 0;
+			*rowner = lki->lki_node;
+			*rpid = lki->lki_node; /* nodeid for pid as well */
+			error = -EAGAIN;
+			break;
+		}
+	}
+
+	kfree(qinfo.gqi_lockinfo);
+
+	log_debug("global conflict %d %"PRIx64"-%"PRIx64" ex %d own %lu, pid %du",
+		  error, *start, *end, *ex, *rowner, *rpid);
+ out:
+	keep_null_lock(dlm, lp);
+ ret:
+	return error;
+}
+
+static int get_local_conflict(dlm_t *dlm, struct dlm_resource *r,
+			      struct lm_lockname *name, unsigned long owner,
+			      uint64_t *start, uint64_t *end, int *ex,
+			      unsigned long *rowner, unsigned int *rpid)
+{
+	struct posix_lock *po;
+	int found = FALSE;
+
+	list_for_each_entry(po, &r->locks, list) {
+		if (po->owner == owner)
+			continue;
+		if (!ranges_overlap(po->start, po->end, *start, *end))
+			continue;
+
+		if (*ex || po->ex) {
+			*start = po->start;
+			*end = po->end;
+			*ex = po->ex;
+			*rowner = po->owner;
+			*rpid = po->pid;
+			found = TRUE;
+			break;
+		}
+	}
+	return found;
+}
+
+static int do_plock_get(dlm_t *dlm, struct lm_lockname *name,
+			unsigned long owner, uint64_t *start, uint64_t *end,
+			int *ex, unsigned long *rowner, unsigned int *rpid)
+{
+	struct dlm_resource *r;
+	int error, found;
+
+	error = get_resource(dlm, name, NO_CREATE, &r);
+	if (!error) {
+		error = down_interruptible(&r->sema);
+		if (error) {
+			put_resource(r);
+			goto out;
+		}
+		
+		found = get_local_conflict(dlm, r, name, owner, start, end, ex,
+					   rowner, rpid);
+		up(&r->sema);
+		put_resource(r);
+		if (found) {
+			error = 1;
+			goto out;
+		}
+	}
+
+	error = get_global_conflict(dlm, name, owner, start, end, ex, rowner, rpid);
+	if (error == -EAGAIN) {
+		log_debug("pl get global conflict %"PRIx64"-%"PRIx64" %d %lu %du",
+			  *start, *end, *ex, *rowner, *rpid);
+		error = 1;
+	}
+ out:
+	return error;
+}
+
+static int local_conflict(dlm_t *dlm, struct dlm_resource *r,
+			  struct lm_lockname *name, unsigned long owner,
+			  uint64_t start, uint64_t end, int ex)
+{
+	uint64_t get_start = start, get_end = end;
+	unsigned long get_owner = 0;
+	unsigned int get_pid = 0;
+	int get_ex = ex;
+
+	return get_local_conflict(dlm, r, name, owner, &get_start, &get_end, 
+				  &get_ex, &get_owner, &get_pid);
+}
+
+static int global_conflict(dlm_t *dlm, struct lm_lockname *name,
+			   unsigned long owner, uint64_t start, uint64_t end,
+			   int ex)
+{
+	uint64_t get_start = start, get_end = end;
+	unsigned long get_owner = 0;
+	unsigned int get_pid = 0;
+	int get_ex = ex;
+
+	return get_global_conflict(dlm, name, owner, &get_start, &get_end, 
+				   &get_ex, &get_owner, &get_pid);
+}
+
+int lm_dlm_plock_get(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		     struct file *file, struct file_lock *fl)
+{
+	dlm_t *dlm = (dlm_t *) lockspace;
+	unsigned long owner;
+	unsigned int pid;
+	int ex, error;
+
+	ex = (fl->fl_type == F_WRLCK) ? 1 : 0;
+
+	error = do_plock_get(dlm, name, fl->fl_owner, &fl->fl_start,
+			     &fl->fl_end, &ex, &owner, &pid);
+	if (error < 0)
+		return error;
+	if (error == 0)
+		fl->fl_type = F_UNLCK;
+	else {
+		fl->fl_type = (ex) ? F_WRLCK : F_RDLCK;
+		fl->fl_pid = pid;
+	}
+
+	return 0;
+}
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_dlm/thread.c linux-2.6.9.debug/fs/gfs_locking/lock_dlm/thread.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_dlm/thread.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_dlm/thread.c	2006-12-20 17:07:48.000000000 +0300
@@ -0,0 +1,443 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "lock_dlm.h"
+
+/* 
+ * Run in dlm_async thread 
+ */
+
+/**
+ * queue_submit - add lock request to queue for dlm_async thread
+ * @lp: DLM lock
+ *
+ * A lock placed on this queue is re-submitted to DLM as soon as
+ * dlm_async thread gets to it.  
+ */
+
+static void queue_submit(dlm_lock_t *lp)
+{
+	dlm_t *dlm = lp->dlm;
+
+	spin_lock(&dlm->async_lock);
+	list_add_tail(&lp->slist, &dlm->submit);
+	set_bit(LFL_SLIST, &lp->flags);
+	spin_unlock(&dlm->async_lock);
+	wake_up(&dlm->wait);
+}
+
+/**
+ * process_blocking - processing of blocking callback
+ * @lp: DLM lock
+ *
+ */
+
+static void process_blocking(dlm_lock_t *lp, int bast_mode)
+{
+	dlm_t *dlm = lp->dlm;
+	unsigned int cb;
+
+	switch (make_lmstate(bast_mode)) {
+	case LM_ST_EXCLUSIVE:
+		cb = LM_CB_NEED_E;
+		break;
+	case LM_ST_DEFERRED:
+		cb = LM_CB_NEED_D;
+		break;
+	case LM_ST_SHARED:
+		cb = LM_CB_NEED_S;
+		break;
+	default:
+		DLM_ASSERT(0, printk("unknown bast mode %u\n", lp->bast_mode););
+	}
+
+	dlm->fscb(dlm->fsdata, cb, &lp->lockname);
+}
+
+/**
+ * process_complete - processing of completion callback for a lock request
+ * @lp: DLM lock
+ *
+ */
+
+static void process_complete(dlm_lock_t *lp)
+{
+	dlm_t *dlm = lp->dlm;
+	struct lm_async_cb acb;
+	int16_t prev_mode = lp->cur;
+
+	memset(&acb, 0, sizeof(acb));
+
+	if (lp->lksb.sb_status == -DLM_ECANCEL) {
+		log_all("complete dlm cancel %x,%"PRIx64" flags %lx",
+			lp->lockname.ln_type, lp->lockname.ln_number,
+			lp->flags);
+
+		lp->req = lp->cur;
+		acb.lc_ret |= LM_OUT_CANCELED;
+		if (lp->cur == DLM_LOCK_IV)
+			lp->lksb.sb_lkid = 0;
+		goto out;
+	}
+
+	if (test_and_clear_bit(LFL_DLM_UNLOCK, &lp->flags)) {
+		if (lp->lksb.sb_status != -DLM_EUNLOCK) {
+			log_all("unlock sb_status %d %x,%"PRIx64" flags %lx",
+				lp->lksb.sb_status, lp->lockname.ln_type,
+				lp->lockname.ln_number, lp->flags);
+			return;
+		}
+
+		lp->cur = DLM_LOCK_IV;
+		lp->req = DLM_LOCK_IV;
+		lp->lksb.sb_lkid = 0;
+		atomic_dec(&dlm->lock_count);
+
+		if (test_and_clear_bit(LFL_UNLOCK_SYNC, &lp->flags)) {
+			complete(&lp->uast_wait);
+			return;
+		}
+		if (test_and_clear_bit(LFL_UNLOCK_DELETE, &lp->flags)) {
+			delete_lp(lp);
+			return;
+		}
+		goto out;
+	}
+
+	if (lp->lksb.sb_flags & DLM_SBF_VALNOTVALID)
+		memset(lp->lksb.sb_lvbptr, 0, DLM_LVB_LEN);
+
+	if (lp->lksb.sb_flags & DLM_SBF_ALTMODE) {
+		if (lp->req == DLM_LOCK_PR)
+			lp->req = DLM_LOCK_CW;
+		else if (lp->req == DLM_LOCK_CW)
+			lp->req = DLM_LOCK_PR;
+	}
+
+	/*
+	 * A canceled lock request.  The lock was just taken off the delayed
+	 * list and was never even submitted to dlm.
+	 */
+
+	if (test_and_clear_bit(LFL_CANCEL, &lp->flags)) {
+		log_all("complete internal cancel %x,%"PRIx64"",
+			lp->lockname.ln_type, lp->lockname.ln_number);
+		lp->req = lp->cur;
+		acb.lc_ret |= LM_OUT_CANCELED;
+		goto out;
+	}
+
+	/*
+	 * An error occured.
+	 */
+
+	if (lp->lksb.sb_status) {
+		/* a "normal" error */
+		if ((lp->lksb.sb_status == -EAGAIN) &&
+		    (lp->lkf & DLM_LKF_NOQUEUE)) {
+			lp->req = lp->cur;
+			if (lp->cur == DLM_LOCK_IV)
+				lp->lksb.sb_lkid = 0;
+			goto out;
+		}
+
+		/* this could only happen with cancels I think */
+		log_all("ast sb_status %d %x,%"PRIx64" flags %lx",
+			lp->lksb.sb_status, lp->lockname.ln_type,
+			lp->lockname.ln_number, lp->flags);
+		return;
+	}
+
+	/*
+	 * This is an AST for an EX->EX conversion for sync_lvb from GFS.
+	 */
+
+	if (test_and_clear_bit(LFL_SYNC_LVB, &lp->flags)) {
+		complete(&lp->uast_wait);
+		return;
+	}
+
+	/*
+	 * A lock has been demoted to NL because it initially completed during
+	 * BLOCK_LOCKS.  Now it must be requested in the originally requested
+	 * mode.
+	 */
+
+	if (test_and_clear_bit(LFL_REREQUEST, &lp->flags)) {
+
+		DLM_ASSERT(lp->req == DLM_LOCK_NL,);
+		DLM_ASSERT(lp->prev_req > DLM_LOCK_NL,);
+
+		lp->cur = DLM_LOCK_NL;
+		lp->req = lp->prev_req;
+		lp->prev_req = DLM_LOCK_IV;
+		lp->lkf &= ~DLM_LKF_CONVDEADLK;
+
+		set_bit(LFL_NOCACHE, &lp->flags);
+
+		if (test_bit(DFL_BLOCK_LOCKS, &dlm->flags) &&
+		    !test_bit(LFL_NOBLOCK, &lp->flags))
+			queue_delayed(lp, QUEUE_LOCKS_BLOCKED);
+		else
+			queue_submit(lp);
+		return;
+	}
+
+	/* 
+	 * A request is granted during dlm recovery.  It may be granted
+	 * because the locks of a failed node were cleared.  In that case,
+	 * there may be inconsistent data beneath this lock and we must wait
+	 * for recovery to complete to use it.  When gfs recovery is done this
+	 * granted lock will be converted to NL and then reacquired in this
+	 * granted state.
+	 */
+
+	if (test_bit(DFL_BLOCK_LOCKS, &dlm->flags) &&
+	    !test_bit(LFL_NOBLOCK, &lp->flags) &&
+	    lp->req != DLM_LOCK_NL) {
+
+		lp->cur = lp->req;
+		lp->prev_req = lp->req;
+		lp->req = DLM_LOCK_NL;
+		lp->lkf |= DLM_LKF_CONVERT;
+		lp->lkf &= ~DLM_LKF_CONVDEADLK;
+
+		log_debug("rereq %x,%"PRIx64" id %x %d,%d",
+			  lp->lockname.ln_type, lp->lockname.ln_number,
+			  lp->lksb.sb_lkid, lp->cur, lp->req);
+
+		set_bit(LFL_REREQUEST, &lp->flags);
+		queue_submit(lp);
+		return;
+	}
+
+	/*
+	 * DLM demoted the lock to NL before it was granted so GFS must be
+	 * told it cannot cache data for this lock.
+	 */
+
+	if (lp->lksb.sb_flags & DLM_SBF_DEMOTED)
+		set_bit(LFL_NOCACHE, &lp->flags);
+
+      out:
+
+	/*
+	 * This is an internal lock_dlm lock (for jid's or plock's)
+	 */
+
+	if (test_bit(LFL_INLOCK, &lp->flags)) {
+		clear_bit(LFL_NOBLOCK, &lp->flags);
+		lp->cur = lp->req;
+		complete(&lp->uast_wait);
+		return;
+	}
+
+	/*
+	 * Normal completion of a lock request.  Tell GFS it now has the lock.
+	 */
+
+	clear_bit(LFL_NOBLOCK, &lp->flags);
+	lp->cur = lp->req;
+
+	acb.lc_name = lp->lockname;
+	acb.lc_ret |= make_lmstate(lp->cur);
+
+	if (!test_and_clear_bit(LFL_NOCACHE, &lp->flags) &&
+	    (lp->cur > DLM_LOCK_NL) && (prev_mode > DLM_LOCK_NL))
+		acb.lc_ret |= LM_OUT_CACHEABLE;
+
+	if (prev_mode == DLM_LOCK_IV && lp->cur > DLM_LOCK_IV)
+		atomic_inc(&dlm->lock_count);
+
+	dlm->fscb(dlm->fsdata, LM_CB_ASYNC, &acb);
+}
+
+/**
+ * no_work - determine if there's work for the dlm_async thread
+ * @dlm:
+ *
+ * Returns: 1 if no work, 0 otherwise
+ */
+
+static __inline__ int no_work(dlm_t *dlm)
+{
+	int ret;
+
+	spin_lock(&dlm->async_lock);
+
+	ret = list_empty(&dlm->complete) &&
+	    list_empty(&dlm->blocking) &&
+	    list_empty(&dlm->submit) &&
+	    list_empty(&dlm->starts) && !test_bit(DFL_MG_FINISH, &dlm->flags);
+
+	spin_unlock(&dlm->async_lock);
+
+	return ret;
+}
+
+static __inline__ int check_drop(dlm_t *dlm)
+{
+	if (!dlm->drop_locks_count)
+		return FALSE;
+
+	if (check_timeout(dlm->drop_time, dlm->drop_locks_period)) {
+		dlm->drop_time = jiffies;
+		if (atomic_read(&dlm->lock_count) >= dlm->drop_locks_count)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static __inline__ int check_shrink(dlm_t *dlm)
+{
+	if (check_timeout(dlm->shrink_time, SHRINK_CACHE_TIME)){
+		dlm->shrink_time = jiffies;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/**
+ * dlm_async - thread for a variety of asynchronous processing
+ * @data:
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+static int dlm_async(void *data)
+{
+	dlm_t *dlm = (dlm_t *) data;
+	dlm_lock_t *lp = NULL;
+	dlm_start_t *ds = NULL;
+	uint8_t complete, blocking, submit, start, finish, drop, shrink;
+	DECLARE_WAITQUEUE(wait, current);
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&dlm->wait, &wait);
+		if (no_work(dlm))
+			schedule();
+		remove_wait_queue(&dlm->wait, &wait);
+		set_current_state(TASK_RUNNING);
+
+		complete = blocking = submit = start = finish = 0;
+		drop = shrink = 0;
+
+		spin_lock(&dlm->async_lock);
+
+		if (!list_empty(&dlm->complete)) {
+			lp = list_entry(dlm->complete.next, dlm_lock_t, clist);
+			list_del(&lp->clist);
+			clear_bit(LFL_CLIST, &lp->flags);
+			complete = 1;
+		} else if (!list_empty(&dlm->blocking)) {
+			lp = list_entry(dlm->blocking.next, dlm_lock_t, blist);
+			list_del(&lp->blist);
+			clear_bit(LFL_BLIST, &lp->flags);
+			blocking = lp->bast_mode;
+			lp->bast_mode = 0;
+		} else if (!list_empty(&dlm->submit)) {
+			lp = list_entry(dlm->submit.next, dlm_lock_t, slist);
+			list_del(&lp->slist);
+			clear_bit(LFL_SLIST, &lp->flags);
+			submit = 1;
+		} else if (!test_bit(DFL_RECOVER, &dlm->flags) &&
+			   !list_empty(&dlm->starts)) {
+			ds = list_entry(dlm->starts.next, dlm_start_t, list);
+			list_del(&ds->list);
+			set_bit(DFL_RECOVER, &dlm->flags);
+			start = 1;
+		} else if (test_and_clear_bit(DFL_MG_FINISH, &dlm->flags)) {
+			finish = 1;
+		}
+
+		/* Don't get busy doing this stuff during recovery. */
+		if (!test_bit(DFL_RECOVER, &dlm->flags)) {
+			drop = check_drop(dlm);
+			shrink = check_shrink(dlm);
+		}
+		spin_unlock(&dlm->async_lock);
+
+		/* once withdrawn don't call into gfs for anything */
+		if (test_bit(DFL_WITHDRAW, &dlm->flags))
+			complete = blocking = drop = shrink = 0;
+
+		if (complete)
+			process_complete(lp);
+
+		else if (blocking)
+			process_blocking(lp, blocking);
+
+		else if (submit)
+			process_submit(lp);
+
+		else if (start)
+			process_start(dlm, ds);
+
+		else if (finish)
+			process_finish(dlm);
+
+		if (drop)
+			dlm->fscb(dlm->fsdata, LM_CB_DROPLOCKS, NULL);
+		if (shrink)
+			shrink_null_cache(dlm);
+
+		schedule();
+	}
+
+	return 0;
+}
+
+/**
+ * init_async_thread
+ * @dlm:
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int init_async_thread(dlm_t *dlm)
+{
+	struct task_struct *p;
+	int error;
+
+	p = kthread_run(dlm_async, dlm, "lock_dlm1");
+	error = IS_ERR(p);
+	if (error) {
+		log_all("can't start lock_dlm1 daemon %d", error);
+		return error;
+	}
+	dlm->thread1 = p;
+
+	p = kthread_run(dlm_async, dlm, "lock_dlm2");
+	error = IS_ERR(p);
+	if (error) {
+		log_all("can't start lock_dlm2 daemon %d", error);
+		kthread_stop(dlm->thread1);
+		return error;
+	}
+	dlm->thread2 = p;
+
+	return 0;
+}
+
+/**
+ * release_async_thread
+ * @dlm:
+ *
+ */
+
+void release_async_thread(dlm_t *dlm)
+{
+	kthread_stop(dlm->thread1);
+	kthread_stop(dlm->thread2);
+}
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/Makefile linux-2.6.9.debug/fs/gfs_locking/lock_gulm/Makefile
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/Makefile	2006-12-20 17:07:36.000000000 +0300
@@ -0,0 +1,32 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+##
+##  This copyrighted material is made available to anyone wishing to use,
+##  modify, copy, or redistribute it subject to the terms and conditions
+##  of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+obj-$(CONFIG_LOCK_GULM) += lock_gulm.o
+
+lock_gulm-y	:=	gulm_core.o \
+		gulm_firstlock.o \
+		gulm_fs.o \
+		gulm_jid.o \
+		gulm_lock_queue.o \
+		gulm_lt.o \
+		gulm_main.o \
+		gulm_plock.o \
+		gulm_recsig.o \
+		handler.o \
+		lg_core.o \
+		lg_lock.o \
+		lg_main.o \
+		utils_tostr.o \
+		xdr_base.o \
+		xdr_io.o \
+		xdr_socket.o
+
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gio_wiretypes.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gio_wiretypes.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gio_wiretypes.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gio_wiretypes.h	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,486 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+#ifndef __gio_wiretypes_h__
+#define __gio_wiretypes_h__
+
+/* an attempt to do something about tracking changes to the protocol over
+ * the wires.
+ * If I was really cute, this would be effectivily a checksum of this file.
+ */
+#define GIO_WIREPROT_VERS (0x67000015)
+
+/*****************Error codes.
+ * everyone uses these same error codes.
+ */
+#define gio_Err_Ok              (0)
+#define gio_Err_BadLogin        (1001)
+#define gio_Err_BadCluster      (1003)
+#define gio_Err_BadConfig       (1004)
+#define gio_Err_BadGeneration   (1005)
+#define gio_Err_BadWireProto    (1019)
+
+#define gio_Err_NotAllowed      (1006)
+#define gio_Err_Unknown_Cs      (1007)
+#define gio_Err_BadStateChg     (1008)
+#define gio_Err_MemoryIssues    (1009)
+
+#define gio_Err_TryFailed       (1011)
+#define gio_Err_AlreadyPend     (1013)
+#define gio_Err_Canceled        (1015)
+
+/* next free error code: 1002 1010 1012 1014 1016 1017 1018 1020 */
+
+/*
+ * Error:  just sort of a generic error code thing.
+ *    uint32: gERR
+ *    uint32: opcode that this is in reply to. (can be zeros)
+ *    uint32: error code
+ */
+#define gulm_err_reply (0x67455252) /* gERR */
+
+#define gulm_nop (0x674e4f50)  /* gNOP */
+
+/********************* Core *****************/
+/* 
+ * login request
+ *    uint32: gCL0
+ *    uint32: proto version
+ *    string: cluster ID
+ *    string: My Name
+ *    uint64: generation number
+ *    uint32: config CRC
+ *    uint32: rank
+ * login reply
+ *    uint32: gCL1
+ *    uint64: generation number
+ *    uint32: error code
+ *    uint32: rank
+ *    uint8:  ama
+ *   If I am the Master or Arbitrating and there are no errors, A
+ *   serialization of the current nodelist follows. And a client or slave
+ *   is connecting (not resources).
+ *
+ * logout request:
+ *    uint32: gCL2
+ *    string: node name
+ *    uint8:  S/P/A/M/R
+ * logout reply:   Don't seem to use this....
+ *    uint32: gCL3
+ *    uint32: error code
+ *
+ * resource login request:
+ *    uint32: gCL4
+ *    uint32: proto version
+ *    string: cluster ID
+ *    string: resource name
+ *    uint32: options
+ *  login reply (gCL1) is sent in return.
+ *
+ * beat req
+ *    uint32: gCB0
+ *    string: My Name
+ * beat rpl
+ *    uint32: gCB1
+ *    uint32: error code
+ *
+ * Membership Request
+ *    uint32: gCMA
+ *    string: node name
+ *
+ * Membership update
+ *    uint32: gCMU
+ *    string: node name
+ *    IPv6:   IP
+ *    uint8:  Current State
+ *
+ * Membership list request info.
+ *    uint32: gCMl
+ *
+ * Membership list info.
+ *    uint32: gCML
+ *    list_start_marker
+ *     string: node name
+ *     IPv6:   IP
+ *     uint8:  state
+ *     uint8:  laststate
+ *     uint8:  mode (S/P/A/M/C)
+ *     uint32: missed beats
+ *     uint64: last beat
+ *     uint64: delay avg
+ *     uint64: max delay
+ *    list_stop_marker
+ *
+ * Request Resource info
+ *    uint32: gCR0
+ *
+ * Resource list info
+ *    uint32: gCR1
+ *    list_start_marker
+ *     string: name
+ *    list_stop_marker
+ *
+ * Force node into Expired:
+ *    uint32: gCFE
+ *    string: node name
+ *
+ * Core state request:
+ *    uint32: gCSR
+ *
+ * Core state changes:
+ *    uint32: gCSC
+ *    uint8:  state  (slave, pending, arbitrating, master)
+ *    uint8:  quorate (true/false)
+ *  If state == Slave, then the next two will follow.
+ *    IPv6:   MasterIP
+ *    string: MasterName
+ *
+ * Quorum Change:
+ *    uint32: gCQC
+ *    uint8:  quorate (true/false)
+ *
+ * Core shutdown req:
+ *    uint32: gCSD
+ *
+ * Switch core from current state into Pending:
+ *    uint32: gCSP
+ *
+ * Fetch the current config
+ *    uint32: gCC0
+ * Current Config Reply:
+ *    uint32: gCC1
+ *    list start:
+ *       string: key
+ *       string: value
+ *    list stop:
+ *
+ */
+#define gulm_core_login_req  (0x67434c00) /* gCL0 */
+#define gulm_core_login_rpl  (0x67434c01) /* gCL1 */
+#define gulm_core_logout_req (0x67434c02) /* gCL2 */
+#define gulm_core_logout_rpl (0x67434c03) /* gCL3 */
+#define gulm_core_reslgn_req (0x67434c04) /* gCL4 */
+#define gulm_core_beat_req   (0x67434200) /* gCB0 */
+#define gulm_core_beat_rpl   (0x67434201) /* gCB1 */
+#define gulm_core_mbr_req    (0x67434d41) /* gCMA */
+#define gulm_core_mbr_updt   (0x67434d55) /* gCMU */
+#define gulm_core_mbr_lstreq (0x67434d6c) /* gCMl */
+#define gulm_core_mbr_lstrpl (0x67434d4c) /* gCML */
+#define gulm_core_mbr_force  (0x67434645) /* gCFE */
+#define gulm_core_res_req    (0x67435200) /* gCR0 */
+#define gulm_core_res_list   (0x67435201) /* gCR1 */
+#define gulm_core_state_req  (0x67435352) /* gCSR */
+#define gulm_core_state_chgs (0x67435343) /* gCSC */
+#define gulm_core_quorm_chgs (0x67435143) /* gCSC */
+#define gulm_core_shutdown   (0x67435344) /* gCSD */
+#define gulm_core_forcepend  (0x67435350) /* gCSP */
+#define gulm_core_configreq  (0x67434300) /* gCC0 */
+#define gulm_core_configrpl  (0x67434301) /* gCC1 */
+
+/* in the st field */
+#define gio_Mbr_Logged_in  (0x05)
+#define gio_Mbr_Logged_out (0x06)
+#define gio_Mbr_Expired    (0x07)
+#define gio_Mbr_Killed     (0x08)
+#define gio_Mbr_OM_lgin    (0x09)
+
+/* in the ama field */
+#define gio_Mbr_ama_Slave       (0x01)
+#define gio_Mbr_ama_Master      (0x02)
+#define gio_Mbr_ama_Pending     (0x03)
+#define gio_Mbr_ama_Arbitrating (0x04)
+#define gio_Mbr_ama_Resource    (0x05)
+#define gio_Mbr_ama_Client      (0x06)
+/* the Client entery is ONLY for mode tracking.
+ * nodelist reply is the only place it is used.
+ */
+
+/* options that affect behavors on services. (resources) */
+#define gulm_svc_opt_important (0x00000001)
+#define gulm_svc_opt_locked    (0x00000002)
+
+/********************* Info Traffic *****************
+ *
+ * Note that for many of these, they can be sent to all of the servers and
+ * will get sane replies.  Some of these can only be sent to specific
+ * servers.
+ *
+ * stats req:
+ *    uint32: gIS0
+ * stats rpl:
+ *    uint32: gIS1
+ *    list start:
+ *       string: key
+ *       string: value
+ *    list stop:
+ * Notes:
+ *  The stats reply is a set of string pairs.  This way the server can send
+ *  whatever things it wants, and the same client code will work for
+ *  anything.
+ *
+ * set verbosity:
+ *    uint32: gIV0
+ *    string: verb flags (with -/+) to [un]set
+ * Note:
+ *  We don't bother with a reply for this.  If the server got it, it works.
+ *  If it didn't, it cannot send an error back anyways.
+ *
+ * close socket:
+ *   uint32: gSC0
+ * Note:
+ *   Tells the server to close this connection cleanly.  We're done with
+ *   it.  This is *not* the same as loging out.  You must login before you
+ *   can logout.  And many commands sent from gulm_tool happen without
+ *   logging in.  These commands would be useful for clients in many cases,
+ *   so I don't want to put a close at the end of them, but if I don't,
+ *   there will be error messages printed on the console when gulm_tool
+ *   calls them.
+ *   So we need a way to close a connection cleanly that has not been
+ *   logged in.
+ *
+ * request slave list:
+ *    uint32: gIL0
+ * slave list replay:
+ *    uint32: gIL1
+ *    list start:
+ *       string: name
+ *       uint32: poller idx
+ *    list stop:
+ */
+#define gulm_info_stats_req      (0x67495300) /* gIS0 */
+#define gulm_info_stats_rpl      (0x67495301) /* gIS1 */
+#define gulm_info_set_verbosity  (0x67495600) /* gIV0 */
+#define gulm_socket_close        (0x67534300) /* gSC0 */
+#define gulm_info_slave_list_req (0x67494c00) /* gIL0 */
+#define gulm_info_slave_list_rpl (0x67494c01) /* gIL1 */
+
+/********************* Lock Traffic *****************
+ * All lock traffic.
+ *
+ * login req:
+ *    uint32: gLL0
+ *    uint32: proto version
+ *    string: node name
+ *    uint8:  Client/Slave
+ * login rpl:
+ *    uint32: gLL1
+ *    uint32: error code
+ *    uint8:  Slave/Master
+ *    xdr of current lock state if no errors and master sending reply
+ *       and you're a slave.
+ *
+ * logout req:
+ *    uint32: gLL2
+ * logout rpl:
+ *    uint32: gLL3
+ *
+ * select lockspace:
+ *    uint32: gLS0
+ *    raw:    usually just four bytes for lockspace name.
+ *            but can be most anything.
+ *            uh, i think i assume that it is only four bytes in some places.
+ *            Need to look into this...
+ *
+ * lock req:
+ *    uint32: gLR0
+ *    raw:    key
+ *    uint64: sub id
+ *    uint64: start
+ *    uint64: stop
+ *    uint8:  state
+ *    uint32: flags
+ *    raw:    lvb -- Only exists if hasLVB flag is true.
+ * lock rpl:
+ *    uint32: gLR1
+ *    raw:    key
+ *    uint64: sub id
+ *    uint64: start
+ *    uint64: stop
+ *    uint8:  state
+ *    uint32: flags
+ *    uint32: error code
+ *    raw:    lvb -- Only exists if hasLVB flag is true.
+ *
+ * lock state update:
+ *    uint32: gLRU
+ *    string: node name
+ *    uint64: sub id
+ *    uint64: start
+ *    uint64: stop
+ *    raw:    key
+ *    uint8:  state
+ *    uint32: flags
+ *    raw:    lvb -- Only exists if hasLVB flag is true.
+ *
+ * Action req:
+ *    uint32: gLA0
+ *    raw:    key
+ *    uint64: sub id
+ *    uint8:  action
+ *    raw:    lvb -- Only exists if action is SyncLVB
+ * Action Rpl:
+ *    uint32: gLA1
+ *    raw:    key
+ *    uint64: sub id
+ *    uint8:  action
+ *    uint32: error code
+ *
+ * Action update:
+ *    uint32: gLAU
+ *    string: node name
+ *    uint64: sub id
+ *    raw:    key
+ *    uint8:  action
+ *    raw:    lvb -- Only exists if action is SyncLVB
+ *
+ * Slave Update Rply:   -- for both actions and requests.
+ *    uint32: gLUR
+ *    raw:    key
+ *
+ * Query Lock Request:
+ *    uint32: gLQ0
+ *    raw:    key
+ *    uint64: subid
+ *    uint64: start
+ *    uint64: stop
+ *    uint8:  state
+ * 
+ * Query Lock Reply:
+ *    uint32: gLQ1
+ *    raw:    key
+ *    uint64: subid
+ *    uint64: start
+ *    uint64: stop
+ *    uint8:  state
+ *    uint32: error
+ *    list start mark
+ *     string: node
+ *     uint64: subid
+ *     uint64: start
+ *     uint64: stop
+ *     uint8:  state
+ *    list stop mark
+ *
+ * Drop lock Callback:
+ *    uint32: gLC0
+ *    raw:    key
+ *    uint64: subid
+ *    uint8:  state
+ *
+ * Drop all locks callback:  This is the highwater locks thing
+ *    uint32: gLC2
+ *
+ * Drop expired locks:
+ *    uint32: gLEO
+ *    string: node name  if NULL, then drop all exp for mask.
+ *    raw:    keymask  if keymask & key == key, then dropexp on this lock.
+ *
+ * Expire Locks:
+ *    uint32: gLEE
+ *    string: node name  cannot be NULL
+ *    raw:    keymask  if keymask & key == key, then expire on this lock.
+ *
+ * Lock list req:
+ *    uint32: gLD0
+ * Lock list rpl:
+ *    uint32: gLD1
+ *    list start mark
+ *     uint8: key length
+ *     raw:   key
+ *     uint8: lvb length
+ *     if lvb length > 0, raw: LVB
+ *     uint32: Holder count
+ *     list start mark
+ *      string: holders
+ *      uint64: subid
+ *      uint8: state
+ *      uint64: start
+ *      uint64: stop
+ *     list stop mark
+ *     uint32: LVB holder count
+ *     list start mark
+ *      string: LVB Holders
+ *      uint64: subid
+ *     list stop mark
+ *     uint32: Expired holder count
+ *     list start mark
+ *      string: ExpHolders
+ *      uint64: subid
+ *     list stop mark
+ *    list stop mark
+ *
+ */
+#define gulm_lock_login_req   (0x674C4C00) /* gLL0 */
+#define gulm_lock_login_rpl   (0x674C4C01) /* gLL1 */
+#define gulm_lock_logout_req  (0x674C4C02) /* gLL2 */
+#define gulm_lock_logout_rpl  (0x674C4C03) /* gLL3 */
+#define gulm_lock_sel_lckspc  (0x674C5300) /* gLS0 */
+#define gulm_lock_state_req   (0x674C5200) /* gLR0 */
+#define gulm_lock_state_rpl   (0x674C5201) /* gLR1 */
+#define gulm_lock_state_updt  (0x674C5255) /* gLRU */
+#define gulm_lock_action_req  (0x674C4100) /* gLA0 */
+#define gulm_lock_action_rpl  (0x674C4101) /* gLA1 */
+#define gulm_lock_action_updt (0x674C4155) /* gLAU */
+#define gulm_lock_update_rpl  (0x674c5552) /* gLUR */
+#define gulm_lock_query_req   (0x674c5100) /* gLQ0 */
+#define gulm_lock_query_rpl   (0x674c5101) /* gLQ1 */
+#define gulm_lock_cb_state    (0x674C4300) /* gLC0 */
+#define gulm_lock_cb_dropall  (0x674C4302) /* gLC2 */
+#define gulm_lock_drop_exp    (0x674C454F) /* gLEO */
+#define gulm_lock_expire      (0x674C4545) /* gLEE */
+#define gulm_lock_dump_req    (0x674c4400) /* gLD0 */
+#define gulm_lock_dump_rpl    (0x674c4401) /* gLD1 */
+#define gulm_lock_rerunqueues (0x674c5251) /* gLRQ */
+
+/* marks for the login */
+#define gio_lck_st_Slave     (0x00)
+#define gio_lck_st_Client    (0x01)
+
+/* state change requests */
+#define gio_lck_st_Unlock    (0x00)
+#define gio_lck_st_Exclusive (0x01)
+#define gio_lck_st_Deferred  (0x02)
+#define gio_lck_st_Shared    (0x03)
+/* actions */
+#define gio_lck_st_Cancel    (0x09)
+#define gio_lck_st_HoldLVB   (0x0b)
+#define gio_lck_st_UnHoldLVB (0x0c)
+#define gio_lck_st_SyncLVB   (0x0d)
+
+/* flags */
+ /* only valid with Try.  Tells server to send out a drop lock callback. */
+#define gio_lck_fg_Do_CB       (0x00000001)
+ /* try to get, if there are conflicts, return error instead of blocking */
+#define gio_lck_fg_Try         (0x00000002)
+ /* Either Shared or Deferred.  Only valid when state is Shr or Dfr. */
+#define gio_lck_fg_Any         (0x00000004)
+ /* Ignore any expired holders on lock. */
+#define gio_lck_fg_NoExp       (0x00000008)
+ /* There is an LVB attached to this lock msg. */
+#define gio_lck_fg_hasLVB      (0x00000010)
+ /* Only returned by server.  There was no internal unlocking to grant req. */
+#define gio_lck_fg_Cachable    (0x00000020)
+ /* Put this request onto the front of the request queues. */
+#define gio_lck_fg_Piority     (0x00000040)
+ /* this is just an idea, but it might be useful.  Basically just says to
+  * not keep the exp hold, just drop this hold like a shared would be.
+  * no idea if it would be useful or sane. (but its two lines of code)
+  */
+#define gio_lck_fg_DropOnExp   (0x00000080)
+ /* this is saved on each holder, basically, you are gonna ignore any
+  * callbacks about this lock, so tell the server not to even bother
+  * sending them.  A tiny performance boost by lowering the network load.
+  */
+#define gio_lck_fg_NoCallBacks (0x00000100)
+
+#endif /*__gio_wiretypes_h__*/
+/* vim: set ai cin et sw=3 ts=3 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm.h	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,256 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef GULM_DOT_H
+#define GULM_DOT_H
+
+#define GULM_RELEASE_NAME "2.6.9-60.3"
+
+/* uh, do I need all of these headers? */
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif				/*  MODVERSIONS  */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/smp_lock.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#if (BITS_PER_LONG == 64)
+#define PRIu64 "lu"
+#define PRId64 "ld"
+#define PRIo64 "lo"
+#define PRIx64 "lx"
+#define PRIX64 "lX"
+#define SCNu64 "lu"
+#define SCNd64 "ld"
+#define SCNo64 "lo"
+#define SCNx64 "lx"
+#define SCNX64 "lX"
+#else
+#define PRIu64 "Lu"
+#define PRId64 "Ld"
+#define PRIo64 "Lo"
+#define PRIx64 "Lx"
+#define PRIX64 "LX"
+#define SCNu64 "Lu"
+#define SCNd64 "Ld"
+#define SCNo64 "Lo"
+#define SCNx64 "Lx"
+#define SCNX64 "LX"
+#endif
+
+
+#undef MAX
+#define MAX(a,b) ((a>b)?a:b)
+
+#undef MIN
+#define MIN(a,b) ((a<b)?a:b)
+
+/*  Divide x by y.  Round up if there is a remainder.  */
+#define DIV_RU(x, y) (((x) + (y) - 1) / (y))
+
+#include <linux/lm_interface.h>
+
+#include "gulm_prints.h"
+
+#include "libgulm.h"
+
+#include "handler.h"
+
+/* Really should try to avoid using this.
+ */
+#define RETRY_MALLOC(do_this, until_this) \
+for (;;) { \
+        { do_this; } \
+        if (until_this) \
+                break; \
+        printk("LOCK_GULM: out of memory: %s, %u\n", __FILE__, __LINE__); \
+        yield(); \
+}
+
+/* Some fixed length constants.
+ * Some of these should be made dynamic in size in the future.
+ */
+#define GIO_KEY_SIZE  (48)
+#define GIO_LVB_SIZE  (32)
+#define GIO_NAME_SIZE (32)
+#define GIO_NAME_LEN  (GIO_NAME_SIZE-1)
+#define GULM_CRC_INIT (0x6d696b65)
+
+/* a hash bucket
+ * this puts the bucket list and the spinlock for the list next to each
+ * other.  Mostly because it makes things nice and easy if we're on a
+ * nonsmp machine and the spinlocks don't exist.  (previously I tried to
+ * malloc nothing.  kernel wasn't happy about that.)
+ *
+ * An array of these makes a hash table.
+ */
+struct gulm_hash_bucket_s {
+	struct list_head bucket;
+	spinlock_t lock;
+};
+typedef struct gulm_hash_bucket_s gulm_hb_t;
+
+/* What we know about this filesytem */
+struct gulm_fs_s {
+	struct list_head fs_list;
+	char fs_name[GIO_NAME_SIZE];	/* lock table name */
+
+	lm_callback_t cb;	/* file system callback function */
+	lm_fsdata_t *fsdata;	/* private file system data */
+
+	callback_qu_t cq;
+
+	uint32_t fsJID;
+	uint32_t lvb_size;
+
+	/* Stuff for the first mounter lock and state */
+	int firstmounting;
+	/* the recovery done func needs to behave slightly differnt when we are
+	 * the first node in an fs.
+	 */
+
+	/* Stuff for JID mapping locks */
+	uint32_t JIDcount;	/* how many JID locks are there. */
+	struct semaphore headerlock;
+};
+typedef struct gulm_fs_s gulm_fs_t;
+
+typedef struct gulm_cm_s {
+	uint8_t myName[64];
+	uint8_t clusterID[256]; /* doesn't need to be 256. */
+	uint8_t starts;
+
+	uint32_t handler_threads;	/* howmany to have */
+	uint32_t verbosity;
+
+	uint64_t GenerationID;
+
+	/* lm interface pretty much requires that we maintian a table of
+	 * locks.  The way lvbs work is a prefect example of why.  As is
+	 * the panic you get if you send a cb up about a lock that has been
+	 * put away.
+	 */
+	gulm_hb_t *gfs_lockmap;
+
+	gulm_interface_p hookup;
+
+} gulm_cm_t;
+
+/* things about each lock. */
+typedef struct gulm_lock_s {
+   struct list_head gl_list;
+   atomic_t count; /* gfs can call multiple gets and puts for same lock. */
+
+   uint8_t *key;
+   uint16_t keylen;
+   gulm_fs_t *fs; /* which fs we belong to */
+   char *lvb;
+   int cur_state; /* for figuring out wat reply to tell gfs. */
+} gulm_lock_t;
+
+
+/*****************************************************************************/
+/* cross pollenate prototypes */
+
+/* from gulm_firstlock.c */
+int get_mount_lock (gulm_fs_t * fs, int *first);
+int downgrade_mount_lock (gulm_fs_t * fs);
+int drop_mount_lock (gulm_fs_t * fs);
+
+/* from gulm_lt.c */
+int gulm_lt_init (void);
+void gulm_lt_release(void);
+int pack_lock_key(uint8_t *key, uint16_t keylen, uint8_t type,
+		uint8_t *fsname, uint8_t *pk, uint8_t pklen);
+int pack_drop_mask(uint8_t *mask, uint16_t mlen, uint8_t *fsname);
+void do_drop_lock_req (uint8_t *key, uint16_t keylen, uint8_t state);
+int gulm_get_lock (lm_lockspace_t * lockspace, struct lm_lockname *name,
+	       lm_lock_t ** lockp);
+void gulm_put_lock (lm_lock_t * lock);
+unsigned int gulm_lock (lm_lock_t * lock, unsigned int cur_state,
+	   unsigned int req_state, unsigned int flags);
+unsigned int gulm_unlock (lm_lock_t * lock, unsigned int cur_state);
+void gulm_cancel (lm_lock_t * lock);
+int gulm_hold_lvb (lm_lock_t * lock, char **lvbp);
+void gulm_unhold_lvb (lm_lock_t * lock, char *lvb);
+void gulm_sync_lvb (lm_lock_t * lock, char *lvb);
+
+/* from gulm_plock.c */
+int gulm_punlock (lm_lockspace_t * lockspace, struct lm_lockname *name,
+	      struct file *file, struct file_lock *fl);
+int gulm_plock (lm_lockspace_t *lockspace, struct lm_lockname *name,
+		struct file *file, int cmd, struct file_lock *fl);
+int gulm_plock_get (lm_lockspace_t * lockspace, struct lm_lockname *name,
+		 struct file *file, struct file_lock *fl);
+
+/*from gulm_core.c */
+void cm_logout (void);
+int cm_login (void);
+void delete_ipnames (struct list_head *namelist);
+
+/* from gulm_fs.c */
+void init_gulm_fs (void);
+void request_journal_replay (uint8_t * name);
+void check_all_for_stales(void);
+void passup_droplocks (void);
+gulm_fs_t *get_fs_by_name (uint8_t * name);
+void dump_internal_lists (void);
+void gulm_recovery_done (lm_lockspace_t * lockspace,
+			 unsigned int jid, unsigned int message);
+void gulm_unmount (lm_lockspace_t * lockspace);
+void gulm_others_may_mount (lm_lockspace_t * lockspace);
+int gulm_mount (char *table_name, char *host_data,
+		lm_callback_t cb, lm_fsdata_t * fsdata,
+		unsigned int min_lvb_size, struct lm_lockstruct *lockstruct);
+void gulm_withdraw (lm_lockspace_t * lockspace);
+
+/* from gulm_jid.c */
+void jid_fs_init (gulm_fs_t * fs);
+void jid_fs_release (gulm_fs_t * fs);
+void get_journalID (gulm_fs_t * fs);
+int lookup_name_by_jid (gulm_fs_t * fs, uint32_t jid, uint8_t * name);
+void release_JID (gulm_fs_t * fs, uint32_t jid);
+void put_journalID (gulm_fs_t * fs, int leavebehind);
+void check_for_stale_expires (gulm_fs_t * fs);
+
+int find_jid_by_name_and_mark_replay (gulm_fs_t * fs, uint8_t * name, uint32_t * jid);
+
+/* to be called from the lg_lock callbacks. */
+void jid_header_lock_drop (uint8_t * key, uint16_t keylen);
+void sig_watcher_lock_drop(uint8_t * key, uint16_t keylen);
+
+/* from gulm_recsig.c */
+void tap_sig(gulm_fs_t *fs, uint8_t *name, uint8_t len);
+int watch_sig(gulm_fs_t *fs, uint8_t *name, uint8_t len,
+		void(*func)(void *misc), void *misc);
+void sig_watcher_init(void);
+
+extern struct lm_lockops gulm_ops;
+
+#endif				/*  GULM_DOT_H  */
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_core.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_core.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_core.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_core.c	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,259 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include "gulm_lock_queue.h"
+#include "utils_tostr.h"
+
+extern gulm_cm_t gulm_cm;
+
+/* private vars. */
+int cm_thd_running;
+struct completion cm_thd_startup;
+struct task_struct *cm_thd_task;
+/**
+ */
+int
+gulm_core_login_reply (void *misc, uint64_t gen, uint32_t error,
+		       uint32_t rank, uint8_t corestate)
+{
+	if (error != 0) {
+		log_err ("Core returned error %d:%s.\n", error,
+			 gio_Err_to_str (error));
+		cm_thd_running = FALSE;
+		return error;
+	}
+
+	if( gulm_cm.GenerationID != 0 ) {
+		GULM_ASSERT(gulm_cm.GenerationID == gen,
+				printk("us: %"PRIu64" them: %"PRIu64"\n",
+					gulm_cm.GenerationID,gen);
+				);
+	}
+	gulm_cm.GenerationID = gen;
+
+
+	log_msg (lgm_Network2, "Logged into local core.\n");
+
+	return 0;
+}
+
+/**
+ * gulm_core_logout_reply - 
+ * @misc: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+gulm_core_logout_reply (void *misc)
+{
+	log_msg (lgm_Network2, "Logged out of local core.\n");
+	return 0;
+}
+
+/**
+ */
+int
+gulm_core_nodechange (void *misc, char *nodename,
+		      struct in6_addr *nodeip, uint8_t nodestate)
+{
+	if (nodestate == lg_core_Fenced) {
+		request_journal_replay (nodename);
+	}
+	/* if me and state is logout, Need to close out things if we can.
+	 */
+	if (gulm_cm.starts && nodestate == lg_core_Logged_out &&
+			strcmp(gulm_cm.myName, nodename) == 0 ) {
+		glq_shutdown ();
+		cm_thd_running = FALSE;
+		lg_core_logout (gulm_cm.hookup);
+		return -1;
+	}
+	return 0;
+}
+
+int gulm_core_statechange (void *misc, uint8_t corestate, uint8_t quorate,
+                           struct in6_addr *masterip, char *mastername)
+{
+	int *cst = (int *)misc;
+	if( misc != NULL ) {
+		if( corestate != lg_core_Slave &&
+				corestate != lg_core_Master ) {
+			*cst = TRUE;
+		}else{
+			*cst = FALSE;
+		}
+	}
+	if( corestate == lg_core_Slave ||
+	    corestate == lg_core_Master ) {
+	   /* we should be part of a live, quorate cluster now. */
+	    check_all_for_stales();
+	}
+	return 0;
+}
+
+/**
+ */
+int
+gulm_core_error (void *misc, uint32_t err)
+{
+	log_err ("Got error code %d %#x back fome some reason!\n", err, err);
+	return 0;
+}
+
+static lg_core_callbacks_t core_cb = {
+      login_reply:gulm_core_login_reply,
+      logout_reply:gulm_core_logout_reply,
+      nodechange:gulm_core_nodechange,
+      statechange:gulm_core_statechange,
+      error:gulm_core_error
+};
+
+/**
+ * cm_io_recving_thread - 
+ * @data: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+cm_io_recving_thread (void *data)
+{
+	int err;
+
+	daemonize ("gulm_res_recvd");
+	cm_thd_task = current;
+	complete (&cm_thd_startup);
+
+	while (cm_thd_running) {
+		err = lg_core_handle_messages (gulm_cm.hookup, &core_cb, NULL);
+		if (err != 0) {
+			log_err
+			    ("Got an error in gulm_res_recvd err: %d\n", err);
+			if (!cm_thd_running)
+				break;
+			/* 
+			 * Pause a bit, then try to log back into the local
+			 * lock_gulmd.  Keep doing this until an outside force
+			 * stops us. (which I don't think there is any at this
+			 * point.  forceunmount would be one, if we ever do
+			 * that.)
+			 *
+			 * If we are still in the gulm_mount() function, we
+			 * should not retry. We should just exit.
+			 *
+			 * Is this really smart?  There is zero garuntees
+			 * that the state of things will be usable when we
+			 * return.
+			 *
+			 */
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout (3 * HZ);
+
+			while ((err =
+				lg_core_login (gulm_cm.hookup, TRUE)) != 0) {
+				log_err
+				    ("Got a %d trying to login to lock_gulmd.  Is it running?\n",
+				     err);
+				current->state = TASK_INTERRUPTIBLE;
+				schedule_timeout (3 * HZ);
+			}
+		}
+	}			/* while( gulm_cm.cm_thd_running ) */
+
+	complete (&cm_thd_startup);
+	return 0;
+}
+
+/**
+ * cm_logout - 
+ */
+void
+cm_logout (void)
+{
+
+	if (cm_thd_running) {
+		cm_thd_running = FALSE;
+		lg_core_logout (gulm_cm.hookup);
+
+		/* wait for thread to finish */
+		wait_for_completion (&cm_thd_startup);
+	}
+
+}
+
+/**
+ * cm_login - 
+ * 
+ * Returns: int
+ */
+int
+cm_login (void)
+{
+	int err = -1;
+	int cst=TRUE;
+
+	cm_thd_running = FALSE;
+	init_completion (&cm_thd_startup);
+
+	err = lg_core_login (gulm_cm.hookup, TRUE);
+	if (err != 0) {
+		log_err
+		    ("Got a %d trying to login to lock_gulmd.  Is it running?\n",
+		     err);
+		goto exit;
+	}
+	/* handle login reply.  which will start the lt thread. */
+	err = lg_core_handle_messages (gulm_cm.hookup, &core_cb, NULL);
+	if (err != 0) {
+		goto exit;
+	}
+
+	/* do not pass go until Slave(client) or Master */
+	while(cst) {
+		lg_core_corestate(gulm_cm.hookup);
+		err = lg_core_handle_messages (gulm_cm.hookup, &core_cb, &cst);
+		if (err != 0) {
+			goto exit;
+		}
+		if(cst) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout (3 * HZ);
+			/* TODO if interrupted, exit */
+		}
+	}
+
+	/* start recver thread. */
+	cm_thd_running = TRUE;
+	err = kernel_thread (cm_io_recving_thread, NULL, 0);
+	if (err < 0) {
+		log_err ("Failed to start gulm_res_recvd. (%d)\n", err);
+		goto exit;
+	}
+	wait_for_completion (&cm_thd_startup);
+
+	err = 0;
+      exit:
+	if (err > 0) err = - err;
+	return err;
+}
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_firstlock.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_firstlock.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_firstlock.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_firstlock.c	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,310 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/crc32.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include "handler.h"
+#include "gulm_lock_queue.h"
+
+extern gulm_cm_t gulm_cm;
+
+/****************************************************************************/
+struct gulm_flck_return_s {
+	int error;
+	struct completion sleep;
+};
+
+/**
+ * gulm_firstlock_finish - 
+ * @item: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_firstlock_finish (struct glck_req *item)
+{
+	struct gulm_flck_return_s *g = (struct gulm_flck_return_s *)item->misc;
+	g->error = item->error;
+	complete (&g->sleep);
+}
+
+/**
+ * gulm_cancel_firstlock - 
+ * @misc: 
+ * 
+ */
+void gulm_cancel_firstlock (void *misc)
+{
+	gulm_fs_t *fs = (gulm_fs_t *)misc;
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		log_err ("Out of memory, Cannot cancel Firstlock request.\n");
+		return;
+	}
+
+	/* after cancel is processed, glq will call kfree on item->key. */
+	item->key = kmalloc(GIO_KEY_SIZE, GFP_KERNEL);
+	if (item->key == NULL) {
+		glq_recycle_req(item);
+		log_err ("Out of memory, Cannot cancel Firstlock request.\n");
+		return;
+	}
+	item->keylen = pack_lock_key(item->key, GIO_KEY_SIZE, 'F',
+			fs->fs_name, "irstMount", 9);
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_cancel;
+	item->finish = NULL;
+
+	glq_cancel(item);
+}
+
+/**
+ * do_lock_time_out - 
+ * @d: 
+ *
+ * after timeout, set cancel request on the handler queue. (since we cannot
+ * call it from within the timer code. (socket io within interrupt space is
+ * bad.))
+ * 
+ */
+static void
+do_lock_time_out (unsigned long d)
+{
+	gulm_fs_t *fs = (gulm_fs_t *)d;
+	qu_function_call (&fs->cq, gulm_cancel_firstlock, fs);
+}
+
+/**
+ * get_mount_lock - 
+ * @fs: 
+ * @first: 
+ * 
+ * Get the Firstmount lock.
+ * We try to grab it Exl.  IF we get that, then we are the first client
+ * mounting this fs.  Otherwise we grab it shared to show that there are
+ * clients using this fs.
+ * 
+ * Returns: int
+ */
+int
+get_mount_lock (gulm_fs_t * fs, int *first)
+{
+	int err, keylen;
+	struct timer_list locktimeout;
+	struct gulm_flck_return_s gret;
+	uint8_t key[GIO_KEY_SIZE];
+	glckr_t *item;
+
+	keylen = pack_lock_key(key, GIO_KEY_SIZE, 'F', fs->fs_name, "irstMount", 9);
+	if( keylen <= 0 ) return keylen;
+
+
+      try_it_again:
+	*first = FALSE;		/* assume we're not first */
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	/* glq does not try to free the key for state or action requests. */
+	item->key = key;
+	item->keylen = keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_state;
+	item->state = lg_lock_state_Exclusive;
+	item->flags = lg_lock_flag_Try|lg_lock_flag_IgnoreExp|lg_lock_flag_NoCallBacks;
+	item->error = gret.error = 0;
+
+	init_completion (&gret.sleep);
+
+	item->misc = &gret;
+	item->finish = gulm_firstlock_finish;
+
+	glq_queue (item);
+	wait_for_completion (&gret.sleep);
+
+	if (gret.error == 0) {
+		/* we got the lock, we're the first mounter. */
+		*first = TRUE;
+		log_msg (lgm_locking, "fsid=%s: Got mount lock Exclusive.\n",
+			 fs->fs_name);
+		return 0;
+	} else {
+		log_msg (lgm_locking,
+			 "fsid=%s: Didn't get mount lock Exl, someone else "
+			 "was first, trying for shared.\n", fs->fs_name);
+
+		/* the try failed, pick it up shared.
+		 * If it takes too long, start over.
+		 * */
+		init_timer (&locktimeout);
+		locktimeout.function = do_lock_time_out;
+		locktimeout.data = (unsigned long)fs;
+		mod_timer (&locktimeout, jiffies + (120 * HZ));
+
+		item = glq_get_new_req();
+		if (item == NULL) {
+			err = -ENOMEM;
+			goto fail;
+		}
+
+		item->key = key;
+		item->keylen = keylen;
+		item->subid = 0;
+		item->start = 0;
+		item->stop = ~((uint64_t)0);
+		item->type = glq_req_type_state;
+		item->state = lg_lock_state_Shared;
+		item->flags = lg_lock_flag_NoCallBacks;
+		item->error = gret.error = 0;
+
+		init_completion (&gret.sleep);
+
+		item->misc = &gret;
+		item->finish = gulm_firstlock_finish;
+
+		glq_queue (item);
+		wait_for_completion (&gret.sleep);
+
+		del_timer (&locktimeout);
+
+		if (gret.error == 0) {
+			/* kewl we got it. */
+			log_msg (lgm_locking,
+				 "fsid=%s: Got mount lock shared.\n",
+				 fs->fs_name);
+			return 0;
+		}
+
+		log_msg (lgm_locking,
+			 "fsid=%s: Shared req timed out, trying Exl again.\n",
+			 fs->fs_name);
+		goto try_it_again;
+	}
+      fail:
+	log_err ("Exit get_mount_lock err=%d\n", err);
+	return err;
+}
+
+/**
+ * downgrade_mount_lock - 
+ * @fs: 
+ * 
+ * drop the Firstmount lock down to shared.  This lets others mount.
+ * 
+ * Returns: int
+ */
+int
+downgrade_mount_lock (gulm_fs_t * fs)
+{
+	int keylen;
+	struct gulm_flck_return_s gret;
+	uint8_t key[GIO_KEY_SIZE];
+	glckr_t *item;
+
+	keylen = pack_lock_key(key, GIO_KEY_SIZE, 'F',
+			fs->fs_name, "irstMount", 9);
+	if( keylen <= 0 ) return keylen;
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		return -ENOMEM;
+	}
+
+	item->key = key;
+	item->keylen = keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_state;
+	item->state = lg_lock_state_Shared;
+	item->flags = lg_lock_flag_NoCallBacks;
+	item->error = gret.error = 0;
+
+	init_completion (&gret.sleep);
+
+	item->misc = &gret;
+	item->finish = gulm_firstlock_finish;
+
+	glq_queue (item);
+	wait_for_completion (&gret.sleep);
+
+	if (gret.error != 0)
+		log_err ("fsid=%s: Couldn't unlock mount lock!!!!!! %d\n",
+			 fs->fs_name, gret.error);
+	return 0;
+}
+
+/**
+ * drop_mount_lock - drop our hold on the firstmount lock.
+ * @fs: <> the filesystem pointer.
+ * 
+ * Returns: int
+ */
+int
+drop_mount_lock (gulm_fs_t * fs)
+{
+	int keylen;
+	struct gulm_flck_return_s gret;
+	uint8_t key[GIO_KEY_SIZE];
+	glckr_t *item;
+
+	keylen = pack_lock_key(key, GIO_KEY_SIZE, 'F', fs->fs_name, "irstMount", 9);
+	if( keylen <= 0 ) return keylen;
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		return -ENOMEM;
+	}
+
+	item->key = key;
+	item->keylen = keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_state;
+	item->state = lg_lock_state_Unlock;
+	item->flags = 0;
+	item->error = gret.error = 0;
+
+	init_completion (&gret.sleep);
+
+	item->misc = &gret;
+	item->finish = gulm_firstlock_finish;
+
+	glq_queue (item);
+	wait_for_completion (&gret.sleep);
+
+	if (gret.error != 0)
+		log_err ("fsid=%s: Couldn't unlock mount lock!!!!!! %d\n",
+			 fs->fs_name, gret.error);
+	return 0;
+}
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_fs.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_fs.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_fs.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_fs.c	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,782 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/utsname.h>	/* for extern system_utsname */
+
+#include "handler.h"
+#include "gulm_lock_queue.h"
+
+/* things about myself */
+extern gulm_cm_t gulm_cm;
+
+/* globals for this file.*/
+uint32_t filesystems_count = 0;
+LIST_HEAD (filesystems_list);
+struct semaphore filesystem_lck;	/* we use a sema instead of a spin
+					 * here because all of the
+					 * interruptible things we do
+					 * inside of it.  If i stop doing
+					 * nasty things within this it
+					 * doesn't need to be a sema.
+					 */
+struct semaphore start_stop_lock;
+atomic_t start_stop_cnt;
+
+/**
+ * init_gulm_fs - 
+ */
+void
+init_gulm_fs (void)
+{
+	init_MUTEX (&filesystem_lck);
+	init_MUTEX (&start_stop_lock);
+	atomic_set (&start_stop_cnt, 0);
+}
+
+/*****************************************************************************/
+struct rjrpf_s {
+	gulm_fs_t *fs;
+	uint8_t *name;
+};
+
+void
+request_journal_replay_per_fs (void *d)
+{
+	struct rjrpf_s *rf = (struct rjrpf_s *) d;
+	uint32_t jid;
+	unsigned int ujid;
+
+	/* lookup jid <=> name mapping */
+	if (find_jid_by_name_and_mark_replay (rf->fs, rf->name, &jid) != 0) {
+		log_msg (lgm_JIDMap,
+			 "In fs (%s), no jid for name (%s) was found.\n",
+			 rf->fs->fs_name, rf->name);
+	} else {
+		log_msg (lgm_JIDMap,
+			 "In fs (%s), jid %d was found for name (%s).\n",
+			 rf->fs->fs_name, jid, rf->name);
+
+		/* all that the replay journal call back into gfs does is
+		 * malloc some memory and add it to a list.  So we really
+		 * don't need to queue that action.  Since that is what gfs
+		 * is doing.
+		 *
+		 * This will need to change if gfs changes.
+		 *
+		 * Basically, we assume that the callback is non-blocking.
+		 */
+		ujid = jid;
+		rf->fs->cb (rf->fs->fsdata, LM_CB_NEED_RECOVERY, &ujid);
+	}
+
+	kfree (rf->name);
+	kfree (rf);
+
+}
+
+/**
+ * request_journal_replay - give a journal replay request to mounted filesystems
+ * @name: < the name of the node that died.
+ * 
+ * 
+ * Returns: void
+ */
+void
+request_journal_replay (uint8_t * name)
+{
+	struct list_head *tmp;
+	gulm_fs_t *fs;
+	struct rjrpf_s *rf;
+
+	log_msg (lgm_Always, "Checking for journals for node \"%s\"\n",
+		 name);
+
+	down (&filesystem_lck);
+
+	list_for_each (tmp, &filesystems_list) {
+		fs = list_entry (tmp, gulm_fs_t, fs_list);
+
+		/* we don't want to process replay requests when we are
+		 * still in the first mounter state.  All the journals are
+		 * getting replayed anyways, and there could be some issue
+		 * with stuff happening twice.
+		 */
+		if (fs->firstmounting)
+			continue;
+
+		/* due to the way the new jid mapping code works, we had to
+		 * move it out of here.
+		 * (making calls to the lock server.  Things can deadlock
+		 * if the jid mapping calls are made from this thread of
+		 * execution.)
+		 *
+		 * I need to look over this.  There HAS to be a better way
+		 * to manage the way we figgure out which journals gfs
+		 * needs to replay.
+		 */
+
+		rf = kmalloc (sizeof (struct rjrpf_s), GFP_KERNEL);
+		GULM_ASSERT (rf != NULL,);
+
+		rf->fs = fs;
+		rf->name = kmalloc (strlen (name) + 1, GFP_KERNEL);
+		GULM_ASSERT (rf->name != NULL,);
+		memcpy (rf->name, name, strlen (name) + 1);
+
+		qu_function_call (&fs->cq, request_journal_replay_per_fs, rf);
+
+	}
+	up (&filesystem_lck);
+}
+
+/* can you say kludges kids?!  I *knew* you could. */
+void
+cast_check_for_stale_expires(void*d)
+{
+	check_for_stale_expires(d);
+}
+/**
+ * check_all_for_stales - 
+ * 
+ * scan every fs we've got for possible journals that need replaying.
+ * 
+ */
+void
+check_all_for_stales(void)
+{
+	struct list_head *tmp;
+	gulm_fs_t *fs;
+	down (&filesystem_lck);
+
+	list_for_each (tmp, &filesystems_list) {
+		fs = list_entry (tmp, gulm_fs_t, fs_list);
+		if (!fs->firstmounting) {
+			qu_function_call (&fs->cq, cast_check_for_stale_expires, fs);
+		}
+	}
+	up (&filesystem_lck);
+}
+
+/**
+ * passup_droplocks - 
+ */
+void
+passup_droplocks (void)
+{
+	struct list_head *tmp;
+	gulm_fs_t *fs;
+	down (&filesystem_lck);
+	list_for_each (tmp, &filesystems_list) {
+		fs = list_entry (tmp, gulm_fs_t, fs_list);
+		qu_drop_req (&fs->cq, fs->cb, fs->fsdata, LM_CB_DROPLOCKS, 0,
+			     0);
+		/* If this decides to block someday, we need to change this
+		 * function.
+		 */
+	}
+	up (&filesystem_lck);
+}
+
+#if 0
+/**
+ * dump_internal_lists - 
+ * 
+ */
+void
+dump_internal_lists (void)
+{
+	struct list_head *tmp;
+	gulm_fs_t *fs;
+	down (&filesystem_lck);
+	list_for_each (tmp, &filesystems_list) {
+		fs = list_entry (tmp, gulm_fs_t, fs_list);
+		log_msg (lgm_Always, "Handler queue for %s\n", fs->fs_name);
+		display_handler_queue (&fs->cq);
+		/* other lists? */
+	}
+	up (&filesystem_lck);
+}
+#endif
+
+/**
+ * get_fs_by_name - 
+ * @name: 
+ * 
+ * 
+ * Returns: gulm_fs_t
+ */
+gulm_fs_t *
+get_fs_by_name (uint8_t * name)
+{
+	struct list_head *tmp;
+	gulm_fs_t *fs = NULL;
+	down (&filesystem_lck);
+	list_for_each (tmp, &filesystems_list) {
+		fs = list_entry (tmp, gulm_fs_t, fs_list);
+		if (strcmp (name, fs->fs_name) == 0) {
+			up (&filesystem_lck);
+			return fs;
+		}
+	}
+	up (&filesystem_lck);
+	return NULL;
+}
+
+/*****************************************************************************/
+
+/**
+ * start_gulm_threads - 
+ * @host_data: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+start_gulm_threads (char *csnm, char *hostdata)
+{
+	int error = 0;
+
+	down (&start_stop_lock);
+	atomic_inc (&start_stop_cnt);
+	if (atomic_read (&start_stop_cnt) == 1) {
+		/* first one. get stuff going */
+		strncpy (gulm_cm.clusterID, csnm, 255);
+		gulm_cm.clusterID[255] = '\0';
+
+		if (hostdata != NULL && strlen (hostdata) > 0) {
+			strncpy (gulm_cm.myName, hostdata, 64);
+		} else {
+			strncpy (gulm_cm.myName, system_utsname.nodename, 64);
+		}
+		gulm_cm.myName[63] = '\0';
+
+
+		error = lg_initialize (&gulm_cm.hookup, gulm_cm.clusterID,
+				       "GFS Kernel Interface");
+		if (error != 0) {
+			log_err ("lg_initialize failed, %d\n", error);
+			goto fail;
+		}
+		gulm_cm.starts = TRUE;
+
+		/* breaking away from ccs. just hardcoding defaults here.
+		 * Noone really used these anyways and if ppl want them
+		 * badly, we'll find another way to set them. (modprobe
+		 * options for example. or maybe sysfs?)
+		 * ppl want verbosity
+		 * */
+		gulm_cm.handler_threads = 2;
+		gulm_cm.verbosity = lgm_Network ;
+
+		error = cm_login ();
+		if (error != 0) {
+			log_err ("cm_login failed. %d\n", error);
+			goto fail;
+		}
+		error = glq_startup ();
+		if (error != 0) {
+			log_err ("glq_startup failed. %d\n", error);
+			goto fail;
+		}
+
+	}
+      fail:
+	up (&start_stop_lock);
+	return error;
+}
+
+/**
+ * stop_gulm_threads - 
+ */
+void
+stop_gulm_threads (void)
+{
+	down (&start_stop_lock);
+	atomic_dec (&start_stop_cnt);
+	if (atomic_read (&start_stop_cnt) == 0) {
+		/* last one, put it all away. */
+		glq_shutdown ();
+		cm_logout ();
+		lg_release (gulm_cm.hookup);
+		gulm_cm.hookup = NULL;
+		gulm_cm.GenerationID = 0;
+	}
+	up (&start_stop_lock);
+}
+
+/*****************************************************************************/
+/**
+ * send_drop_exp - 
+ * @fs: 
+ * @name: 
+ * 
+ * 
+ * Returns: int
+ */
+int send_drop_exp (gulm_fs_t * fs, char *name)
+{
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		log_err("drop_exp: failed to get needed memory. skipping.\n");
+		return -ENOMEM;
+	}
+
+	item->keylen = 3 + strlen(fs->fs_name);
+	item->key = kmalloc(item->keylen, GFP_KERNEL);
+	if (item->key == NULL) {
+		glq_recycle_req(item);
+		log_err("drop_exp: failed to get needed memory. skipping.\n");
+		return -ENOMEM;
+	}
+	item->keylen = pack_drop_mask(item->key, item->keylen, fs->fs_name);
+
+	/* lvb is name for drops. */
+	if (name != NULL) {
+		item->lvblen = strlen(name) +1;
+		item->lvb = kmalloc(item->lvblen, GFP_KERNEL);
+		if (item->lvb == NULL) {
+			glq_recycle_req(item); /* frees key for us */
+			log_err("drop_exp: failed to get needed memory. skipping.\n");
+			return -ENOMEM;
+		}
+		memcpy(item->lvb, name, item->lvblen);
+	} else {
+		item->lvb = NULL;
+		item->lvblen = 0;
+	}
+
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_drop;
+	item->state = 0;
+	item->flags = 0;
+	item->error = 0;
+	item->finish = NULL;
+
+	glq_queue (item);
+
+	return 0;
+}
+/**
+ * expire_my_locks - 
+ * @fs: 
+ * 
+ * 
+ * Returns: int
+ */
+int expire_my_locks (gulm_fs_t * fs)
+{
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		log_err("drop_exp: failed to get needed memory. skipping.\n");
+		return -ENOMEM;
+	}
+
+	item->keylen = 3 + strlen(fs->fs_name);
+	item->key = kmalloc(item->keylen, GFP_KERNEL);
+	if (item->key == NULL) {
+		glq_recycle_req(item);
+		log_err("drop_exp: failed to get needed memory. skipping.\n");
+		return -ENOMEM;
+	}
+	item->keylen = pack_drop_mask(item->key, item->keylen, fs->fs_name);
+
+	/* lvb is name for drops. */
+	item->lvblen = strlen(gulm_cm.myName) +1;
+	item->lvb = kmalloc(item->lvblen, GFP_KERNEL);
+	if (item->lvb == NULL) {
+		glq_recycle_req(item); /* frees key for us */
+		log_err("drop_exp: failed to get needed memory. skipping.\n");
+		return -ENOMEM;
+	}
+	memcpy(item->lvb, gulm_cm.myName, item->lvblen);
+
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_expire;
+	item->state = 0;
+	item->flags = 0;
+	item->error = 0;
+	item->finish = NULL;
+
+	glq_queue (item);
+
+	return 0;
+}
+/*****************************************************************************/
+
+/**
+ * gulm_check_replays - 
+ * @misc: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_check_replays(void *misc)
+{
+	gulm_fs_t *fs = (gulm_fs_t*)misc;
+	qu_function_call (&fs->cq, cast_check_for_stale_expires, fs);
+}
+
+/**
+ * gulm_mount
+ * @table_name: clusterID:FS_Name
+ * @host_data:
+ * @cb: GFS callback function
+ * @fsdata: opaque GFS handle
+ * @lockstruct: the structure of crap to fill in
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+int
+gulm_mount (char *table_name, char *host_data,
+	    lm_callback_t cb, lm_fsdata_t * fsdata,
+	    unsigned int min_lvb_size, struct lm_lockstruct *lockstruct)
+{
+	gulm_fs_t *gulm;
+	char *work=NULL, *tbln;
+	int first;
+	int error = -1;
+	struct list_head *lltmp;
+
+	work = kmalloc(256, GFP_KERNEL);
+	if(work == NULL ) {
+		log_err("Out of Memory.\n");
+		error = -ENOMEM;
+		goto fail;
+	}
+	strncpy (work, table_name, 256);
+
+	tbln = strstr (work, ":");
+	if (tbln == NULL) {
+		log_err
+		    ("Malformed table name. Couldn't find separator ':' between "
+		     "clusterID and lockspace name.\n");
+		error = -1;
+		goto fail;
+	}
+	*tbln++ = '\0';
+
+	/* make sure that the cluster name exists. */
+	if (strlen (work) <= 0) {
+		log_err ("Cluster name \"%s\" is too short.\n", work);
+		error = -EPROTO;
+		goto fail;
+	}
+	if (strlen (work) > 16) {
+		log_err ("Cluster name \"%s\" is too long.\n", work);
+		error = -EPROTO;
+		goto fail;
+	}
+
+	/* the second one is an artifact of the way I use the name.  
+	 * A better fix to this will happen when I actually get dynamic key
+	 * lengths working.
+	 */
+	if (strlen (tbln) > MIN (GIO_NAME_LEN, (GIO_KEY_SIZE - 15))) {
+		log_err
+		    ("Warning! lockspace name (%s) is longer than %d chars!\n",
+		     tbln, MIN (GIO_NAME_LEN, (GIO_KEY_SIZE - 15)));
+		error = -EPROTO;
+		goto fail;
+	}
+	if (strlen (tbln) <= 0) {
+		log_err ("Table name \"%s\" is too short.\n", tbln);
+		error = -EPROTO;
+		goto fail;
+	}
+
+	/*  Check to make sure this lock table isn't already being used  */
+	down (&filesystem_lck);
+	list_for_each (lltmp, &filesystems_list) {
+		gulm = list_entry (lltmp, gulm_fs_t, fs_list);
+		if (!strncmp (gulm->fs_name, tbln, GIO_NAME_LEN)) {
+			log_err ("\"%s\" is already in use\n", tbln);
+			error = -EEXIST;
+			up (&filesystem_lck);
+			goto fail;
+		}
+	}
+	up (&filesystem_lck);
+
+	/*  Set up our main structure  */
+
+	gulm = kmalloc (sizeof (gulm_fs_t), GFP_KERNEL);
+	if (!gulm) {
+		log_err ("out of memory\n");
+		error = -ENOMEM;
+		goto fail;
+	}
+	memset (gulm, 0, sizeof (gulm_fs_t));
+
+	INIT_LIST_HEAD (&gulm->fs_list);
+
+	strncpy (gulm->fs_name, tbln, GIO_NAME_LEN);
+	gulm->cb = cb;
+	gulm->fsdata = fsdata;
+	gulm->lvb_size = min_lvb_size;
+
+	if ((error = start_gulm_threads (work, host_data)) != 0) {
+		log_err ("Got a %d trying to start the threads.\n", error);
+		goto fail_free_gulm;
+	}
+
+	if ((error =
+	     start_callback_qu (&gulm->cq, gulm_cm.handler_threads)) < 0) {
+		log_err ("fsid=%s: Failed to start the callback handler.\n",
+			 gulm->fs_name);
+		goto fail_free_gulm;
+	}
+
+	/* the mount lock HAS to be the first thing done in the LTs for this fs. */
+	error = get_mount_lock (gulm, &first);
+	if (error != 0) {
+		log_err("fsid=%s: Error %d while trying to get the mount lock\n",
+		     gulm->fs_name, error);
+		goto fail_callback;
+	}
+
+	error = watch_sig(gulm, gulm->fs_name, strlen(gulm->fs_name)+1, gulm_check_replays, gulm);
+	if( error != 0 ) {
+		log_err("fsid=%s: couldn't watch CFR because %d\n",
+				gulm->fs_name, error);
+		goto fail_mountlock;
+	}
+
+	jid_fs_init (gulm);
+	get_journalID (gulm);
+
+	/* things act a bit different until the first mounter is finished.
+	 */
+	if (first)
+		gulm->firstmounting = TRUE;
+
+	/*  Success  */
+	down (&filesystem_lck);
+	list_add (&gulm->fs_list, &filesystems_list);
+	filesystems_count++;
+	up (&filesystem_lck);
+
+	log_msg (lgm_JIDMap, "fsid=%s: We will be using jid %d\n",
+		 gulm->fs_name, gulm->fsJID);
+
+	lockstruct->ls_jid = gulm->fsJID;
+	lockstruct->ls_first = first;
+	lockstruct->ls_lvb_size = gulm->lvb_size;
+	lockstruct->ls_lockspace = gulm;
+	lockstruct->ls_ops = &gulm_ops;
+	lockstruct->ls_flags = 0;
+	log_msg (lgm_Network2, "Done: %s, async mode\n", table_name);
+
+	gulm_cm.starts = FALSE;
+	if(work != NULL ) kfree(work);
+	return 0;
+
+fail_mountlock:
+	drop_mount_lock (gulm);
+
+      fail_callback:
+	stop_callback_qu (&gulm->cq);
+
+      fail_free_gulm:
+	stop_gulm_threads ();
+	kfree (gulm);
+
+      fail:
+
+	if(work != NULL ) kfree(work);
+	gulm_cm.starts = FALSE;
+	log_msg (lgm_Always, "fsid=%s: Exiting gulm_mount with errors %d\n",
+		 table_name, error);
+	/* VFS does weird things with the error results, so before we try
+	 * to return a gulm error code, flip it to -1.
+	 */
+	if (error > 999 || error < -999 ) error = -1;
+	return error;
+}
+
+/**
+ * gulm_others_may_mount
+ * @lockspace: handle to specific lock space
+ *
+ * GFS calls this function if it was the first mounter after it's done
+ * checking all the journals.
+ *
+ */
+void
+gulm_others_may_mount (lm_lockspace_t * lockspace)
+{
+	gulm_fs_t *fs = (gulm_fs_t *) lockspace;
+	int err = 0;
+
+	/* first send the drop all exp message.
+	 * */
+	err = send_drop_exp (fs, NULL);
+	if (err < 0)
+		log_err
+		    ("fsid=%s: Problems sending DropExp request to LTPX: %d\n",
+		     fs->fs_name, err);
+
+	/* then move the FirstMountLock to shared so others can mount. */
+	err = downgrade_mount_lock (fs);
+
+	if (err < 0) {
+		log_err ("fsid=%s: error sending Fs_FinMount_Req.(%d)\n",
+			 fs->fs_name, err);
+	}
+
+	/* first mounter is all done.  let the gulm_recovery_done function
+	 * behave as normal now.
+	 */
+	fs->firstmounting = FALSE;
+}
+
+/**
+ * gulm_umount
+ * @lockspace: handle to specific lock space
+ *
+ */
+void
+gulm_unmount (lm_lockspace_t * lockspace)
+{
+	gulm_fs_t *gulm_fs = (gulm_fs_t *) lockspace;
+
+	down (&filesystem_lck);
+	list_del (&gulm_fs->fs_list);
+	--filesystems_count;
+	up (&filesystem_lck);
+
+	/* close and release stuff */
+	watch_sig(gulm_fs, gulm_fs->fs_name, strlen(gulm_fs->fs_name)+1, NULL, NULL);
+	drop_mount_lock (gulm_fs);
+	put_journalID (gulm_fs, FALSE);
+	jid_fs_release (gulm_fs);
+
+	stop_callback_qu (&gulm_fs->cq);
+
+	kfree (gulm_fs);
+
+	stop_gulm_threads ();
+
+}
+
+/**
+ * gulm_withdraw
+ * @lockspace: handle to specific lock space
+ *
+ */
+void
+gulm_withdraw(lm_lockspace_t * lockspace)
+{
+	gulm_fs_t *gulm_fs = (gulm_fs_t *) lockspace;
+	down (&filesystem_lck);
+	list_del (&gulm_fs->fs_list);
+	--filesystems_count;
+	up (&filesystem_lck);
+
+	/* close and release stuff */
+	drop_mount_lock (gulm_fs);
+	/* leave this around for others to clean up.
+	 * marking myself as being replayed right away so in bad cases,
+	 * atleast check_for_stale will find us.
+	 * */
+	put_journalID (gulm_fs, TRUE);
+	jid_fs_release (gulm_fs);
+
+	stop_callback_qu (&gulm_fs->cq);
+
+	expire_my_locks (gulm_fs);
+	tap_sig(gulm_fs, gulm_fs->fs_name, strlen(gulm_fs->fs_name)+1);
+
+	/* need to let things run through the queues.
+	 * Only really an issue if you happen to be the only gfs/gulm fs
+	 * mounted.
+	 * */
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout( 2 * HZ);
+
+	kfree (gulm_fs);
+
+	stop_gulm_threads ();
+}
+
+/**
+ * gulm_recovery_done - 
+ * @lockspace: 
+ * @jid: 
+ * 
+ * Returns: void
+ */
+void
+gulm_recovery_done (lm_lockspace_t * lockspace, unsigned int jid,
+		    unsigned int message)
+{
+	gulm_fs_t *fs = (gulm_fs_t *) lockspace;
+	int err;
+	uint8_t *name=NULL;
+
+	if (message != LM_RD_SUCCESS) {
+		/* Need to start thinking about how I want to use this... */
+		goto exit;
+	}
+
+	name = kmalloc(64, GFP_KERNEL);
+	if(name == NULL) {
+		log_err("out of memory.\n");
+		goto exit;
+	}
+
+	if (jid == fs->fsJID) {	/* this may be drifting crud through. */
+		/* hey! its me! */
+		strncpy (name, gulm_cm.myName, 64);
+	} else if (lookup_name_by_jid (fs, jid, name) != 0) {
+		log_msg (lgm_JIDMap,
+			 "fsid=%s: Could not find a client for jid %d\n",
+			 fs->fs_name, jid);
+		goto exit;
+	}
+	if (strlen (name) == 0) {
+		log_msg (lgm_JIDMap, "fsid=%s: No one mapped to jid %d\n",
+			 fs->fs_name, jid);
+		goto exit;
+	}
+	log_msg (lgm_JIDMap, "fsid=%s: Found %s for jid %d\n",
+		 fs->fs_name, name, jid);
+
+	err = send_drop_exp (fs, name);
+
+	if (jid != fs->fsJID) {
+		/* rather dumb to do this to ourselves right after we mount... */
+		log_msg (lgm_JIDMap,
+			 "fsid=%s: Clearing JID %d for use by others\n",
+			 fs->fs_name, jid);
+		release_JID (fs, jid);
+	}
+
+	/* If someone died while replaying someoneelse's journal, there will be
+	 * stale expired jids.
+	 */
+	check_for_stale_expires (fs);
+
+exit:
+	if(name!=NULL) kfree(name);
+}
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_jid.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_jid.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_jid.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_jid.c	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,651 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include "gulm_lock_queue.h"
+
+extern gulm_cm_t gulm_cm;
+
+/****************************************************************************/
+
+/* jid locks:
+ *
+ * Header lock: "JHeader" + \0\0\0 + fsname
+ *         lvb: <uint32> :number of JIDs
+ * Mappinglock: "JM" + <uint32> + \0\0\0\0 + fsname
+ *         lvb: [012] + <node name>
+ *              0: unused
+ *              1: replaying journal
+ *              2: Mounted
+ * list lock  : "JL" + "listlock" + fsname
+ * Node Locks : "JN" + <nodename[8]> + fsname
+ *
+ */
+#define jid_header_lvb_size (8)
+
+/**
+ * jid_get_header_name - 
+ * @fs: <
+ * @key: <>
+ * @keylen: <> 
+ * 
+ * key is buffer to write to, keylen is size of buffer on input, and real
+ * length on output.
+ * 
+ * Returns: int
+ */
+int
+jid_get_header_name (uint8_t * fsname, uint8_t * key, uint16_t * keylen)
+{
+	int len;
+
+	len = pack_lock_key(key, *keylen, 'J', fsname, "Header", 6);
+	if( len <=0 ) return len;
+
+	*keylen = len;
+
+	return 0;
+}
+
+/**
+ * jid_get_lock_name - 
+ * @fs: <
+ * @jid: <
+ * @key: <>
+ * @keylen: <>
+ * 
+ * key is buffer to write to, keylen is size of buffer on input, and real
+ * length on output.
+ * 
+ * Returns: int
+ */
+int
+jid_get_lock_name (uint8_t * fsname, uint32_t jid, uint8_t * key,
+		   uint16_t * keylen)
+{
+	int len;
+	uint8_t temp[9];
+
+	temp[0] = 'M';
+	temp[1] = (jid >> 0) & 0xff;
+	temp[2] = (jid >> 8) & 0xff;
+	temp[3] = (jid >> 16) & 0xff;
+	temp[4] = (jid >> 24) & 0xff;
+	temp[5] = 0;
+	temp[6] = 0;
+	temp[7] = 0;
+	temp[8] = 0;
+
+	len = pack_lock_key(key, *keylen, 'J', fsname, temp, 9);
+	if( len <=0 ) return len;
+
+	*keylen = len;
+
+	return 0;
+}
+
+/**
+ * gulm_jid_finish - 
+ * @item: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_jid_finish (struct glck_req *item)
+{
+	struct completion *sleep = (struct completion *)item->misc;
+	complete (sleep);
+}
+
+/**
+ * jid_lvb_action - 
+ * @key: 
+ * @keylen: 
+ * @lvb: 
+ * @lvblen: 
+ * @action: 
+ * 
+ * 
+ * Returns: void
+ */
+void jid_lvb_action (uint8_t * key, uint16_t keylen, uint8_t * lvb,
+		uint16_t lvblen, uint8_t action)
+{
+	struct completion sleep;
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		return;
+	}
+
+	item->key = key;
+	item->keylen = keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_action;
+	item->state = action;
+	item->flags = 0;
+	item->error =  0;
+	item->lvb = lvb;
+	item->lvblen = lvblen;
+
+	init_completion (&sleep);
+
+	item->misc = &sleep;
+	item->finish = gulm_jid_finish;
+
+	glq_queue (item);
+	wait_for_completion (&sleep);
+}
+void
+jid_sync_lvb (uint8_t * key, uint16_t keylen, uint8_t * lvb, uint16_t lvblen)
+{
+	jid_lvb_action (key, keylen, lvb, lvblen, lg_lock_act_SyncLVB);
+}
+void
+jid_unhold_lvb (uint8_t * key, uint16_t keylen)
+{
+	jid_lvb_action (key, keylen, NULL, 0, lg_lock_act_UnHoldLVB);
+}
+void
+jid_hold_lvb (uint8_t * key, uint16_t keylen)
+{
+	jid_lvb_action (key, keylen, NULL, 0, lg_lock_act_HoldLVB);
+}
+
+
+/**
+ * jid_get_lock_state_inr - 
+ * @key: 
+ * @keylen: 
+ * @state: 
+ * @flags:
+ * @lvb: 
+ * @lvblen: 
+ * 
+ * 
+ */
+void
+jid_get_lock_state_inr (uint8_t * key, uint16_t keylen, uint8_t state,
+			uint32_t flags, uint8_t * lvb, uint16_t lvblen)
+{
+	struct completion sleep;
+	glckr_t *item;
+	GULM_ASSERT (keylen > 6,
+			printk("keylen: %d\n", keylen););
+
+	init_completion (&sleep);
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		return;
+	}
+
+	item->key = key;
+	item->keylen = keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_state;
+	item->state = state;
+	item->flags = flags;
+	item->error =  0;
+	item->lvb = lvb;
+	item->lvblen = lvblen;
+
+	item->misc = &sleep;
+	item->finish = gulm_jid_finish;
+
+	glq_queue (item);
+
+	wait_for_completion (&sleep);
+}
+
+/**
+ * jid_get_lock_state_lvb - 
+ * @key: 
+ * @keylen: 
+ * @state: 
+ * @lvb: 
+ * @lvblen: 
+ * 
+ * 
+ */
+void
+jid_get_lock_state_lvb (uint8_t * key, uint16_t keylen, uint8_t state,
+			uint8_t * lvb, uint16_t lvblen)
+{
+	jid_get_lock_state_inr (key, keylen, state, 0, lvb, lvblen);
+}
+/**
+ * jid_get_lock_state - 
+ * @key: 
+ * @keylen: 
+ * @state: 
+ * 
+ * 
+ */
+void
+jid_get_lock_state (uint8_t * key, uint16_t keylen, uint8_t state)
+{
+	jid_get_lock_state_inr (key, keylen, state, 0, NULL, 0);
+}
+
+/****************************************************************************/
+
+/**
+ * jid_rehold_lvbs - 
+ * @fs: 
+ * 
+ * 
+ */
+void
+jid_rehold_lvbs (gulm_fs_t * fs)
+{
+	int i;
+	uint32_t oldjcnt;
+	uint8_t key[GIO_KEY_SIZE], lvb[jid_header_lvb_size];
+	uint16_t keylen = GIO_KEY_SIZE;
+
+	oldjcnt = fs->JIDcount;
+
+	jid_get_header_name (fs->fs_name, key, &keylen);
+	jid_get_lock_state_lvb (key, keylen, lg_lock_state_Shared, lvb,
+				jid_header_lvb_size);
+	fs->JIDcount = (uint32_t) (lvb[0]) << 0;
+	fs->JIDcount |= (uint32_t) (lvb[1]) << 8;
+	fs->JIDcount |= (uint32_t) (lvb[2]) << 16;
+	fs->JIDcount |= (uint32_t) (lvb[3]) << 24;
+
+	if( fs->JIDcount > oldjcnt ) {
+		for (i = oldjcnt; i < fs->JIDcount; i++) {
+			keylen = sizeof (key);
+			jid_get_lock_name (fs->fs_name, i, key, &keylen);
+			jid_hold_lvb (key, keylen);
+		}
+	}
+
+}
+
+void
+jid_grow_space (gulm_fs_t * fs)
+{
+	uint8_t key[GIO_KEY_SIZE], lvb[jid_header_lvb_size];
+	uint16_t keylen = GIO_KEY_SIZE;
+	uint32_t jidc;
+
+	keylen = sizeof (key);
+	jid_get_header_name (fs->fs_name, key, &keylen);
+	down (&fs->headerlock);
+	jid_get_lock_state_lvb (key, keylen, lg_lock_state_Exclusive, lvb,
+				jid_header_lvb_size);
+	jidc = (uint32_t) (lvb[0]) << 0;
+	jidc |= (uint32_t) (lvb[1]) << 8;
+	jidc |= (uint32_t) (lvb[2]) << 16;
+	jidc |= (uint32_t) (lvb[3]) << 24;
+	jidc += 1;
+	lvb[3] = (jidc >> 24) & 0xff;
+	lvb[2] = (jidc >> 16) & 0xff;
+	lvb[1] = (jidc >> 8) & 0xff;
+	lvb[0] = (jidc >> 0) & 0xff;
+	jid_sync_lvb (key, keylen, lvb, jid_header_lvb_size);
+	jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+	/* do an unlock here, so that when rehold grabs it shared, there is no
+	 * lvb writing. yeah, bit icky.  fix some other day.
+	 */
+
+	jid_rehold_lvbs (fs);
+	up (&fs->headerlock);
+}
+
+/**
+ * lookup_name_by_jid - 
+ * @fs: 
+ * @jid: 
+ * @name: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lookup_name_by_jid (gulm_fs_t * fs, uint32_t jid, uint8_t * name)
+{
+	uint8_t key[GIO_KEY_SIZE], lvb[64];
+	uint16_t keylen = GIO_KEY_SIZE;
+	int err = 0;
+
+	if (jid >= fs->JIDcount) {
+		err = -1;
+		goto exit;
+	}
+
+	jid_get_lock_name (fs->fs_name, jid, key, &keylen);
+	jid_get_lock_state_inr (key, keylen, lg_lock_state_Exclusive,
+				lg_lock_flag_IgnoreExp, lvb, 64);
+
+	if (lvb[0] != 0) {
+		memcpy (name, &lvb[1], strlen (&lvb[1]) + 1);
+	} else {
+		err = -1;
+	}
+
+	jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+
+      exit:
+	return err;
+}
+
+/**
+ * Release_JID - 
+ * @fs: 
+ * @jid: 
+ * 
+ * This is called when a node replays someone else's journal.
+ * 
+ */
+void
+release_JID (gulm_fs_t * fs, uint32_t jid)
+{
+	uint8_t key[GIO_KEY_SIZE], lvb[64];
+	uint16_t keylen = GIO_KEY_SIZE;
+
+	/* there is no such, so this becomes a nop. */
+	if (jid >= fs->JIDcount)
+		return;
+
+	jid_get_lock_name (fs->fs_name, jid, key, &keylen);
+	jid_get_lock_state_inr (key, keylen, lg_lock_state_Exclusive,
+				lg_lock_flag_IgnoreExp, lvb, 64);
+	if (lvb[0] == 1 ) {
+		/* if byte0 is 2, then that node is alive.  They're waiting
+		 * for us to finish, but once we're done, it would be mean
+		 * to mark their jid as free.  So we leave the byte alone.
+		 *
+		 * Actually, If the byte isn't 1 (which means we are
+		 * replaying the journal) don't change it.
+		 *
+		 * Remind: 0 = free, 1 = replaying, 2 = owned.
+		 */
+		lvb[0] = 0;
+		jid_sync_lvb (key, keylen, lvb, strlen (&lvb[1]) + 2);
+	}
+	jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+
+}
+
+/**
+ * put_journalID - 
+ * @fs: 
+ * 
+ * This is called when this node unmounts or withdraws.
+ * 
+ */
+void
+put_journalID (gulm_fs_t * fs, int leavebehind)
+{
+	uint8_t key[GIO_KEY_SIZE], lvb[64];
+	uint16_t keylen = GIO_KEY_SIZE;
+
+	/* there is no such, so this becomes a nop. */
+	if (fs->fsJID >= fs->JIDcount)
+		return;
+
+	jid_get_lock_name (fs->fs_name, fs->fsJID, key, &keylen);
+	jid_get_lock_state_inr (key, keylen, lg_lock_state_Exclusive,
+				lg_lock_flag_IgnoreExp, lvb, 64);
+	if(leavebehind)
+		lvb[0] = 1;
+	else
+		lvb[0] = 0;
+	jid_sync_lvb (key, keylen, lvb, strlen (&lvb[1]) + 2);
+	jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+}
+
+/**
+ * get_journalID - 
+ * @fs: 
+ * @jid: 
+ * 
+ * grab EXL on names until we find one we want. (or have all.)
+ * grab that one.
+ * Unlock everything we got.
+ * 
+ * Returns: int
+ */
+void
+get_journalID (gulm_fs_t * fs)
+{
+	uint8_t key[GIO_KEY_SIZE], lvb[64];
+	uint16_t keylen = GIO_KEY_SIZE;
+	int i, first_clear = -1, lockedto;
+
+retry:
+	/* find an empty space, or ourselves again */
+	for (i = 0, lockedto = 0; i < fs->JIDcount; i++, lockedto++) {
+		keylen = sizeof (key);
+		jid_get_lock_name (fs->fs_name, i, key, &keylen);
+		jid_get_lock_state_inr (key, keylen, lg_lock_state_Exclusive,
+					lg_lock_flag_IgnoreExp, lvb, 64);
+		if (first_clear == -1 && lvb[0] == 0 ) {
+			first_clear = i;
+		} else if (strcmp (gulm_cm.myName, &lvb[1]) == 0) {
+			first_clear = i;
+			break;
+		}
+	}
+	if (first_clear >= 0) {
+		/* we should be hold all jid mapping locks up to this one
+		 * (and maybe beyond) EXL, so just lvb sync to the one we
+		 * want.
+		 */
+		lvb[0] = 2;
+		memcpy (&lvb[1], gulm_cm.myName, strlen (gulm_cm.myName) + 1);
+
+		keylen = sizeof (key);
+		jid_get_lock_name (fs->fs_name, first_clear, key, &keylen);
+		jid_sync_lvb (key, keylen, lvb, strlen (gulm_cm.myName) + 2);
+
+		fs->fsJID = first_clear;
+	}
+
+	/* unlock them so others can find */
+	for (; lockedto >= 0; lockedto--) {
+		keylen = sizeof (key);
+		jid_get_lock_name (fs->fs_name, lockedto, key, &keylen);
+		jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+	}
+
+	if (first_clear < 0) {
+		jid_grow_space (fs);
+		goto retry;
+	}
+}
+
+/**
+ * find_jid_by_name_and_mark_replay - 
+ * @fs: 
+ * @name: 
+ * @jid: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+find_jid_by_name_and_mark_replay (gulm_fs_t * fs, uint8_t * name,
+				  uint32_t * jid)
+{
+	uint32_t i, found = -1;
+	uint8_t key[GIO_KEY_SIZE], lvb[64];
+	uint16_t keylen = GIO_KEY_SIZE;
+
+	down (&fs->headerlock); /*???*/
+	for (i = 0; i < fs->JIDcount; i++) {
+		keylen = sizeof (key);
+		jid_get_lock_name (fs->fs_name, i, key, &keylen);
+		jid_get_lock_state_inr (key, keylen, lg_lock_state_Exclusive,
+					lg_lock_flag_IgnoreExp, lvb, 64);
+		if (strcmp (name, &lvb[1]) == 0) {
+			*jid = i;
+			found = 0;
+			lvb[0] = 1;
+			jid_sync_lvb (key, keylen, lvb, strlen (&lvb[1]) + 2);
+			jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+			break;
+		}
+		jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+
+	}
+	up (&fs->headerlock);
+
+	return found;
+}
+
+/**
+ * Check_for_replays - 
+ * @fs: 
+ * 
+ * 
+ * Returns: int
+ */
+void
+check_for_stale_expires (gulm_fs_t * fs)
+{
+	uint32_t i;
+	uint8_t key[GIO_KEY_SIZE], lvb[64];
+	uint16_t keylen = GIO_KEY_SIZE;
+	unsigned int ujid;
+
+	down (&fs->headerlock); /*???*/
+	for (i = 0; i < fs->JIDcount; i++) {
+		keylen = sizeof (key);
+		jid_get_lock_name (fs->fs_name, i, key, &keylen);
+		jid_get_lock_state_inr (key, keylen, lg_lock_state_Exclusive,
+					lg_lock_flag_IgnoreExp, lvb, 64);
+		jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+
+		if (lvb[0] == 1) {
+			log_msg (lgm_JIDMap,
+				 "fsid=%s: stale JID %d found\n",
+				 fs->fs_name, i);
+			ujid = i;
+			fs->cb (fs->fsdata, LM_CB_NEED_RECOVERY, &ujid);
+		}
+	}
+	up (&fs->headerlock);
+}
+
+
+/**
+ * jid_fs_init - 
+ * @fs: 
+ * 
+ */
+void
+jid_fs_init (gulm_fs_t * fs)
+{
+	uint8_t key[GIO_KEY_SIZE];
+	uint16_t keylen = GIO_KEY_SIZE;
+
+	fs->JIDcount = 0;
+
+	init_MUTEX (&fs->headerlock);
+
+	jid_get_header_name (fs->fs_name, key, &keylen);
+	jid_hold_lvb (key, keylen);
+	jid_rehold_lvbs (fs);
+}
+
+/**
+ * jid_fs_release - 
+ * @fs: 
+ * 
+ */
+void
+jid_fs_release (gulm_fs_t * fs)
+{
+	uint32_t i;
+	uint8_t key[GIO_KEY_SIZE];
+	uint16_t keylen = GIO_KEY_SIZE;
+	for (i = 0; i < fs->JIDcount; i++) {
+		keylen = sizeof (key);
+		jid_get_lock_name (fs->fs_name, i, key, &keylen);
+		jid_unhold_lvb (key, keylen);
+	}
+	keylen = GIO_KEY_SIZE;
+	jid_get_header_name (fs->fs_name, key, &keylen);
+	jid_unhold_lvb (key, keylen);
+	jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+}
+
+/**
+ * jid_unlock_callback - 
+ * @d: 
+ * 
+ * *MUST* be called from a Handler thread.
+ * 
+ * Returns: int
+ */
+void
+jid_unlock_callback (void *d)
+{
+	uint8_t key[GIO_KEY_SIZE];
+	uint16_t keylen = GIO_KEY_SIZE;
+
+	gulm_fs_t *fs = (gulm_fs_t *) d;
+	jid_get_header_name (fs->fs_name, key, &keylen);
+
+	down (&fs->headerlock);
+	jid_get_lock_state (key, keylen, lg_lock_state_Unlock);
+
+	jid_rehold_lvbs (fs);
+	up (&fs->headerlock);
+}
+
+/**
+ * jid_header_lock_drop - 
+ * @key: 
+ * @keylen: 
+ * 
+ * Returns: void
+ */
+void
+jid_header_lock_drop (uint8_t * key, uint16_t keylen)
+{
+	gulm_fs_t *fs;
+	uint8_t *fsname;
+	uint8_t len;
+	uint8_t ktype, jtype;
+	ktype = key[0];
+	len = key[1];
+	fsname = &key[2];
+	jtype = key[4 + len];
+
+	/* make sure this is the header lock.... */
+	if (ktype == 'J' && jtype == 'H' &&
+			(fs = get_fs_by_name (fsname)) != NULL) {
+		qu_function_call (&fs->cq, jid_unlock_callback, fs);
+	}
+}
+
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_lock_queue.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_lock_queue.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_lock_queue.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_lock_queue.c	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,841 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+#include <linux/crc32.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include "handler.h"
+
+#include "gulm_lock_queue.h"
+
+/* The Queues. */
+struct list_head glq_Free;
+spinlock_t glq_FreeLock;
+unsigned int glq_FreeCount;
+struct list_head glq_OutQueue;
+spinlock_t glq_OutLock;
+unsigned int glq_OutCount;
+#define ReplyMapBits (13)
+#define ReplyMapSize (1 << ReplyMapBits)
+#define ReplyMapMask (ReplyMapSize - 1)
+gulm_hb_t *glq_ReplyMap;
+
+/* The Threads. */
+struct task_struct *glq_recver_task = NULL;
+struct task_struct *glq_sender_task = NULL;
+struct completion glq_startedup;
+int glq_running;
+wait_queue_head_t glq_send_wchan;
+
+/* */
+extern gulm_cm_t gulm_cm;
+
+/* The code. */
+/**
+ * glq_init - 
+ * 
+ * Returns: int
+ */
+int glq_init(void)
+{
+	int i;
+
+	glq_running = FALSE;
+	glq_recver_task = NULL;
+	glq_sender_task = NULL;
+	init_waitqueue_head (&glq_send_wchan);
+	init_completion (&glq_startedup);
+
+	INIT_LIST_HEAD (&glq_Free);
+	spin_lock_init (&glq_FreeLock);
+	glq_FreeCount = 0;
+	INIT_LIST_HEAD (&glq_OutQueue);
+	spin_lock_init (&glq_OutLock);
+	glq_OutCount = 0;
+
+	glq_ReplyMap = vmalloc(sizeof(gulm_hb_t) * ReplyMapSize);
+	if( glq_ReplyMap == NULL ) {
+		return -ENOMEM;
+	}
+	for(i=0; i < ReplyMapSize; i++) {
+		INIT_LIST_HEAD (&glq_ReplyMap[i].bucket);
+		spin_lock_init (&glq_ReplyMap[i].lock);
+	}
+	/* ?Add some empty reqs to the Free list right now? */
+	return 0;
+}
+
+/**
+ * glq_release - 
+ *
+ * doesn't grab spins, because by the time this is called, there should be
+ * no other threads anywhere that could possibly be working on these lists.
+ * 
+ * Returns: void
+ */
+void glq_release(void)
+{
+	struct list_head *tmp, *lltmp;
+	glckr_t *item;
+	int i;
+
+	list_for_each_safe (tmp, lltmp, &glq_OutQueue) {
+		item = list_entry (tmp, glckr_t, list);
+		list_del (tmp);
+		if (item->key != NULL) kfree (item->key);
+		if (item->lvb != NULL) kfree (item->lvb);
+		kfree (item);
+	}
+	glq_FreeCount = 0;
+	list_for_each_safe (tmp, lltmp, &glq_Free) {
+		item = list_entry (tmp, glckr_t, list);
+		list_del (tmp);
+		if (item->key != NULL) kfree (item->key);
+		if (item->lvb != NULL) kfree (item->lvb);
+		kfree (item);
+	}
+	glq_OutCount = 0;
+	for(i=0; i < ReplyMapSize; i++) {
+		list_for_each_safe (tmp, lltmp, &glq_ReplyMap[i].bucket) {
+			item = list_entry (tmp, glckr_t, list);
+			list_del (tmp);
+			if (item->key != NULL) kfree (item->key);
+			if (item->lvb != NULL) kfree (item->lvb);
+			kfree (item);
+		}
+	}
+
+	vfree(glq_ReplyMap);
+}
+
+/**
+ * glq_get_new_req - 
+ *
+ * WARNING! For state and action requests, glq will not free the key or
+ * lvb pointers.  For drop and cancel glq WILL free the pointer when it is
+ * finished.
+ * 
+ * Returns: glckr_t
+ */
+glckr_t *glq_get_new_req(void)
+{
+	struct list_head *tmp;
+	glckr_t *item = NULL;
+
+	/* try to reclaim a recycled req first. */
+	spin_lock (&glq_FreeLock);
+	if (!list_empty (&glq_Free)) {
+		tmp = glq_Free.next;
+		list_del (tmp);
+		item = list_entry (tmp, glckr_t, list);
+		glq_FreeCount --;
+	}
+	spin_unlock (&glq_FreeLock);
+
+	/* nothing on Free list, make new. */
+	if (item == NULL) {
+		item = kmalloc(sizeof(glckr_t), GFP_KERNEL);
+		if (item == NULL) 
+			return NULL;
+		memset(item, 0, sizeof(glckr_t));
+	}
+
+	/* initialize.
+	 * reset list so its good.
+	 */
+	INIT_LIST_HEAD (&item->list);
+
+	return item;
+}
+
+/**
+ * glq_recycle_req - 
+ * @lckr_t: 
+ * 
+ * assumes that item is not on any lists.
+ * 
+ * Returns: void
+ */
+void glq_recycle_req(glckr_t *item)
+{
+	/* clean it up */
+	INIT_LIST_HEAD (&item->list);
+
+	if (item->type == glq_req_type_drop ||
+	    item->type == glq_req_type_cancel ||
+	    item->type == glq_req_type_expire) {
+		if (item->key != NULL) {
+			kfree(item->key);
+			item->key = NULL;
+		}
+		if (item->lvb != NULL) {
+			kfree(item->lvb);
+			item->lvb = NULL;
+		}
+	} else {
+		item->key = NULL;
+		item->lvb = NULL;
+	}
+	item->misc = NULL;
+	item->finish = NULL;
+
+	/* everything else is ignoreable. */
+
+	/* onto the Free list. unless too many. */
+	spin_lock (&glq_FreeLock);
+	if (glq_FreeCount > 20) { /* XXX icky hidden constant */
+		kfree (item);
+	}else{
+		list_add (&item->list, &glq_Free);
+		glq_FreeCount ++;
+	}
+	spin_unlock (&glq_FreeLock);
+}
+
+/**
+ * glq_duplicate - 
+ * @old: 
+ * 
+ * Creates a new glckr that is a copy of the old.  *EVERYTHING* is copied.
+ * 
+ * Returns: glckr_t
+ */
+glckr_t *glq_duplicate(glckr_t *old)
+{
+	glckr_t *new;
+	new = glq_get_new_req();
+	if(new == NULL) return NULL;
+
+	/* these fields just copy over */
+	new->subid = old->subid;
+	new->start = old->start;
+	new->stop = old->stop;
+	new->type = old->type;
+	new->state = old->state;
+	new->flags = old->flags;
+	new->error = old->error;
+	new->misc = old->misc;
+	new->finish = old->finish;
+
+	/* key and lvb field are handled differently based on the type
+	 * field.
+	 */
+	new->keylen = old->keylen;
+	new->lvblen = old->lvblen;
+	if(new->type == glq_req_type_drop ||
+	    new->type == glq_req_type_cancel ||
+	    new->type == glq_req_type_expire) {
+		/* Need to malloc new and memcpy */
+		if(old->key == NULL) {
+			new->key = NULL;
+		}else{
+			new->key = kmalloc(new->keylen, GFP_KERNEL);
+			if(new->key == NULL) {
+				glq_recycle_req(new);
+				return NULL;
+			}
+			memcpy(new->key, old->key, new->keylen);
+		}
+		if(old->lvb == NULL) {
+			new->lvb = NULL;
+		}else{
+			new->lvb = kmalloc(new->lvblen, GFP_KERNEL);
+			if(new->lvb == NULL) {
+				glq_recycle_req(new);
+				return NULL;
+			}
+			memcpy(new->lvb, old->lvb, new->lvblen);
+		}
+	}else{
+		/* just copy pointer */
+		new->key = old->key;
+		new->lvb = old->lvb;
+	}
+
+	return new;
+}
+
+/**
+ * glq_calc_hash_key_long - 
+ * @key: 
+ * @keylen: 
+ * @subid: 
+ * @start: 
+ * @stop: 
+ * 
+ * 
+ * Returns: int
+ */
+int glq_calc_hash_key_long(uint8_t *key, uint16_t keylen,
+		uint64_t subid, uint64_t start, uint64_t stop)
+{
+	int ret = GULM_CRC_INIT;
+	ret = crc32 (ret, &keylen, sizeof(uint16_t));
+	ret = crc32 (ret, key, keylen);
+	ret = crc32 (ret, &subid, sizeof(uint64_t));
+	ret = crc32 (ret, &start, sizeof(uint64_t));
+	ret = crc32 (ret, &stop, sizeof(uint64_t));
+	ret &= ReplyMapMask;
+	return ret;
+}
+
+/**
+ * glq_calc_hash_key - 
+ * @item: 
+ * 
+ * 
+ * Returns: int
+ */
+int glq_calc_hash_key(glckr_t *item)
+{
+	return glq_calc_hash_key_long (item->key, item->keylen, item->subid,
+			item->start, item->stop);
+}
+
+/**
+ * glq_queue - 
+ * @item: 
+ * 
+ * 
+ * Returns: void
+ */
+void glq_queue(glckr_t *item)
+{
+	spin_lock (&glq_OutLock);
+	list_add (&item->list, &glq_OutQueue);
+	glq_OutCount++;
+	spin_unlock (&glq_OutLock);
+	wake_up (&glq_send_wchan);
+}
+
+/**
+ * glq_cancel - 
+ * @item: 
+ * 
+ * You MUST call glq_get_new_req() and fill that with the info of the
+ * request you want to cancel.
+ * 
+ * Returns: void
+ */
+void glq_cancel(glckr_t *cancel)
+{
+	int found = FALSE;
+	struct list_head *tmp, *lltmp;
+	glckr_t *item = NULL;
+
+	spin_lock (&glq_OutLock);
+	list_for_each_safe (tmp, lltmp, &glq_OutQueue) {
+		item = list_entry (tmp, glckr_t, list);
+		if (item->subid == cancel->subid &&
+		    item->start == cancel->start &&
+		    item->stop  == cancel->stop &&
+		    item->keylen == cancel->keylen &&
+		    memcmp(item->key, cancel->key, cancel->keylen) ) {
+			/* found it. */
+			list_del (tmp);
+			found = TRUE;
+			break;
+		}
+	}
+	spin_unlock(&glq_OutLock);
+
+	if (!found) {
+		/* send cancel request to server. */
+		cancel->type = glq_req_type_cancel;
+		glq_queue (cancel);
+	}else{
+		/* finish it here */
+		item->error = lg_err_Canceled;
+		if (item->finish != NULL )
+			item->finish (item);
+		glq_recycle_req (item);
+		glq_recycle_req (cancel);
+	}
+}
+
+/**
+ * glq_send_queue_empty - 
+ * 
+ * Returns: int
+ */
+static int glq_send_queue_empty(void)
+{
+	int ret;
+	spin_lock (&glq_OutLock);
+	ret = list_empty (&glq_OutQueue);
+	spin_unlock (&glq_OutLock);
+	return ret;
+}
+
+/**
+ * glq_sender_thread - 
+ * @data: 
+ * 
+ * 
+ * Returns: int
+ */
+int glq_sender_thread(void *data)
+{
+	int err=0, bucket;
+	struct list_head *tmp;
+	glckr_t *item = NULL;
+	DECLARE_WAITQUEUE (__wait_chan, current);
+
+	daemonize ("gulm_glq_sender");
+	glq_sender_task = current;
+	complete (&glq_startedup);
+
+	while (glq_running) {
+		/* wait for item */
+		current->state = TASK_INTERRUPTIBLE; 
+		add_wait_queue (&glq_send_wchan, &__wait_chan);
+		if( glq_send_queue_empty () )
+			schedule ();
+		remove_wait_queue (&glq_send_wchan, &__wait_chan);
+		current->state = TASK_RUNNING;
+		if (!glq_running) break;
+
+		/* pull item off queue */
+		spin_lock (&glq_OutLock);
+		if (list_empty (&glq_OutQueue) ) {
+			spin_unlock (&glq_OutLock);
+			continue;
+		}
+		tmp = glq_OutQueue.prev;
+		list_del (tmp);
+		glq_OutCount--;
+		spin_unlock (&glq_OutLock);
+		item = list_entry (tmp, glckr_t, list);
+
+		/* send to local ltpx or die */
+		if (item->type == glq_req_type_state ) {
+			INIT_LIST_HEAD (&item->list);
+			bucket = glq_calc_hash_key(item);
+			spin_lock (&glq_ReplyMap[bucket].lock);
+			list_add (&item->list, &glq_ReplyMap[bucket].bucket);
+			spin_unlock (&glq_ReplyMap[bucket].lock);
+			err = lg_lock_state_req (gulm_cm.hookup, item->key,
+					item->keylen, item->subid, item->start,
+					item->stop, item->state, item->flags,
+					item->lvb, item->lvblen);
+		} else if (item->type == glq_req_type_action) {
+			INIT_LIST_HEAD (&item->list);
+			bucket = glq_calc_hash_key(item);
+			spin_lock (&glq_ReplyMap[bucket].lock);
+			list_add (&item->list, &glq_ReplyMap[bucket].bucket);
+			spin_unlock (&glq_ReplyMap[bucket].lock);
+			err = lg_lock_action_req (gulm_cm.hookup, item->key,
+					item->keylen, item->subid, item->state,
+					item->lvb, item->lvblen);
+		} else if (item->type == glq_req_type_query ) {
+			INIT_LIST_HEAD (&item->list);
+			bucket = glq_calc_hash_key(item);
+			spin_lock (&glq_ReplyMap[bucket].lock);
+			list_add (&item->list, &glq_ReplyMap[bucket].bucket);
+			spin_unlock (&glq_ReplyMap[bucket].lock);
+			err = lg_lock_query_req (gulm_cm.hookup, item->key,
+					item->keylen, item->subid, item->start,
+					item->stop, item->state);
+		} else if (item->type == glq_req_type_drop) {
+			err = lg_lock_drop_exp (gulm_cm.hookup, item->lvb,
+					item->key, item->keylen);
+			/* drop exp has no reply. */
+			glq_recycle_req (item);
+		} else if (item->type == glq_req_type_expire) {
+			err = lg_lock_expire (gulm_cm.hookup, item->lvb,
+					item->key, item->keylen);
+			/* expire has no reply. */
+			glq_recycle_req (item);
+		} else if (item->type == glq_req_type_cancel) {
+			err = lg_lock_cancel_req (gulm_cm.hookup, item->key,
+					item->keylen, item->subid);
+			/* cancels have no reply. */
+			glq_recycle_req (item);
+		} else {
+			/* bad type. */
+			log_err ("Unknown send type %d, tossing request.\n",
+					item->type);
+			glq_recycle_req (item);
+		}
+		if (err != 0 ) {
+			log_err ("gulm_glq_sender error %d\n", err);
+			glq_running = FALSE;
+			glq_recycle_req (item);
+			break;
+		}
+	}
+	complete (&glq_startedup);
+	return 0;
+}
+
+/**
+ * glq_login_reply - 
+ * @misc: 
+ * @err: 
+ * @which: 
+ * 
+ * 
+ * Returns: int
+ */
+int glq_login_reply (void *misc, uint32_t error, uint8_t which)
+{
+	if (error != 0) {
+		glq_running = FALSE;
+		log_err ("glq: Got error %d from login request.\n", error);
+	}
+	return error;
+}
+
+/**
+ * glq_logout_reply - 
+ * @misc: 
+ * 
+ * 
+ * Returns: int
+ */
+int glq_logout_reply (void *misc)
+{
+	glq_running = FALSE; /* if it isn't already. */
+	return 0;
+}
+
+/**
+ * glq_lock_state - 
+ * @misc: 
+ * @key: 
+ * @keylen: 
+ * @state: 
+ * @flags: 
+ * @error: 
+ * @LVB: 
+ * @LVBlen: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+glq_lock_state (void *misc, uint8_t * key, uint16_t keylen,
+		    uint64_t subid, uint64_t start, uint64_t stop,
+		    uint8_t state, uint32_t flags, uint32_t error,
+		    uint8_t * LVB, uint16_t LVBlen)
+{
+	int bucket, found = FALSE;
+	struct list_head *tmp;
+	glckr_t *item=NULL;
+
+	/* lookup and remove from ReplyMap */
+	bucket = glq_calc_hash_key_long(key, keylen, subid, start, stop);
+	spin_lock (&glq_ReplyMap[bucket].lock);
+	list_for_each(tmp, &glq_ReplyMap[bucket].bucket) {
+		item = list_entry (tmp, glckr_t, list);
+		if (item->subid == subid &&
+		    item->start == start &&
+		    item->stop  == stop &&
+		    item->keylen == keylen &&
+		    memcmp(item->key, key, keylen) == 0 ) {
+			/* found it. */
+			list_del (tmp);
+			found = TRUE;
+			break;
+		}
+	}
+	spin_unlock(&glq_ReplyMap[bucket].lock);
+
+	if( !found ) {
+		/* not found complaint */
+		return 0;
+	}
+
+	/* restuff results */
+	item->state = state;
+	item->flags = flags;
+	item->error = error;
+	if (item->lvb != NULL && LVB != NULL) {
+		item->lvblen = MIN(item->lvblen, LVBlen);
+		memcpy(item->lvb, LVB, item->lvblen);
+	}
+
+	/* call finish */
+	if (item->finish != NULL) item->finish (item);
+
+	/* put on Free */
+	glq_recycle_req(item);
+	return 0;
+}
+
+/**
+ * glq_lock_action - 
+ * @misc: 
+ * @key: 
+ * @keylen: 
+ * @action: 
+ * @error: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+glq_lock_action (void *misc, uint8_t * key, uint16_t keylen,
+		     uint64_t subid, uint8_t action, uint32_t error)
+{
+	int bucket, found = FALSE;
+	struct list_head *tmp;
+	glckr_t *item = NULL;
+
+	/* lookup and remove from ReplyMap */
+	bucket = glq_calc_hash_key_long(key, keylen, subid, 0, ~((uint64_t)0));
+	spin_lock (&glq_ReplyMap[bucket].lock);
+	list_for_each(tmp, &glq_ReplyMap[bucket].bucket) {
+		item = list_entry (tmp, glckr_t, list);
+		if (item->subid == subid &&
+		    item->start == 0 &&
+		    item->stop  == ~((uint64_t)0) &&
+		    item->keylen == keylen &&
+		    memcmp(item->key, key, keylen) == 0 ) {
+			/* found it. */
+			list_del (tmp);
+			found = TRUE;
+			break;
+		}
+	}
+	spin_unlock(&glq_ReplyMap[bucket].lock);
+
+	if( !found ) {
+		/* not found complaint */
+		return 0;
+	}
+
+	/* restuff results */
+	item->error = error;
+
+	/* call finish */
+	if (item->finish != NULL) item->finish (item);
+
+	/* put on Free */
+	glq_recycle_req(item);
+	return 0;
+}
+
+/**
+ * glq_lock_query -
+ * this is an ugly interface.....
+ * there is somehting that needs to be done here to clean things up.  I'm
+ * not sure what that is right now, and I need to have somehting working.
+ * So we're going with this for now.
+ *
+ */
+int 
+glq_lock_query (void *misc, uint8_t * key, uint16_t keylen,
+		   uint64_t subid, uint64_t start, uint64_t stop,
+		   uint8_t state, uint32_t error, uint8_t * cnode,
+		   uint64_t csubid, uint64_t cstart, uint64_t cstop,
+		   uint8_t cstate)
+{
+	int bucket, found = FALSE;
+	struct list_head *tmp;
+	glckr_t *item = NULL;
+
+	/* lookup and remove from ReplyMap */
+	bucket = glq_calc_hash_key_long(key, keylen, subid, start, stop);
+	spin_lock (&glq_ReplyMap[bucket].lock);
+	list_for_each(tmp, &glq_ReplyMap[bucket].bucket) {
+		item = list_entry (tmp, glckr_t, list);
+		if (item->subid == subid &&
+		    item->start == start &&
+		    item->stop  == stop &&
+		    item->keylen == keylen &&
+		    memcmp(item->key, key, keylen) == 0 ) {
+			/* found it. */
+			list_del (tmp);
+			found = TRUE;
+			break;
+		}
+	}
+	spin_unlock(&glq_ReplyMap[bucket].lock);
+
+	if( !found ) {
+		/* not found complaint */
+		return 0;
+	}
+
+	/* restuff results */
+	item->error = error;
+	item->subid = csubid;
+	item->start = cstart;
+	item->stop = cstop;
+	item->state = cstate;
+
+	/* call finish */
+	if (item->finish != NULL) item->finish (item);
+
+	/* put on Free */
+	glq_recycle_req(item);
+	return 0;
+}
+
+/**
+ * glq_drop_lock_req - 
+ * @misc: 
+ * @key: 
+ * @keylen: 
+ * @state: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+glq_drop_lock_req (void *misc, uint8_t * key, uint16_t keylen,
+		       uint64_t subid, uint8_t state)
+{
+	do_drop_lock_req (key, keylen, state);
+	jid_header_lock_drop (key, keylen);
+	sig_watcher_lock_drop (key, keylen);
+	return 0;
+}
+
+/**
+ * glq_drop_all - 
+ * @misc: 
+ * 
+ * 
+ * Returns: int
+ */
+int glq_drop_all (void *misc)
+{
+	passup_droplocks ();
+	return 0;
+}
+
+/**
+ * glq_error - 
+ * @misc: 
+ * @error: 
+ * 
+ * 
+ * Returns: int
+ */
+int glq_error (void *misc, uint32_t error)
+{
+	log_err ("glq: weird last gasp error %d\n", error);
+	return error;
+}
+
+static lg_lockspace_callbacks_t glq_lock_ops = {
+      login_reply:glq_login_reply,
+      logout_reply:glq_logout_reply,
+      lock_state:glq_lock_state,
+      lock_action:glq_lock_action,
+      lock_query:glq_lock_query,
+      drop_lock_req:glq_drop_lock_req,
+      drop_all:glq_drop_all,
+      error:glq_error
+};
+/**
+ * glq_recving_thread - 
+ * @data: 
+ * 
+ * 
+ * Returns: int
+ */
+int glq_recving_thread(void *data)
+{
+	int err;
+	daemonize ("gulm_glq_recver");
+	glq_recver_task = current;
+	complete (&glq_startedup);
+
+	while (glq_running) {
+		err = lg_lock_handle_messages (gulm_cm.hookup, &glq_lock_ops, NULL);
+		if (err != 0) {
+			log_err ("gulm_glq_recver error %d\n", err);
+			glq_running = FALSE;
+			wake_up (&glq_send_wchan);
+			break;
+		}
+	}
+	complete (&glq_startedup);
+	return 0;
+}
+
+/**
+ * glq_shutdown - 
+ * 
+ * Returns: void
+ */
+void glq_shutdown(void)
+{
+	if (glq_running) glq_running = FALSE;
+	if (glq_sender_task != NULL) {
+		wake_up (&glq_send_wchan);
+		wait_for_completion (&glq_startedup);
+		glq_sender_task = NULL;
+	}
+	if (glq_recver_task != NULL) {
+		lg_lock_logout (gulm_cm.hookup);
+		wait_for_completion (&glq_startedup);
+		glq_recver_task = NULL;
+	}
+}
+
+/**
+ * glq_startup - 
+ * 
+ * Returns: int
+ */
+int glq_startup(void)
+{
+	int err;
+
+	if (glq_running) return 0;
+
+	err = lg_lock_login (gulm_cm.hookup, "GFS ");
+	if (err != 0) {
+		log_err ("Failed to send lock login. %d\n", err);
+		return -err;
+	}
+
+	glq_running = TRUE;
+	if( glq_recver_task == NULL ) {
+		err = kernel_thread (glq_recving_thread, NULL, 0);
+		if( err < 0 ) {
+			log_err ("Failed to start glq_recving_thread %d\n",
+					err);
+			glq_shutdown();
+			return err;
+		}
+		wait_for_completion (&glq_startedup);
+	}
+
+	if (glq_sender_task == NULL) {
+		err = kernel_thread (glq_sender_thread, NULL, 0);
+		if( err < 0 ) {
+			log_err ("Failed to start glq_sender_thread %d\n",
+					err);
+			glq_shutdown();
+			return err;
+		}
+		wait_for_completion (&glq_startedup);
+	}
+	return 0;
+}
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_lock_queue.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_lock_queue.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_lock_queue.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_lock_queue.h	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,81 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+
+/* 
+ * So what if we change this to a request chain like I've got in the
+ * servers.  There are three lists. Free, Send, Reply.  Where the Reply
+ * list is actually more of a hash.
+ *
+ * Activity goes:
+ * - Grab struct from Free, malloc if needed.
+ * - Stuff, stick on Send.
+ * - Send, sends, then sticks on Reply.
+ * - When handle_messages gets a reply, it looks up on Reply and handles
+ *   from there.
+ *
+ * If Reply is a hash, it must be keyed on all important parts!
+ * (keyname, subid, start, stop)
+ */
+#ifndef __gulm_lockqueue_h__
+#define __gulm_lockqueue_h__
+#define glq_req_type_state  (1)
+#define glq_req_type_action (2)
+#define glq_req_type_drop   (3)
+#define glq_req_type_cancel (4)
+#define glq_req_type_query  (5)
+#define glq_req_type_expire (6)
+typedef struct glck_req {
+	struct list_head list;
+
+	/* these five for the key for hash-map look ups.
+	* Any part of any of these can change and thus be a unique request.
+	* (this struct is only put into a hash map to match replies.)
+	*/
+	uint8_t *key;
+	uint16_t keylen;
+	uint64_t subid;
+	uint64_t start;
+	uint64_t stop;
+
+	/* other info about this request. */
+	uint8_t type;
+	uint8_t state;  /* also action */ /* changes on reply (anyflag) */
+	uint32_t flags; /* changes on reply */
+	uint8_t *lvb; /* changes on reply */
+	uint16_t lvblen;
+	uint32_t error;  /* changes on reply */
+
+	/* when we get a reply, do this
+	* this glck_req will not be on any list when finish is called.  Upon
+	* the return of finish, it will be placed onto the Free list.
+	*/
+	void *misc;
+	void (*finish)(struct glck_req *glck);
+
+} glckr_t;
+
+/* prototypes */
+int glq_init(void);
+int glq_startup(void);
+void glq_shutdown(void);
+void glq_release(void);
+glckr_t *glq_get_new_req(void);
+void glq_recycle_req(glckr_t *);
+glckr_t *glq_duplicate(glckr_t *old);
+void glq_queue(glckr_t *);
+void glq_cancel(glckr_t *);
+
+
+#endif /*__gulm_lockqueue_h__*/
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_log_msg_bits.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_log_msg_bits.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_log_msg_bits.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_log_msg_bits.h	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,40 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __gulm_log_msg_bits_h__
+#define __gulm_log_msg_bits_h__
+/* log_msg bit flags
+ * These got thier own file so I can easily include them in both user and
+ * kernel space.
+ * */
+#define lgm_Always      (0x00000000)	/*Print Message no matter what */
+#define lgm_Network     (0x00000001)
+#define lgm_Network2    (0x00000002)
+#define lgm_Stomith     (0x00000004)
+#define lgm_Heartbeat   (0x00000008)
+#define lgm_locking     (0x00000010)
+#define lgm_FuncDebug   (0x00000020)
+#define lgm_Forking     (0x00000040)
+#define lgm_JIDMap      (0x00000080)
+#define lgm_Subscribers (0x00000100)
+#define lgm_LockUpdates (0x00000200)
+#define lgm_LoginLoops  (0x00000400)
+#define lgm_Network3    (0x00000800)
+#define lgm_JIDUpdates  (0x00001000)
+#define lgm_ServerState (0x00002000)
+
+#define lgm_ReallyAll   (0xffffffff)
+
+#define lgm_BitFieldSize (32)
+
+#endif /*__gulm_log_msg_bits_h__*/
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_lt.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_lt.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_lt.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_lt.c	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,1002 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/file.h>
+#include <linux/crc32.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include "handler.h"
+#include "gulm_lock_queue.h"
+#include "utils_tostr.h"
+
+#define gulm_gfs_lmBits (13)
+#define gulm_gfs_lmSize (1 << gulm_gfs_lmBits)
+#define gulm_gfs_lmMask (gulm_gfs_lmSize - 1)
+
+extern gulm_cm_t gulm_cm;
+
+/****************************************************************************/
+/* A bunch of prints that hopefully contain more information that is also
+ * useful
+ *
+ * these are a mess.
+ */
+
+/**
+ * lck_key_to_hex - 
+ * @key: 
+ * @len: 
+ * @workspace: <> place to put string. !! better be 2x len !!
+ * 
+ * 
+ * Returns: char
+ */
+static char *
+lck_key_to_hex (uint8_t * key, uint16_t len, char *workspace)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		sprintf (&workspace[i * 2], "%02x", (key[i] & 0xff));
+	return workspace;
+}
+
+#if 0
+static void __inline__
+db_lck_entered (gulm_lock_t * lck)
+{
+	char bb[GIO_KEY_SIZE * 2 + 3];
+	lck_key_to_hex (lck->key, lck->keylen, bb);
+	printk ("Started  lock 0x%s cur:%#x req:%#x flags:%#x\n", bb,
+		lck->cur_state, lck->req_state, lck->flags);
+}
+static void __inline__
+db_lck_exited (gulm_lock_t * lck)
+{
+	char bb[GIO_KEY_SIZE * 2 + 3];
+	lck_key_to_hex (lck->key, lck->keylen, bb);
+	printk ("Finished lock 0x%s result:%#x\n", bb, lck->result);
+}
+#endif
+
+static void __inline__
+dump_gulm_lock_t (gulm_lock_t * lck)
+{
+	char bb[GIO_KEY_SIZE * 2 + 3];
+
+	lck_key_to_hex (lck->key, lck->keylen, bb);
+	log_msg (lgm_Always, " key = 0x%s\n", bb);
+	log_msg (lgm_Always, " cur_state = %d\n", lck->cur_state);
+}
+
+/* DEBUG_BY_LOCK is gone.  I may later add something back if needed.
+ *
+ * I love the idea of being able to log only certain locks, I just cannot
+ * think of an easy way to do it.  The best I can come up with is some
+ * pattern (or set of) that are used to decide which locks get logged.  But
+ * that could be expensive if the pattern is checked everytime, and won't
+ * behave as expected if only applied in get_lock.
+ * */
+
+/* The old log functions.
+ * These need their own sort of clean up someday as well.
+ * */
+#define log_msg_lk(key, keylen, fmt, args...) {\
+      uint8_t bb[GIO_KEY_SIZE*2 +3]; \
+      lck_key_to_hex( key, keylen, bb); \
+      printk(PROTO_NAME ": On lock 0x%s " fmt , bb , ## args ); \
+   }
+
+#define log_err_lk(key, keylen, fmt, args...) {\
+      uint8_t bb[GIO_KEY_SIZE*2 +3]; \
+      lck_key_to_hex( key, keylen, bb); \
+      printk(KERN_ERR PROTO_NAME ": ERROR On lock 0x%s " fmt , bb , ## args ); \
+   }
+
+#define log_msg_lck(lck, fmt, args...) {\
+      uint8_t bb[GIO_KEY_SIZE*2 +3]; \
+      lck_key_to_hex( (lck)->key, (lck)->keylen, bb); \
+      printk(PROTO_NAME ": On lock 0x%s " fmt , bb , ## args ); \
+   }
+
+#define log_err_lck(lck, fmt, args...) {\
+      uint8_t bb[GIO_KEY_SIZE*2 +3]; \
+      lck_key_to_hex( (lck)->key, (lck)->keylen, bb); \
+      printk(KERN_ERR PROTO_NAME ": ERROR On lock 0x%s " fmt , bb , ## args ); \
+   }
+
+#ifdef DEBUG_LVB
+static void __inline__
+print_lk_lvb (uint8_t * key, uint8_t * lvb, uint8_t st, uint8_t * dir)
+{
+	uint8_t bk[GIO_KEY_SIZE * 2 + 3];
+	uint8_t bl[GIO_LVB_SIZE * 2 + 3];
+	int i;
+	for (i = 0; i < GIO_KEY_SIZE; i++)
+		sprintf (&bk[(i * 2)], "%02x", (key[i]) & 0xff);
+	for (i = 0; i < GIO_LVB_SIZE; i++)
+		sprintf (&bl[(i * 2)], "%02x", (lvb[i]) & 0xff);
+	printk (PROTO_NAME ": On lock 0x%s with state %d\n\t%s LVB 0x%s\n",
+		bk, st, dir, bl);
+}
+
+#define lvb_log_msg_lk(k, fmt, args...) log_msg_lk( k , fmt , ## args )
+#define lvb_log_msg(fmt, args...) log_msg(lgm_Always , fmt , ## args )
+#else				/*DEBUG_LVB */
+#define print_lk_lvb(k,l,s,d)
+#define lvb_log_msg_lk(k, fmt, args...)
+#define lvb_log_msg(fmt, args...)
+#endif				/*DEBUG_LVB */
+
+/****************************************************************************/
+/**
+ * pack_lock_key - 
+ * @key: 
+ * @keylen: 
+ * 
+ * key is: <type><fsname len><fsname>\0<pk len><pk>\0
+ * <type> is: G J F N P
+ * <fsname len> is 0-256
+ * 
+ * Returns: int
+ */
+int pack_lock_key(uint8_t *key, uint16_t keylen, uint8_t type,
+		uint8_t *fsname, uint8_t *pk, uint8_t pklen)
+{
+	int fsnlen;
+	fsnlen = strlen(fsname);
+
+	if( keylen <= (fsnlen + pklen + 5) ) return -1;
+
+	memset (key, 0, keylen);
+
+	key[0] = type;
+
+	key[1] = fsnlen;
+	memcpy(&key[2], fsname, fsnlen);
+	key[2 + fsnlen] = 0;
+
+	key[3 + fsnlen] = pklen;
+
+	memcpy(&key[4 + fsnlen], pk, pklen);
+
+	key[4 + fsnlen + pklen] = 0;
+
+	return fsnlen + pklen + 5;
+}
+
+/**
+ * unpack_lock_key - 
+ * @key: <
+ * @keylen: <
+ * @type: >
+ * @fsname: >
+ * @fsnlen: >
+ * @pk: >
+ * @pklen: >
+ * 
+ * if you're gonna fiddle with bytes returned here, copy first!
+ *
+ * this is broken. do I even really need this?
+ * 
+ * Returns: int
+ */
+int unpack_lock_key(uint8_t *key, uint16_t keylen, uint8_t *type,
+		uint8_t **fsname, uint8_t *fsnlen,
+		uint8_t **pk, uint8_t *pklen)
+{
+	int fsnl, pkl;
+	if( type != NULL )
+		*type = key[0];
+
+	fsnl = key[1];
+	if( fsnlen != NULL && *fsname != NULL ) {
+		*fsnlen = key[1];
+		*fsname = &key[2];
+	}
+
+	/* 0 = key[2 + fsnl] */
+
+	pkl = key[3 + fsnl];
+	if( pklen != NULL && *pk != NULL ) {
+		*pklen = key[3 + fsnl];
+		*pk = &key[4 + fsnl];
+	}
+
+	/* 0 = key[4 + fsnl + *pklen] */
+
+	return fsnl + pkl + 5;
+}
+
+/**
+ * pack_drop_mask - 
+ * @mask: 
+ * @fsname: 
+ * 
+ * 
+ * Returns: int
+ */
+int pack_drop_mask(uint8_t *mask, uint16_t mlen, uint8_t *fsname)
+{
+	int fsnlen;
+	fsnlen = strlen(fsname);
+
+	memset (mask, 0, mlen);
+
+	mask[0] = 0xff;
+	mask[1] = fsnlen;
+	memcpy(&mask[2], fsname, fsnlen);
+	mask[2 + fsnlen] = 0;
+	/* rest should be 0xff */
+
+	return 3 + fsnlen;
+}
+
+/**
+ * gulm_lt_init - 
+ * 
+ * Returns: int
+ */
+int gulm_lt_init (void)
+{
+	int i;
+	gulm_cm.gfs_lockmap = vmalloc(sizeof(gulm_hb_t) * gulm_gfs_lmSize);
+	if (gulm_cm.gfs_lockmap == NULL)
+		return -ENOMEM;
+	for(i=0; i < gulm_gfs_lmSize; i++) {
+		spin_lock_init (&gulm_cm.gfs_lockmap[i].lock);
+		INIT_LIST_HEAD (&gulm_cm.gfs_lockmap[i].bucket);
+	}
+	return 0;
+}
+
+/**
+ * gulm_lt_release - 
+ */
+void gulm_lt_release(void)
+{
+	struct list_head *tmp, *lltmp;
+	gulm_lock_t *lck;
+	int i;
+
+	for(i=0; i < gulm_gfs_lmSize; i++) {
+		list_for_each_safe (tmp, lltmp, &gulm_cm.gfs_lockmap[i].bucket) {
+			lck = list_entry (tmp, gulm_lock_t, gl_list);
+			list_del (tmp);
+
+			if (lck->lvb != NULL) kfree (lck->lvb);
+
+			kfree(lck);
+		}
+	}
+
+	vfree (gulm_cm.gfs_lockmap);
+}
+
+/**
+ * find_and_mark_lock - 
+ * @key: 
+ * @keylen: 
+ * @lockp: 
+ * 
+ * looks for a lock struct of key.  If found, marks it.
+ * 
+ * Returns: TRUE or FALSE
+ */
+int
+find_and_mark_lock (uint8_t * key, uint8_t keylen, gulm_lock_t ** lockp)
+{
+	int found = FALSE;
+	uint32_t bkt;
+	gulm_lock_t *lck = NULL;
+	struct list_head *tmp;
+
+	/* now find the lock */
+	bkt = crc32 (GULM_CRC_INIT, key, keylen);
+	bkt &= gulm_gfs_lmMask;
+
+	spin_lock (&gulm_cm.gfs_lockmap[bkt].lock);
+	list_for_each (tmp, &gulm_cm.gfs_lockmap[bkt].bucket) {
+		lck = list_entry (tmp, gulm_lock_t, gl_list);
+		if (memcmp (lck->key, key, keylen) == 0) {
+			found = TRUE;
+			atomic_inc (&lck->count);
+			break;
+		}
+	}
+	spin_unlock (&gulm_cm.gfs_lockmap[bkt].lock);
+
+	if (found)
+		*lockp = lck;
+
+	return found;
+}
+
+/**
+ * mark_lock - 
+ * @lck: 
+ * 
+ * like above, but since we have the lock, don't search for it.
+ * 
+ * Returns: int
+ */
+void __inline__
+mark_lock (gulm_lock_t * lck)
+{
+	atomic_inc (&lck->count);
+}
+
+/**
+ * unmark_and_release_lock - 
+ * @lck: 
+ * 
+ * decrement the counter on a lock, freeing it if it reaches 0.
+ * (also removes it from the hash table)
+ * 
+ * TRUE if lock was freed.
+ *
+ * Returns: TRUE or FALSE
+ */
+int
+unmark_and_release_lock (gulm_lock_t * lck)
+{
+	uint32_t bkt;
+	int deld = FALSE;
+
+	bkt = crc32 (GULM_CRC_INIT, lck->key, lck->keylen);
+	bkt &= gulm_gfs_lmMask;
+
+	spin_lock (&gulm_cm.gfs_lockmap[bkt].lock);
+	if (atomic_dec_and_test (&lck->count)) {
+		list_del (&lck->gl_list);
+		deld = TRUE;
+	}
+	spin_unlock (&gulm_cm.gfs_lockmap[bkt].lock);
+	if (deld) {
+		if (lck->lvb != NULL) {
+			kfree (lck->lvb);
+		}
+		kfree (lck->key);
+		kfree (lck);
+	}
+
+	return deld;
+}
+
+/****************************************************************************/
+
+/**
+ * gulm_key_to_lm_lockname - 
+ * @key: 
+ * @lockname: 
+ * 
+ */
+void
+gulm_key_to_lm_lockname (uint8_t * key, struct lm_lockname *lockname)
+{
+	int pos;
+
+	pos = key[1] + 4;
+	/* pos now points to the first byte of the GFS lockname that was
+	 * embedded in the gulm lock key, skipping over the fs name.
+	 */
+
+	(*lockname).ln_type = key[pos];
+	(*lockname).ln_number  = (u64) (key[pos+1]) << 56;
+	(*lockname).ln_number |= (u64) (key[pos+2]) << 48;
+	(*lockname).ln_number |= (u64) (key[pos+3]) << 40;
+	(*lockname).ln_number |= (u64) (key[pos+4]) << 32;
+	(*lockname).ln_number |= (u64) (key[pos+5]) << 24;
+	(*lockname).ln_number |= (u64) (key[pos+6]) << 16;
+	(*lockname).ln_number |= (u64) (key[pos+7]) << 8;
+	(*lockname).ln_number |= (u64) (key[pos+8]) << 0;
+}
+
+/**
+ * do_drop_lock_req - 
+ * @key: 
+ * @keylen: 
+ * @state: 
+ * 
+ * 
+ * Returns: void
+ */
+void
+do_drop_lock_req (uint8_t *key, uint16_t keylen, uint8_t state)
+{
+	gulm_lock_t *lck;
+	unsigned int type;
+	struct lm_lockname lockname;
+
+	if (!find_and_mark_lock (key, keylen, &lck)) {
+		return;
+	}
+
+	switch (state) {
+	case lg_lock_state_Unlock:
+		type = LM_CB_DROPLOCKS;
+		break;
+	case lg_lock_state_Exclusive:
+		type = LM_CB_NEED_E;
+		break;
+	case lg_lock_state_Shared:
+		type = LM_CB_NEED_S;
+		break;
+	case lg_lock_state_Deferred:
+		type = LM_CB_NEED_D;
+		break;
+	default:
+		type = LM_CB_DROPLOCKS;
+		break;
+	}
+	gulm_key_to_lm_lockname (key, &lockname);
+
+	qu_drop_req (&lck->fs->cq, lck->fs->cb, lck->fs->fsdata, type,
+		     lockname.ln_type, lockname.ln_number);
+
+	unmark_and_release_lock (lck);
+}
+
+/****************************************************************************/
+
+/**
+ * calc_lock_result - 
+ * @lck: 
+ * @state: 
+ * @error: 
+ * @flags: 
+ * 
+ * This calculates the correct result to return for gfs lock requests.
+ * 
+ * Returns: int
+ */
+int
+calc_lock_result (gulm_lock_t * lck,
+		  uint8_t state, uint32_t error, uint32_t flags)
+{
+	gulm_fs_t *fs = lck->fs;
+	int result = -69;
+
+	/* adjust result based on success status. */
+	switch (error) {
+	case lg_err_Ok:
+		/* set result to current lock state. */
+		switch (state) {
+		case lg_lock_state_Shared:
+			result = LM_ST_SHARED;
+			break;
+		case lg_lock_state_Deferred:
+			result = LM_ST_DEFERRED;
+			break;
+		case lg_lock_state_Exclusive:
+			result = LM_ST_EXCLUSIVE;
+			break;
+		case lg_lock_state_Unlock:
+			result = LM_ST_UNLOCKED;
+			break;
+		default:
+			GULM_ASSERT (0,
+				     dump_gulm_lock_t (lck);
+				     log_err_lck
+				     (lck, "fsid=%s: Anit no lock state %d.\n",
+						  fs->fs_name, state);
+			    );
+			break;
+		}
+
+		/* if no internal unlocks, it is cachable. */
+		if (result != LM_ST_UNLOCKED && (flags & lg_lock_flag_Cachable))
+			result |= LM_OUT_CACHEABLE;
+
+		break;
+	case lg_err_Canceled:
+		result = LM_OUT_CANCELED | lck->cur_state;
+		break;
+	case lg_err_TryFailed:
+		result = lck->cur_state;	/* if we didn't get it. */
+		break;
+	default:
+		log_err_lck(lck, "state:%#x error:%d flags:%#x",
+				state, error, flags);
+		result = -error;
+		break;
+	}
+
+	return result;
+}
+
+/****************************************************************************/
+
+/**
+ * gulm_get_lock - 
+ * @lockspace: 
+ * @name: 
+ * @lockp:
+ * 
+ * Returns: 0 on success, -EXXX on failure
+ */
+int
+gulm_get_lock (lm_lockspace_t * lockspace, struct lm_lockname *name,
+	       lm_lock_t ** lockp)
+{
+	int err=0, len, bkt;
+	gulm_fs_t *fs = (gulm_fs_t *) lockspace;
+	uint8_t key[GIO_KEY_SIZE];
+	uint8_t temp[9];
+	gulm_lock_t *lck=NULL;
+
+	temp[0] = name->ln_type & 0xff;
+	temp[1] = (name->ln_number >> 56) & 0xff;
+	temp[2] = (name->ln_number >> 48) & 0xff;
+	temp[3] = (name->ln_number >> 40) & 0xff;
+	temp[4] = (name->ln_number >> 32) & 0xff;
+	temp[5] = (name->ln_number >> 24) & 0xff;
+	temp[6] = (name->ln_number >> 16) & 0xff;
+	temp[7] = (name->ln_number >> 8) & 0xff;
+	temp[8] = (name->ln_number >> 0) & 0xff;
+
+	len = pack_lock_key(key, GIO_KEY_SIZE, 'G', fs->fs_name, temp, 9);
+	if( len <=0 ) {err = len; goto exit;}
+
+	if (!find_and_mark_lock (key, len, &lck)) {
+		/* not found, must create. */
+		lck = kmalloc(sizeof(gulm_lock_t), GFP_KERNEL);
+		if (lck == NULL) {
+			err = -ENOMEM;
+			goto exit;
+		}
+		INIT_LIST_HEAD (&lck->gl_list);
+		atomic_set (&lck->count, 1);
+		lck->key = kmalloc (len, GFP_KERNEL);
+		if (lck->key == NULL) {
+			kfree(lck);
+			err = -ENOMEM;
+			goto exit;
+		}
+		memcpy (lck->key, key, len);
+		lck->keylen = len;
+		lck->fs = fs;
+		lck->lvb = NULL;
+		lck->cur_state = LM_ST_UNLOCKED;
+
+		bkt = crc32 (GULM_CRC_INIT, key, len);
+		bkt &= gulm_gfs_lmMask;
+
+		spin_lock (&gulm_cm.gfs_lockmap[bkt].lock);
+		list_add (&lck->gl_list, &gulm_cm.gfs_lockmap[bkt].bucket);
+		spin_unlock (&gulm_cm.gfs_lockmap[bkt].lock);
+
+	}
+	*lockp = lck;
+
+exit:
+	return err;
+}
+
+/**
+ * gulm_put_lock - 
+ * @lock: 
+ * 
+ * 
+ * Returns: void
+ */
+void
+gulm_put_lock (lm_lock_t * lock)
+{
+	unmark_and_release_lock ((gulm_lock_t *) lock);
+}
+
+/**
+ * gulm_lock_finish - 
+ * @glck: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_lock_finish (struct glck_req *item)
+{
+	int result;
+	gulm_lock_t *lck = (gulm_lock_t *)item->misc;
+	gulm_fs_t *fs = lck->fs;
+	struct lm_lockname lockname;
+
+	result = calc_lock_result (lck, item->state, item->error, item->flags);
+	/* requeue lock requests on error.  We should be able to retry from
+	 * most any error.  Will need to watch the logs though....
+	 * Since the current item *will* be freed when this function
+	 * returns, we will have to make a new one to do this.  No big deal,
+	 * but probably worth a new function.
+	 */
+	if( result < 0 ) {
+		struct glck_req *new=NULL;
+		RETRY_MALLOC(new = glq_duplicate(item), new);
+		glq_queue(new);
+		return;
+	}
+
+	gulm_key_to_lm_lockname (lck->key, &lockname);
+
+	qu_async_rpl (&fs->cq, fs->cb, fs->fsdata, &lockname, result);
+
+	/* marked in gulm_lock() */
+	unmark_and_release_lock (lck);
+}
+
+/**
+ * gulm_lock - 
+ * @lock: 
+ * @cur_state: 
+ * @req_state: 
+ * @flags: 
+ * 
+ * 
+ * Returns: int
+ */
+unsigned int
+gulm_lock (lm_lock_t * lock, unsigned int cur_state,
+	   unsigned int req_state, unsigned int flags)
+{
+	glckr_t *item;
+	gulm_lock_t *lck = (gulm_lock_t *) lock;
+	gulm_fs_t *fs = lck->fs;
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		return -ENOMEM;
+	}
+
+	mark_lock (lck); /* matching unmark is in gulm_lock_finish */
+
+	item->key = lck->key;
+	item->keylen = lck->keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_state;
+
+	switch (req_state) {
+	case LM_ST_EXCLUSIVE:
+		item->state = lg_lock_state_Exclusive;
+		break;
+	case LM_ST_DEFERRED:
+		item->state = lg_lock_state_Deferred;
+		break;
+	case LM_ST_SHARED:
+		item->state = lg_lock_state_Shared;
+		break;
+	case LM_ST_UNLOCKED:
+		item->state = lg_lock_state_Unlock;
+		break;
+	default:
+		GULM_ASSERT (0, log_err ("fsid=%s: Anit no lock state %d.\n",
+					 fs->fs_name, req_state););
+		break;
+	}
+	item->flags = 0;
+	if (flags & LM_FLAG_TRY) {
+		item->flags |= lg_lock_flag_Try;
+	}
+	if (flags & LM_FLAG_TRY_1CB) {
+		item->flags |= lg_lock_flag_Try | lg_lock_flag_DoCB;
+	}
+	if (flags & LM_FLAG_NOEXP) {
+		item->flags |= lg_lock_flag_IgnoreExp;
+	}
+	if (flags & LM_FLAG_ANY) {
+		item->flags |= lg_lock_flag_Any;
+	}
+	if (flags & LM_FLAG_PRIORITY) {
+		item->flags |= lg_lock_flag_Piority;
+	}
+	if (lck->lvb != NULL) {
+		item->lvb = lck->lvb;
+		item->lvblen = fs->lvb_size;
+	}else{
+		item->lvb = NULL;
+		item->lvblen = 0;
+	}
+	item->error = 0;
+
+	item->misc = lck;
+	item->finish = gulm_lock_finish;
+
+	lck->cur_state = cur_state;
+
+	glq_queue (item);
+
+	return LM_OUT_ASYNC;
+}
+
+/**
+ * gulm_unlock - 
+ * @lock: 
+ * @cur_state: 
+ * 
+ * 
+ * Returns: int
+ */
+unsigned int
+gulm_unlock (lm_lock_t * lock, unsigned int cur_state)
+{
+	int e;
+	e = gulm_lock (lock, cur_state, LM_ST_UNLOCKED, 0);
+	return e;
+}
+
+/**
+ * gulm_cancel - 
+ * @lock: 
+ * 
+ */
+void
+gulm_cancel (lm_lock_t * lock)
+{
+	glckr_t *item;
+	gulm_lock_t *lck = (gulm_lock_t *) lock;
+
+	mark_lock (lck);
+
+	item = glq_get_new_req();
+	if( item == NULL ) goto exit;
+
+	/* have to make a copy for cancel req. */
+	item->key = kmalloc(lck->keylen, GFP_KERNEL);
+	if (item->key == NULL) {
+		glq_recycle_req(item);
+		goto exit;
+	}
+	memcpy(item->key, lck->key, lck->keylen);
+	item->keylen = lck->keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_cancel;
+	item->finish = NULL;
+
+	glq_cancel(item);
+
+exit:
+	unmark_and_release_lock (lck);
+}
+
+/****************************************************************************/
+struct gulm_lvb_temp_s {
+	int error;
+	struct completion sleep;
+};
+
+/**
+ * gulm_lvb_finish - 
+ * @glck: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_lvb_finish(struct glck_req *glck)
+{
+	struct gulm_lvb_temp_s *g = (struct gulm_lvb_temp_s *)glck->misc;
+	g->error = glck->error;
+	complete (&g->sleep);
+}
+
+/**
+ * gulm_hold_lvb - 
+ * @lock: 
+ * @lvbp:
+ * 
+ * 
+ * Returns: 0 on success, -EXXX on failure
+ */
+int
+gulm_hold_lvb (lm_lock_t * lock, char **lvbp)
+{
+	int err = -1;
+	struct gulm_lvb_temp_s ghlt;
+	glckr_t *item;
+	gulm_lock_t *lck = (gulm_lock_t *) lock;
+	gulm_fs_t *fs = lck->fs;
+
+	mark_lock (lck);
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	item->key = lck->key;
+	item->keylen = lck->keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_action;
+	item->state = lg_lock_act_HoldLVB;
+	item->flags = 0;
+	item->error = ghlt.error = 0;
+
+	init_completion (&ghlt.sleep);
+
+	item->misc = &ghlt;
+	item->finish = gulm_lvb_finish;
+
+	lck->lvb = kmalloc (fs->lvb_size, GFP_KERNEL);
+	if (lck->lvb == NULL) {
+		err = -ENOMEM;
+		goto fail;
+	}
+	memset (lck->lvb, 0, fs->lvb_size);
+
+	item->lvb = lck->lvb;
+	item->lvblen = fs->lvb_size;
+
+	glq_queue (item);
+	wait_for_completion (&ghlt.sleep);
+	/* after here, item is no longer valid
+	 * (memory was probably freed.)
+	 * is why we use ghlt.error and not item->error.
+	 */
+
+	if (ghlt.error != lg_err_Ok) {
+		log_err ("fsid=%s: Got error %d on hold lvb request.\n",
+			 fs->fs_name, ghlt.error);
+		kfree (lck->lvb);
+		lck->lvb = NULL;
+		goto fail;
+	}
+
+	*lvbp = lck->lvb;
+
+	unmark_and_release_lock (lck);
+
+	lvb_log_msg_lk (lck->key, "fsid=%s: Exiting gulm_hold_lvb\n",
+			fs->fs_name);
+	return 0;
+      fail:
+	unmark_and_release_lock (lck);
+	if (err != 0)
+		log_msg (lgm_Always,
+			 "fsid=%s: Exiting gulm_hold_lvb with errors (%d)\n",
+			 fs->fs_name, err);
+	return err;
+}
+
+/**
+ * gulm_unhold_lvb - 
+ * @lock: 
+ * @lvb: 
+ * 
+ * 
+ * Returns: void
+ */
+void
+gulm_unhold_lvb (lm_lock_t * lock, char *lvb)
+{
+	struct gulm_lvb_temp_s ghlt;
+	glckr_t *item;
+	gulm_lock_t *lck = (gulm_lock_t *) lock;
+	gulm_fs_t *fs = lck->fs;
+
+	mark_lock (lck);
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		log_err("unhold_lvb: failed to get needed memory. skipping.\n");
+		goto exit;
+	}
+
+	item->key = lck->key;
+	item->keylen = lck->keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_action;
+	item->state = lg_lock_act_UnHoldLVB;
+	item->flags = 0;
+	item->error = ghlt.error = 0;
+
+	init_completion (&ghlt.sleep);
+
+	item->misc = &ghlt;
+	item->finish = gulm_lvb_finish;
+
+	item->lvb = lck->lvb;
+	item->lvblen = fs->lvb_size;
+
+	glq_queue (item);
+	wait_for_completion (&ghlt.sleep);
+	/* after here, item is no longer valid
+	 * (memory was probably freed.)
+	 * is why we use ghlt.error and not item->error.
+	 */
+
+	if (ghlt.error != lg_err_Ok) {
+		log_err ("fsid=%s: Got error %d on unhold LVB request.\n",
+			 lck->fs->fs_name, ghlt.error);
+	}
+	/* free it always.  GFS thinks it is gone no matter what the server
+	 * thinks. (and as much as i hate to say it this way, far better to
+	 * leak in userspace than in kernel space.)
+	 */
+	if (lck->lvb != NULL)
+		kfree (lck->lvb);
+	lck->lvb = NULL;
+      exit:
+	unmark_and_release_lock (lck);
+	lvb_log_msg ("Exiting gulm_unhold_lvb\n");
+}
+
+/**
+ * gulm_sync_lvb - 
+ * @lock: 
+ * @lvb: 
+ * 
+ */
+void
+gulm_sync_lvb (lm_lock_t * lock, char *lvb)
+{
+	struct gulm_lvb_temp_s ghlt;
+	glckr_t *item;
+	gulm_lock_t *lck = (gulm_lock_t *) lock;
+	gulm_fs_t *fs = lck->fs;
+
+	mark_lock (lck);
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		log_err("sync_lvb: failed to get needed memory. skipping.\n");
+		goto exit;
+	}
+
+	item->key = lck->key;
+	item->keylen = lck->keylen;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_action;
+	item->state = lg_lock_act_SyncLVB;
+	item->flags = 0;
+	item->error = ghlt.error = 0;
+
+	init_completion (&ghlt.sleep);
+
+	item->misc = &ghlt;
+	item->finish = gulm_lvb_finish;
+
+	item->lvb = lck->lvb;
+	item->lvblen = fs->lvb_size;
+
+	glq_queue (item);
+	wait_for_completion (&ghlt.sleep);
+	/* after here, item is no longer valid
+	 * (memory was probably freed.)
+	 * is why we use ghlt.error and not item->error.
+	 */
+
+	if (ghlt.error != lg_err_Ok) {
+		log_err ("fsid=%s: Got error %d on sync LVB request.\n",
+			 lck->fs->fs_name, ghlt.error);
+	}
+
+      exit:
+	unmark_and_release_lock (lck);
+	lvb_log_msg ("Exiting gulm_sync_lvb\n");
+
+}
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_main.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_main.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_main.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_main.c	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,107 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/init.h>
+
+#include "gulm_lock_queue.h"
+
+MODULE_DESCRIPTION ("Grand Unified Locking Module " GULM_RELEASE_NAME);
+MODULE_AUTHOR ("Red Hat, Inc.");
+MODULE_LICENSE ("GPL");
+
+gulm_cm_t gulm_cm;
+
+struct lm_lockops gulm_ops = {
+      lm_proto_name:PROTO_NAME,
+      lm_mount:gulm_mount,
+      lm_others_may_mount:gulm_others_may_mount,
+      lm_unmount:gulm_unmount,
+      lm_withdraw:gulm_withdraw,
+      lm_get_lock:gulm_get_lock,
+      lm_put_lock:gulm_put_lock,
+      lm_lock:gulm_lock,
+      lm_unlock:gulm_unlock,
+      lm_cancel:gulm_cancel,
+      lm_hold_lvb:gulm_hold_lvb,
+      lm_unhold_lvb:gulm_unhold_lvb,
+      lm_sync_lvb:gulm_sync_lvb,
+      lm_plock_get:gulm_plock_get,
+      lm_plock:gulm_plock,
+      lm_punlock:gulm_punlock,
+      lm_recovery_done:gulm_recovery_done,
+      lm_owner:THIS_MODULE,
+};
+
+/**
+ * init_gulm - Initialize the gulm module
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+int __init
+init_gulm (void)
+{
+	int error;
+
+	memset (&gulm_cm, 0, sizeof (gulm_cm_t));
+	gulm_cm.hookup = NULL;
+
+	/* register with the lm layers. */
+	error = lm_register_proto (&gulm_ops);
+	if (error)
+		goto fail;
+
+	error = glq_init();
+	if (error != 0 )
+		goto lm_fail;
+
+	error = gulm_lt_init();
+	if (error != 0)
+		goto glq_fail;
+
+	init_gulm_fs ();
+	sig_watcher_init ();
+
+	printk ("Gulm %s (built %s %s) installed\n",
+		GULM_RELEASE_NAME, __DATE__, __TIME__);
+
+	return 0;
+
+glq_fail:
+	glq_release();
+
+   lm_fail:
+	lm_unregister_proto (&gulm_ops);
+
+      fail:
+	return error;
+}
+
+/**
+ * exit_gulm - cleanup the gulm module
+ *
+ */
+
+void __exit
+exit_gulm (void)
+{
+	gulm_lt_release();
+	glq_release();
+	lm_unregister_proto (&gulm_ops);
+}
+
+module_init (init_gulm);
+module_exit (exit_gulm);
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_plock.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_plock.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_plock.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_plock.c	2006-12-20 17:07:57.000000000 +0300
@@ -0,0 +1,321 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include "gulm_lock_queue.h"
+
+/*****************************************************************************/
+/**
+ * gulm_plock_packname - 
+ * @fsname: 
+ * @num: 
+ * @key: 
+ * @keylen: 
+ * 
+ * 
+ * Returns: int
+ */
+int gulm_plock_packname(uint8_t * fsname, uint64_t num, uint8_t *key, uint16_t keylen)
+{
+	uint8_t temp[8];
+	temp[0] = (num >> 56) & 0xff;
+	temp[1] = (num >> 48) & 0xff;
+	temp[2] = (num >> 40) & 0xff;
+	temp[3] = (num >> 32) & 0xff;
+	temp[4] = (num >> 24) & 0xff;
+	temp[5] = (num >> 16) & 0xff;
+	temp[6] = (num >> 8) & 0xff;
+	temp[7] = (num >> 0) & 0xff;
+	return pack_lock_key(key, keylen, 'P', fsname, temp, 8);
+}
+
+struct gulm_pqur_s {
+	uint64_t start;
+	uint64_t stop;
+	uint64_t subid;
+	int error;
+	uint8_t state;
+	struct completion sleep;
+};
+/**
+ * gulm_plock_query_finish - 
+ * @glck: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_plock_query_finish(struct glck_req *glck)
+{
+	struct gulm_pqur_s *g = (struct gulm_pqur_s *)glck->misc;
+	g->error = glck->error;
+	g->start = glck->start;
+	g->stop = glck->stop;
+	g->subid = glck->subid;
+	g->state = glck->state;
+	complete (&g->sleep);
+}
+/**
+ * gulm_plock_get - 
+ */
+int
+gulm_plock_get (lm_lockspace_t * lockspace, struct lm_lockname *name,
+		 struct file *file, struct file_lock *fl)
+{
+	int err = 0;
+	struct gulm_pqur_s pqur;
+	uint8_t key[GIO_KEY_SIZE];
+	gulm_fs_t *fs = (gulm_fs_t *) lockspace;
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	item->keylen = gulm_plock_packname(fs->fs_name, name->ln_number,
+			key, GIO_KEY_SIZE);
+	item->key = key;
+	item->subid = (unsigned long) fl->fl_owner;
+	item->start = fl->fl_start;
+	item->stop = fl->fl_end;
+	item->type = glq_req_type_query;
+	if (fl->fl_type == F_WRLCK) {
+		item->state = lg_lock_state_Exclusive;
+	} else {
+		item->state = lg_lock_state_Shared;
+	}
+	item->flags = lg_lock_flag_NoCallBacks;
+	item->error = pqur.error = 0;
+
+	init_completion (&pqur.sleep);
+
+	item->misc = &pqur;
+	item->finish = gulm_plock_query_finish;
+
+	glq_queue (item);
+	wait_for_completion (&pqur.sleep);
+
+	if (pqur.error == lg_err_TryFailed) {
+		err = -EAGAIN;
+		fl->fl_start = pqur.start;
+		fl->fl_end = pqur.stop;
+		fl->fl_pid = pqur.subid;
+		if( pqur.state == lg_lock_state_Exclusive )
+			fl->fl_type = F_WRLCK;
+		else
+			fl->fl_type = F_RDLCK;
+	} else if (pqur.error == 0) {
+		fl->fl_type = F_UNLCK;
+	} else {
+		err = -pqur.error;
+	}
+
+fail:
+	return err;
+}
+
+struct gulm_plock_req_wait_s {
+	int error;
+	int done;
+	wait_queue_head_t waiter;
+};
+
+/**
+ * gulm_plock_req_finish - 
+ * @glck: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_plock_req_finish(struct glck_req *glck)
+{
+	struct gulm_plock_req_wait_s *g = (struct gulm_plock_req_wait_s *)glck->misc;
+	g->error = glck->error;
+	g->done = TRUE;
+	wake_up (&g->waiter);
+}
+/**
+ * do_plock_cancel - 
+ * @item: 
+ * 
+ * 
+ * Returns: void
+ */
+void do_plock_cancel(glckr_t *item)
+{
+	glckr_t *cancel;
+	cancel = glq_get_new_req();
+	if( cancel == NULL ) {
+		log_err ("Out of memory, Cannot cancel plock request.\n");
+		return;
+	}
+
+	/* after cancel is processed, glq will call kfree on item->key. */
+	cancel->key = kmalloc(item->keylen, GFP_KERNEL);
+	if (cancel->key == NULL) {
+		glq_recycle_req(cancel);
+		log_err ("Out of memory, Cannot cancel plock request.\n");
+		return;
+	}
+	memcpy(cancel->key, item->key, item->keylen);
+	cancel->keylen = item->keylen;
+	cancel->subid = item->subid;
+	cancel->start = item->start;
+	cancel->stop = item->stop;
+	cancel->type = glq_req_type_cancel;
+	cancel->finish = NULL;
+
+	glq_cancel(cancel);
+}
+/**
+ * gulm_plock - 
+ *
+ */
+int
+gulm_plock (lm_lockspace_t *lockspace, struct lm_lockname *name,
+		struct file *file, int cmd, struct file_lock *fl)
+{
+	int err = 0;
+	struct gulm_plock_req_wait_s pwait;
+	uint8_t key[GIO_KEY_SIZE];
+	gulm_fs_t *fs = (gulm_fs_t *) lockspace;
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	item->keylen = gulm_plock_packname(fs->fs_name, name->ln_number,
+			key, GIO_KEY_SIZE);
+	item->key = key;
+	item->subid = (unsigned long) fl->fl_owner;
+	item->start = fl->fl_start;
+	item->stop = fl->fl_end;
+	item->type = glq_req_type_state;
+	if (fl->fl_type == F_WRLCK) {
+		item->state = lg_lock_state_Exclusive;
+	} else {
+		item->state = lg_lock_state_Shared;
+	}
+	item->flags = lg_lock_flag_NoCallBacks;
+	if (!IS_SETLKW(cmd))
+		item->flags |= lg_lock_flag_Try;
+	item->error = pwait.error = 0;
+
+	pwait.done = FALSE;
+	init_waitqueue_head(&pwait.waiter);
+
+	item->misc = &pwait;
+	item->finish = gulm_plock_req_finish;
+
+	glq_queue (item);
+	err = wait_event_interruptible(pwait.waiter, pwait.done);
+	if( err != 0 ) {
+		/* signals. */
+		/* send cancel req. */
+		do_plock_cancel(item);
+		/* wait for canceled (or success if we were too slow in
+		 * canceling) */
+		wait_event(pwait.waiter, pwait.done);
+	}
+
+	if (pwait.error == lg_err_TryFailed) {
+		err = -EAGAIN;
+	} else if (pwait.error == lg_err_Canceled) {
+		err = -EINTR;
+	} else {
+		err = -pwait.error;
+	}
+
+	if ( err == 0) err = posix_lock_file_wait(file, fl);
+
+fail:
+	return err;
+}
+
+struct gulm_pret_s {
+	int error;
+	struct completion sleep;
+};
+
+/**
+ * gulm_plock_finish - 
+ * @glck: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_plock_finish(struct glck_req *glck)
+{
+	struct gulm_pret_s *g = (struct gulm_pret_s *)glck->misc;
+	g->error = glck->error;
+	complete (&g->sleep);
+}
+
+/**
+ * gulm_unplock - 
+ */
+int
+gulm_punlock (lm_lockspace_t * lockspace, struct lm_lockname *name,
+	      struct file *file, struct file_lock *fl)
+{
+	int err = 0;
+	struct gulm_pret_s pret;
+	uint8_t key[GIO_KEY_SIZE];
+	gulm_fs_t *fs = (gulm_fs_t *) lockspace;
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if( item == NULL ) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	item->keylen = gulm_plock_packname(fs->fs_name, name->ln_number,
+			key, GIO_KEY_SIZE);
+	item->key = key;
+	item->subid = (unsigned long) fl->fl_owner;
+	item->start = fl->fl_start;
+	item->stop = fl->fl_end;
+	item->type = glq_req_type_state;
+	item->state = lg_lock_state_Unlock;
+	item->flags = 0;
+	item->error = pret.error = 0;
+
+	init_completion (&pret.sleep);
+
+	item->misc = &pret;
+	item->finish = gulm_plock_finish;
+
+	glq_queue (item);
+	wait_for_completion (&pret.sleep);
+
+	err = -pret.error;
+	if ( err == 0) err = posix_lock_file_wait(file, fl);
+
+fail:
+	return err;
+}
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_prints.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_prints.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_prints.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_prints.h	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,45 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __gulm_prints_h__
+#define __gulm_prints_h__
+#include "gulm_log_msg_bits.h"
+
+#define PROTO_NAME "lock_gulm"
+
+#ifdef GULM_ASSERT
+#undef GULM_ASSERT
+#endif
+#define GULM_ASSERT(x, do) \
+{ \
+  if (!(x)) \
+  { \
+    printk("\n"PROTO_NAME":  Assertion failed on line %d of file %s\n" \
+               PROTO_NAME":  assertion:  \"%s\"\n", \
+               __LINE__, __FILE__, #x ); \
+    {do} \
+    panic("\n"PROTO_NAME":  Record message above and reboot.\n"); \
+  } \
+}
+
+#define log_msg(v, fmt, args...) if(((v)&gulm_cm.verbosity)==(v)||(v)==lgm_Always) {\
+   printk(PROTO_NAME ": " fmt, ## args); \
+}
+#define log_err(fmt, args...) {\
+   printk(KERN_ERR PROTO_NAME ": ERROR " fmt, ## args); \
+}
+
+#define log_nop(fmt, args...)
+#define TICK printk("TICK==>" PROTO_NAME ": [%s:%d] pid:%d\n" , __FILE__ , __LINE__ , current->pid )
+
+#endif /*__gulm_prints_h__*/
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_recsig.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_recsig.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/gulm_recsig.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/gulm_recsig.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,337 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include "gulm_lock_queue.h"
+
+/* This is some speed hackery to abuse the locking system to allow clients
+ * to notify each other of things.  It is a super simple signaling type
+ * system.  All you can know is that a signal was touched.  Pretty
+ * sreightforword.
+ *
+ * I really would rather have something else.  I've a couple of good ideas,
+ * and will most likely switch to one of those at a later date.  But this
+ * is good enough for now, and will get us through the next release.
+ *
+ * (functionally nothing wrong with this, theoretically, I can think of
+ * better designs than this abuse.)
+ */
+
+extern gulm_cm_t gulm_cm;
+
+struct sig_watcher {
+	struct list_head sw_list;
+	uint8_t *name;
+	uint8_t len;
+	void(*func)(void *misc);
+	void *misc;
+};
+struct list_head sig_watchers_list;
+spinlock_t sig_watchers_lock;
+
+/****************************************************************************/
+/* internal funcs */
+/**
+ * release_sw - 
+ * @name: 
+ * @len: 
+ * 
+ * 
+ * Returns: void
+ */
+static void release_sw(uint8_t *name, uint8_t len)
+{
+	struct list_head *tmp;
+	struct sig_watcher *sw;
+	spin_lock(&sig_watchers_lock);
+	list_for_each(tmp, &sig_watchers_list) {
+		sw = list_entry (tmp, struct sig_watcher, sw_list);
+		if( memcmp(name, sw->name, len) == 0 ) {
+			list_del(tmp);
+			kfree(sw->name);
+			kfree(sw);
+			break;
+		}
+	}
+	spin_unlock(&sig_watchers_lock);
+}
+/**
+ * add_sw - 
+ * @name: 
+ * @len: 
+ * @func: 
+ * @misc: 
+ * 
+ * 
+ * Returns: int
+ */
+static int add_sw(uint8_t *name, uint8_t len,
+		void(*func)(void *misc), void *misc)
+{
+	struct sig_watcher *sw;
+
+	sw = kmalloc(sizeof(struct sig_watcher), GFP_KERNEL);
+	if( sw == NULL ) return -ENOMEM;
+	sw->name = kmalloc(len, GFP_KERNEL);
+	if( sw->name == NULL ) {
+		kfree(sw);
+		return -ENOMEM;
+	}
+	memcpy(sw->name, name, len);
+	sw->len = len;
+	sw->func = func;
+	sw->misc = misc;
+	INIT_LIST_HEAD (&sw->sw_list);
+	spin_lock(&sig_watchers_lock);
+	list_add(&sw->sw_list, &sig_watchers_list);
+	spin_unlock(&sig_watchers_lock);
+	return 0;
+}
+
+/**
+ * gulm_sw_finish - 
+ * @item: 
+ * 
+ * 
+ * Returns: void
+ */
+void gulm_sw_finish (struct glck_req *item)
+{
+	struct completion *sleep = (struct completion *)item->misc;
+	complete (sleep);
+}
+
+/**
+ * hold_watch_lock - 
+ * @name: 
+ * @len: 
+ * 
+ * 
+ * Returns: void
+ */
+void hold_watch_lock(gulm_fs_t *fs, uint8_t *name, uint8_t len)
+{
+	uint8_t key[GIO_KEY_SIZE];
+	uint16_t keylen = GIO_KEY_SIZE;
+	struct completion sleep;
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		return;
+	}
+
+	item->keylen = pack_lock_key(key, keylen, 'S', fs->fs_name, name, len);
+	item->key = key;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_state;
+	item->state = lg_lock_state_Shared;
+	item->flags = lg_lock_flag_IgnoreExp;
+	item->error =  0;
+	item->lvb = NULL;
+	item->lvblen = 0;
+
+	init_completion (&sleep);
+
+	item->misc = &sleep;
+	item->finish = gulm_sw_finish;
+
+	glq_queue (item);
+	wait_for_completion (&sleep);
+}
+/**
+ * release_watch_lock - 
+ * @name: 
+ * @len: 
+ * 
+ * 
+ * Returns: void
+ */
+void release_watch_lock(gulm_fs_t *fs, uint8_t *name, uint8_t len)
+{
+	uint8_t key[GIO_KEY_SIZE];
+	uint16_t keylen = GIO_KEY_SIZE;
+	struct completion sleep;
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		return;
+	}
+
+	item->keylen = pack_lock_key(key, keylen, 'S', fs->fs_name, name, len);
+	item->key = key;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_state;
+	item->state = lg_lock_state_Unlock;
+	item->flags = 0;
+	item->error =  0;
+	item->lvb = NULL;
+	item->lvblen = 0;
+
+	init_completion (&sleep);
+
+	item->misc = &sleep;
+	item->finish = gulm_sw_finish;
+
+	glq_queue (item);
+	wait_for_completion (&sleep);
+}
+/**
+ * signal_watch_lock - 
+ * @name: 
+ * @len: 
+ * 
+ * 
+ * Returns: void
+ */
+void signal_watch_lock(gulm_fs_t *fs, uint8_t *name, uint8_t len)
+{
+	uint8_t key[GIO_KEY_SIZE];
+	uint16_t keylen = GIO_KEY_SIZE;
+	struct completion sleep;
+	glckr_t *item;
+
+	item = glq_get_new_req();
+	if (item == NULL) {
+		return;
+	}
+
+	item->keylen = pack_lock_key(key, keylen, 'S', fs->fs_name, name, len);
+	item->key = key;
+	item->subid = 0;
+	item->start = 0;
+	item->stop = ~((uint64_t)0);
+	item->type = glq_req_type_state;
+	item->state = lg_lock_state_Exclusive;
+	item->flags = lg_lock_flag_Try|lg_lock_flag_DoCB|lg_lock_flag_IgnoreExp;
+	item->error =  0;
+	item->lvb = NULL;
+	item->lvblen = 0;
+
+	init_completion (&sleep);
+
+	item->misc = &sleep;
+	item->finish = gulm_sw_finish;
+
+	glq_queue (item);
+	wait_for_completion (&sleep);
+}
+
+/**
+ * sig_watcher_lock_drop - 
+ * @key: 
+ * @keylen: 
+ * 
+ * 
+ * Returns: void
+ */
+void sig_watcher_lock_drop(uint8_t * key, uint16_t keylen)
+{
+	struct list_head *tmp;
+	struct sig_watcher *sw = NULL;
+	int found = FALSE;
+	uint8_t *fsname, len, *name, nlen;
+	if( key[0] != 'S' ) return; /* not a Signal lock */
+	len = key[1];
+	fsname = &key[2];
+	nlen = key[3 + len];
+	name = &key[4 + len];
+	spin_lock(&sig_watchers_lock);
+	list_for_each(tmp, &sig_watchers_list) {
+		sw = list_entry (tmp, struct sig_watcher, sw_list);
+		if( memcmp(name, sw->name, MIN(nlen, sw->len)) == 0 ) {
+			found = TRUE;
+			break;
+		}
+	}
+	spin_unlock(&sig_watchers_lock);
+	if(found) {
+		sw->func(sw->misc);
+	}
+}
+
+/****************************************************************************/
+
+/**
+ * sig_water_init - 
+ * @oid: 
+ * 
+ * 
+ * Returns: void
+ */
+void sig_watcher_init(void)
+{
+	INIT_LIST_HEAD (&sig_watchers_list);
+	spin_lock_init(&sig_watchers_lock);
+}
+
+
+/**
+ * watch_sig - 
+ * @name: 
+ * @len: 
+ * @misc: 
+ * @misc: 
+ * 
+ * Returns: int
+ */
+int watch_sig(gulm_fs_t *fs, uint8_t *name, uint8_t len, void(*func)(void *misc), void *misc)
+{
+	if( func == NULL ) {
+		/* unlock signal lock */
+		release_watch_lock(fs, name, len);
+		release_sw(name, len);
+	}else{
+		/* hold signal lock shared. */
+		if(add_sw(name, len, func, misc) == 0 ) {
+			hold_watch_lock(fs, name, len);
+		}else{
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ * tap_sig - 
+ * @name: 
+ * @len: 
+ * 
+ * 
+ * Returns: int
+ */
+void tap_sig(gulm_fs_t *fs, uint8_t *name, uint8_t len)
+{
+	signal_watch_lock(fs, name, len);
+#if 0
+	/* Make sure it is still Shr. (very lazy way to do this. but it
+	 * should be low traffic enough not to bother.)
+	 * */
+	hold_watch_lock(fs, name, len);
+#endif
+}
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/handler.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/handler.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/handler.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/handler.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,343 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gulm.h"
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#include "handler.h"
+
+/* things about myself
+ * mostly just for verbosity here.
+ * */
+extern gulm_cm_t gulm_cm;
+
+/* the task struct */
+typedef struct runtask_s {
+	struct list_head rt_list;
+
+	gulm_fn fn;
+	lm_callback_t cb;
+	lm_fsdata_t *fsdata;
+	int type;
+	uint64_t lmnum;
+	unsigned int lmtype;
+	int result;
+
+} runtask_t;
+/* ooo crufty. */
+#define LM_CB_GULM_FN 169
+#if LM_CB_GULM_FN == LM_CB_NEED_E || \
+    LM_CB_GULM_FN == LM_CB_NEED_D || \
+    LM_CB_GULM_FN == LM_CB_NEED_S || \
+    LM_CB_GULM_FN == LM_CB_NEED_RECOVERY || \
+    LM_CB_GULM_FN == LM_CB_DROPLOCKS || \
+    LM_CB_GULM_FN == LM_CB_ASYNC
+#error "LM_CB_GULM_FN collision with other LM_CB_*"
+#endif
+
+static __inline__ int
+queue_empty (callback_qu_t * cq)
+{
+	int ret;
+	spin_lock (&cq->list_lock);
+	ret = list_empty (&cq->run_tasks);
+	spin_unlock (&cq->list_lock);
+	return ret;
+}
+
+/**
+ * handler - 
+ * @d: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+handler (void *d)
+{
+	callback_qu_t *cq = (callback_qu_t *) d;
+	runtask_t *rt;
+	struct list_head *tmp;
+	struct lm_lockname lockname;
+	struct lm_async_cb acb;
+
+	daemonize ("gulm_Cb_Handler");
+	atomic_inc (&cq->num_threads);
+	complete (&cq->startup);
+
+	while (cq->running) {
+		do {
+			DECLARE_WAITQUEUE (__wait_chan, current);
+			current->state = TASK_INTERRUPTIBLE;
+			add_wait_queue (&cq->waiter, &__wait_chan);
+			if (queue_empty (cq))
+				schedule ();
+			remove_wait_queue (&cq->waiter, &__wait_chan);
+			current->state = TASK_RUNNING;
+		} while (0);
+
+		if (!cq->running)
+			break;
+		/* remove item from list */
+		spin_lock (&cq->list_lock);
+		if (list_empty (&cq->run_tasks)) {
+			spin_unlock (&cq->list_lock);
+			continue;	/* nothing here. move on */
+		}
+		/* take items off the end of the list, since we add them to the
+		 * beginning.
+		 */
+		tmp = (&cq->run_tasks)->prev;
+		list_del (tmp);
+		cq->task_count--;
+		spin_unlock (&cq->list_lock);
+
+		rt = list_entry (tmp, runtask_t, rt_list);
+
+		if (rt->type == LM_CB_ASYNC) {
+			acb.lc_name.ln_number = rt->lmnum;
+			acb.lc_name.ln_type = rt->lmtype;
+			acb.lc_ret = rt->result;
+			rt->cb (rt->fsdata, rt->type, &acb);
+		} else if (rt->type == LM_CB_GULM_FN) {
+			rt->fn (rt->fsdata);
+		} else {
+			lockname.ln_number = rt->lmnum;
+			lockname.ln_type = rt->lmtype;
+			rt->cb (rt->fsdata, rt->type, &lockname);
+		}
+
+		kfree (rt);
+
+	}			/*while(running) */
+
+	atomic_dec (&cq->num_threads);
+	complete (&cq->startup);
+	return 0;
+}
+
+/**
+ * display_handler_queue - 
+ * @cq: 
+ * 
+ * remember, items are added to the head, and removed from the tail.
+ * So the last item listed, is the next item to be handled.
+ * 
+ */
+void
+display_handler_queue (callback_qu_t * cq)
+{
+	struct list_head *lltmp;
+	runtask_t *rt;
+	int i = 0;
+	log_msg (lgm_Always, "Dumping Handler queue with %d items, max %d\n",
+		 cq->task_count, cq->task_max);
+	spin_lock (&cq->list_lock);
+	list_for_each (lltmp, &cq->run_tasks) {
+		rt = list_entry (lltmp, runtask_t, rt_list);
+		if (rt->type == LM_CB_ASYNC) {
+			log_msg (lgm_Always,
+				 "%4d ASYNC    (%" PRIu64 ", %u) result:%#x\n",
+				 i, rt->lmnum, rt->lmtype, rt->result);
+		} else if (rt->type == LM_CB_GULM_FN) {
+			log_msg (lgm_Always, "%4d GULM FN  func:%p data:%p\n",
+				 i, rt->fn, rt->fsdata);
+		} else {	/* callback. */
+			log_msg (lgm_Always,
+				 "%4d CALLBACK req:%u (%" PRIu64 ", %u)\n", i,
+				 rt->type, rt->lmnum, rt->lmtype);
+		}
+		i++;
+	}
+	spin_unlock (&cq->list_lock);
+}
+
+/**
+ * alloc_runtask - 
+ * Returns: runtask_t
+ */
+runtask_t *
+alloc_runtask (void)
+{
+	runtask_t *rt;
+	rt = kmalloc (sizeof (runtask_t), GFP_KERNEL);
+	return rt;
+}
+
+/**
+ * qu_function_call - 
+ * @cq: 
+ * @fn: 
+ * @data: 
+ * 
+ * Generic function execing on the handler thread.  Mostly so I can add
+ * single things quick without having to build all the details into the
+ * handler queues.
+ * 
+ * Returns: int
+ */
+int
+qu_function_call (callback_qu_t * cq, gulm_fn fn, void *data)
+{
+	runtask_t *rt;
+	rt = alloc_runtask ();
+	if (rt == NULL)
+		return -ENOMEM;
+	rt->cb = NULL;
+	rt->fn = fn;
+	rt->fsdata = data;
+	rt->type = LM_CB_GULM_FN;
+	rt->lmtype = 0;
+	rt->lmnum = 0;
+	rt->result = 0;
+	INIT_LIST_HEAD (&rt->rt_list);
+	spin_lock (&cq->list_lock);
+	list_add (&rt->rt_list, &cq->run_tasks);
+	cq->task_count++;
+	if (cq->task_count > cq->task_max)
+		cq->task_max = cq->task_count;
+	spin_unlock (&cq->list_lock);
+	wake_up (&cq->waiter);
+	return 0;
+}
+
+/**
+ * qu_async_rpl - 
+ * @cq: 
+ * @cb: 
+ * @fsdata: 
+ * @lockname: 
+ * @result: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+qu_async_rpl (callback_qu_t * cq, lm_callback_t cb, lm_fsdata_t * fsdata,
+	      struct lm_lockname *lockname, int result)
+{
+	runtask_t *rt;
+	rt = alloc_runtask ();
+	if (rt == NULL)
+		return -ENOMEM;
+	rt->cb = cb;
+	rt->fsdata = fsdata;
+	rt->type = LM_CB_ASYNC;
+	rt->lmtype = lockname->ln_type;
+	rt->lmnum = lockname->ln_number;
+	rt->result = result;
+	INIT_LIST_HEAD (&rt->rt_list);
+	spin_lock (&cq->list_lock);
+	list_add (&rt->rt_list, &cq->run_tasks);
+	cq->task_count++;
+	if (cq->task_count > cq->task_max)
+		cq->task_max = cq->task_count;
+	spin_unlock (&cq->list_lock);
+	wake_up (&cq->waiter);
+	return 0;
+}
+
+/**
+ * qu_drop_req - 
+ * 
+ * Returns: <0:Error; =0:Ok
+ */
+int
+qu_drop_req (callback_qu_t * cq, lm_callback_t cb, lm_fsdata_t * fsdata,
+	     int type, uint8_t lmtype, uint64_t lmnum)
+{
+	runtask_t *rt;
+	rt = alloc_runtask ();
+	if (rt == NULL)
+		return -ENOMEM;
+	rt->cb = cb;
+	rt->fsdata = fsdata;
+	rt->type = type;
+	rt->lmtype = lmtype;
+	rt->lmnum = lmnum;
+	rt->result = 0;
+	INIT_LIST_HEAD (&rt->rt_list);
+	spin_lock (&cq->list_lock);
+	list_add (&rt->rt_list, &cq->run_tasks);
+	cq->task_count++;
+	if (cq->task_count > cq->task_max)
+		cq->task_max = cq->task_count;
+	spin_unlock (&cq->list_lock);
+	wake_up (&cq->waiter);
+	return 0;
+}
+
+/**
+ * stop_callback_qu - stop the handler thread
+ */
+void
+stop_callback_qu (callback_qu_t * cq)
+{
+	struct list_head *lltmp, *tmp;
+	runtask_t *rt;
+
+	if (cq->running) {
+		cq->running = FALSE;
+		/* make sure all thread stop.
+		 * */
+		while (atomic_read (&cq->num_threads) > 0) {
+			wake_up (&cq->waiter);
+			wait_for_completion (&cq->startup);
+		}
+		/* clear out any left overs. */
+		list_for_each_safe (tmp, lltmp, &cq->run_tasks) {
+			rt = list_entry (tmp, runtask_t, rt_list);
+			list_del (tmp);
+			kfree (rt);
+		}
+	}
+}
+
+/**
+ * start_callback_qu - 
+ *
+ * Returns: <0:Error, >=0:Ok
+ */
+int
+start_callback_qu (callback_qu_t * cq, int cnt)
+{
+	int err;
+	INIT_LIST_HEAD (&cq->run_tasks);
+	spin_lock_init (&cq->list_lock);
+	init_completion (&cq->startup);
+	init_waitqueue_head (&cq->waiter);
+	atomic_set (&cq->num_threads, 0);
+	cq->running = TRUE;
+	cq->task_count = 0;
+	cq->task_max = 0;
+	if (cnt <= 0)
+		cnt = 2;
+	for (; cnt > 0; cnt--) {
+		err = kernel_thread (handler, cq, 0);
+		if (err < 0) {
+			stop_callback_qu (cq);
+			/* calling stop here might not behave correctly in all error
+			 * cases.
+			 */
+			return err;
+		}
+		wait_for_completion (&cq->startup);
+	}
+	return 0;
+}
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/handler.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/handler.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/handler.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/handler.h	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,42 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __handler_c__
+#define __handler_c__
+#include <linux/lm_interface.h>
+
+struct callback_qu_s {
+	struct completion startup;
+	int running;
+	int task_count;
+	int task_max;
+	struct list_head run_tasks;
+	spinlock_t list_lock;
+	wait_queue_head_t waiter;
+	atomic_t num_threads;
+};
+typedef struct callback_qu_s callback_qu_t;
+
+/* kinda an excess overloading */
+typedef void (*gulm_fn) (void *);
+int qu_function_call (callback_qu_t * cq, gulm_fn fn, void *data);
+
+int qu_async_rpl (callback_qu_t * cq, lm_callback_t cb, lm_fsdata_t * fsdata,
+		  struct lm_lockname *lockname, int result);
+int qu_drop_req (callback_qu_t * cq, lm_callback_t cb, lm_fsdata_t * fsdata,
+		 int type, uint8_t lmtype, uint64_t lmnum);
+int start_callback_qu (callback_qu_t * cq, int cnt);
+void stop_callback_qu (callback_qu_t * cq);
+void display_handler_queue (callback_qu_t * cq);
+
+#endif /*__handler_c__*/
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/lg_core.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/lg_core.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/lg_core.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/lg_core.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,669 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/* All of the core related functions for services are here. */
+
+#include "lg_priv.h"
+
+/**
+ * lg_core_selector - 
+ * @ulm_interface_p: 
+ * 
+ * 
+ * Returns: int
+ */
+xdr_socket
+lg_core_selector (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL || lg->first_magic != LGMAGIC
+	    || lg->last_magic != LGMAGIC)
+#ifdef __KERNEL__
+		return NULL;
+#else
+		return -EINVAL;
+#endif
+
+	return lg->core_fd;
+}
+
+/**
+ * lg_core_handle_messages - 
+ * @ulm_interface_p: 
+ * @lg_core_callbacks_t: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_handle_messages (gulm_interface_p lgp, lg_core_callbacks_t * ccbp,
+			 void *misc)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_dec_t *dec;
+	int err = 0;
+	uint64_t x_gen;
+	uint32_t x_code, x_error, x_rank;
+	struct in6_addr x_ip;
+	uint8_t x_state, x_mode;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EBADR;
+
+	down (&lg->core_recver);
+	if (lg->in_core_hm)
+		return -EDEADLK;
+	lg->in_core_hm = TRUE;
+	up (&lg->core_recver);
+
+	dec = lg->core_dec;
+
+	err = xdr_dec_uint32 (dec, &x_code);
+	if (err != 0)
+		goto exit;
+
+	if (gulm_core_login_rpl == x_code) {
+		do {
+			if ((err = xdr_dec_uint64 (dec, &x_gen)) < 0)
+				break;
+			if ((err = xdr_dec_uint32 (dec, &x_error)) < 0)
+				break;
+			if ((err = xdr_dec_uint32 (dec, &x_rank)) < 0)
+				break;
+			if ((err = xdr_dec_uint8 (dec, &x_state)) < 0)
+				break;
+		} while (0);
+		if (err != 0)
+			goto exit;
+		if (ccbp->login_reply == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = ccbp->login_reply (misc, x_gen, x_error, x_rank, x_state);
+		goto exit;
+	} else if (gulm_core_logout_rpl == x_code) {
+		if ((err = xdr_dec_uint32 (dec, &x_error)) != 0)
+			goto exit;
+		if (ccbp->logout_reply != NULL) {
+			err = ccbp->logout_reply (misc);
+		}
+
+		xdr_close (&lg->core_fd);
+		xdr_enc_release (lg->core_enc);
+		lg->core_enc = NULL;
+		xdr_dec_release (lg->core_dec);
+		lg->core_dec = NULL;
+
+		goto exit;
+	} else if (gulm_core_mbr_lstrpl == x_code) {
+		if (ccbp->nodelist != NULL) {
+			err = ccbp->nodelist (misc, lglcb_start, NULL, 0, 0);
+			if (err != 0)
+				goto exit;
+		}
+		do {
+			if ((err = xdr_dec_list_start (dec)) != 0)
+				break;
+			while (xdr_dec_list_stop (dec) != 0) {
+				if ((err =
+				     xdr_dec_string_ag (dec, &lg->cfba,
+							&lg->cfba_len)) != 0)
+					break;
+				if ((err = xdr_dec_ipv6 (dec, &x_ip)) != 0)
+					break;
+				if ((err = xdr_dec_uint8 (dec, &x_state)) != 0)
+					break;
+				if ((err = xdr_dec_uint8 (dec, &x_mode)) != 0)
+					break;
+				if ((err = xdr_dec_uint8 (dec, &x_mode)) != 0)
+					break;
+				if ((err = xdr_dec_uint32 (dec, &x_rank)) != 0)
+					break;
+				if ((err = xdr_dec_uint64 (dec, &x_gen)) != 0)
+					break;
+				if ((err = xdr_dec_uint64 (dec, &x_gen)) != 0)
+					break;
+				if ((err = xdr_dec_uint64 (dec, &x_gen)) != 0)
+					break;
+
+				if (ccbp->nodelist != NULL) {
+					err =
+					    ccbp->nodelist (misc, lglcb_item,
+							    lg->cfba, &x_ip,
+							    x_state);
+					if (err != 0)
+						goto exit;
+				}
+
+			}
+		} while (0);
+		if (err != 0) {
+			goto exit;
+		}
+		if (ccbp->nodelist == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = ccbp->nodelist (misc, lglcb_stop, NULL, 0, 0);
+		goto exit;
+	} else if (gulm_core_state_chgs == x_code) {
+		do {
+			if ((err = xdr_dec_uint8 (dec, &x_state)) != 0)
+				break;
+			if ((err = xdr_dec_uint8 (dec, &x_mode)) != 0)
+				break;
+			if (x_state == gio_Mbr_ama_Slave) {
+				if ((err = xdr_dec_ipv6 (dec, &x_ip)) != 0)
+					break;
+				if ((err =
+				     xdr_dec_string_ag (dec, &lg->cfba,
+							&lg->cfba_len)) != 0)
+					break;
+			}
+		} while (0);
+		if (err != 0) {
+			goto exit;
+		}
+		if (ccbp->statechange == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = ccbp->statechange (misc, x_state, x_mode, &x_ip, lg->cfba);
+		goto exit;
+	} else if (gulm_core_mbr_updt == x_code) {
+		do {
+			if ((err =
+			     xdr_dec_string_ag (dec, &lg->cfba,
+						&lg->cfba_len)) != 0)
+				break;
+			if ((err = xdr_dec_ipv6 (dec, &x_ip)) != 0)
+				break;
+			if ((err = xdr_dec_uint8 (dec, &x_state)) != 0)
+				break;
+		} while (0);
+		if (err != 0) {
+			goto exit;
+		}
+		if (ccbp->nodechange == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = ccbp->nodechange (misc, lg->cfba, &x_ip, x_state);
+		goto exit;
+	} else if (gulm_core_res_list == x_code) {
+		if (ccbp->service_list != NULL) {
+			if ((err =
+			     ccbp->service_list (misc, lglcb_start, NULL)) != 0)
+				goto exit;
+		}
+		do {
+			if ((err = xdr_dec_list_start (dec)) != 0)
+				break;
+			while (xdr_dec_list_stop (dec)) {
+				if ((err =
+				     xdr_dec_string_ag (dec, &lg->cfba,
+							&lg->cfba_len)) != 0)
+					break;
+				if (ccbp->service_list != NULL) {
+					if ((err =
+					     ccbp->service_list (misc,
+								 lglcb_item,
+								 lg->cfba)) !=
+					    0) {
+						goto exit;
+					}
+				}
+			}
+		} while (0);
+		if (err != 0) {
+			goto exit;
+		}
+		if (ccbp->service_list == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = ccbp->service_list (misc, lglcb_stop, NULL);
+		goto exit;
+	} else if (gulm_info_stats_rpl == x_code) {
+		do {
+			if ((err = xdr_dec_list_start (dec)) != 0)
+				break;
+			while (xdr_dec_list_stop (dec) != 0) {
+				if ((err =
+				     xdr_dec_string_ag (dec, &lg->cfba,
+							&lg->cfba_len)) != 0)
+					break;
+				if ((err =
+				     xdr_dec_string_ag (dec, &lg->cfbb,
+							&lg->cfbb_len)) != 0)
+					break;
+			}
+		} while (0);
+		goto exit;
+	} else if (gulm_err_reply == x_code) {
+		if ((err = xdr_dec_uint32 (dec, &x_code)) != 0)
+			goto exit;
+		if ((err = xdr_dec_uint32 (dec, &x_error)) != 0)
+			goto exit;
+		if (ccbp->error == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = ccbp->error (misc, x_error);
+		goto exit;
+	} else {
+		/* unknown code. what to do? */
+		err = -EPROTO;
+		goto exit;
+	}
+
+      exit:
+	lg->in_core_hm = FALSE;
+	return err;
+}
+
+/**
+ * lg_core_login - 
+ * @lgp: 
+ * @important: 
+ *
+ * On any error, things are closed and released to the state of things
+ * before you called login.
+ * 
+ * Returns: int
+ */
+int
+lg_core_login (gulm_interface_p lgp, int important)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	struct sockaddr_in6 adr;
+	int err;
+	xdr_socket cfd;
+	xdr_enc_t *enc;
+	xdr_dec_t *dec;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	adr.sin6_family = AF_INET6;
+	adr.sin6_addr = lg_in6addr_loopback;
+	adr.sin6_port = htons (lg->core_port);
+
+	if ((err = xdr_open (&cfd)) < 0) {
+		return err;
+	}
+
+	if ((err = xdr_connect (&adr, cfd)) < 0) {
+		xdr_close (&cfd);
+		return err;
+	}
+
+	enc = xdr_enc_init (cfd, 128);
+	if (enc == NULL) {
+		xdr_close (&cfd);
+		return -ENOMEM;
+	}
+
+	dec = xdr_dec_init (cfd, 128);
+	if (enc == NULL) {
+		xdr_enc_release (enc);
+		xdr_close (&cfd);
+		return -ENOMEM;
+	}
+
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_reslgn_req)) < 0)
+			break;
+		if ((err = xdr_enc_uint32 (enc, GIO_WIREPROT_VERS)) < 0)
+			break;
+		if ((err = xdr_enc_string (enc, lg->clusterID)) < 0)
+			break;
+		if ((err = xdr_enc_string (enc, lg->service_name)) < 0)
+			break;
+		if ((err = xdr_enc_uint32 (enc, gulm_svc_opt_locked |
+				      gulm_svc_opt_important)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) < 0)
+			break;
+	} while (0);
+	if (err != 0) {
+		xdr_dec_release (dec);
+		xdr_enc_release (enc);
+		xdr_close (&cfd);
+		return err;
+	}
+
+	down (&lg->core_sender);
+	lg->core_fd = cfd;
+	lg->core_enc = enc;
+	lg->core_dec = dec;
+	up (&lg->core_sender);
+
+	return 0;
+}
+
+/**
+ * lg_core_logout - 
+ * @lgp: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_logout (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_fd < 0 || lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->core_enc;
+
+	down (&lg->core_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_logout_req)) != 0)
+			break;
+		if ((err = xdr_enc_string (enc, lg->service_name)) != 0)
+			break;
+		if ((err = xdr_enc_uint8 (enc, gio_Mbr_ama_Resource)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->core_sender);
+	return err;
+}
+
+/**
+ * lg_core_nodeinfo - 
+ * @lgp: 
+ * @nodename: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_nodeinfo (gulm_interface_p lgp, char *nodename)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_fd < 0 || lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EINVAL;
+
+	if (nodename == NULL)
+		return -EINVAL;
+
+	enc = lg->core_enc;
+
+	down (&lg->core_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_mbr_req)) != 0)
+			break;
+		if ((err = xdr_enc_string (enc, nodename)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->core_sender);
+	return err;
+}
+
+/**
+ * lg_core_nodelist - 
+ * @lgp: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_nodelist (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_fd < 0 || lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->core_enc;
+
+	down (&lg->core_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_mbr_lstreq)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->core_sender);
+	return err;
+}
+
+/**
+ * lg_core_servicelist - 
+ * @lgp: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_servicelist (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_fd < 0 || lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->core_enc;
+
+	down (&lg->core_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_res_req)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->core_sender);
+	return err;
+}
+
+/**
+ * lg_core_corestate - 
+ * @lgp: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_corestate (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_fd < 0 || lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->core_enc;
+
+	down (&lg->core_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_state_req)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->core_sender);
+	return err;
+}
+
+/**
+ * lg_core_shutdown - 
+ * @lgp: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_shutdown (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_fd < 0 || lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->core_enc;
+
+	down (&lg->core_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_shutdown)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->core_sender);
+	return err;
+}
+
+/**
+ * lg_core_forceexpire - 
+ * @lgp: 
+ * @node_name: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_forceexpire (gulm_interface_p lgp, char *nodename)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_fd < 0 || lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EINVAL;
+
+	if (nodename == NULL)
+		return -EINVAL;
+
+	enc = lg->core_enc;
+
+	down (&lg->core_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_mbr_force)) != 0)
+			break;
+		if ((err = xdr_enc_string (enc, nodename)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->core_sender);
+	return err;
+}
+
+/**
+ * lg_core_forcepending - 
+ * @lgp: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_core_forcepending (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_fd < 0 || lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->core_enc;
+
+	down (&lg->core_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_core_forcepend)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->core_sender);
+	return err;
+}
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/lg_lock.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/lg_lock.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/lg_lock.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/lg_lock.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,785 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/* all of the lock related fucntion are here. */
+#include "lg_priv.h"
+
+/**
+ * lg_lock_selector - 
+ * @ulm_interface_p: 
+ * 
+ * 
+ * Returns: int
+ */
+xdr_socket
+lg_lock_selector (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL || lg->first_magic != LGMAGIC
+	    || lg->last_magic != LGMAGIC)
+#ifdef __KERNEL__
+		return NULL;
+#else
+		return -EINVAL;
+#endif
+
+	return lg->lock_fd;
+}
+
+/**
+ * lg_lock_handle_messages - 
+ * @ulm_interface_p: 
+ * @lg_lockspace_callbacks_t: 
+ * 
+ * Returns: int
+ */
+int
+lg_lock_handle_messages (gulm_interface_p lgp, lg_lockspace_callbacks_t * cbp,
+			 void *misc)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_dec_t *dec;
+	int err = 0;
+	uint64_t x_subid, x_start, x_stop;
+	uint32_t x_code, x_error, x_flags;
+	uint16_t x_keylen, x_lvblen = 0;
+	uint8_t x_state;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->core_enc == NULL || lg->core_dec == NULL)
+		return -EBADR;
+
+	down (&lg->lock_recver);
+	if (lg->in_lock_hm)
+		return -EDEADLK;
+	lg->in_lock_hm = TRUE;
+	up (&lg->lock_recver);
+
+	dec = lg->lock_dec;
+
+	err = xdr_dec_uint32 (dec, &x_code);
+	if (err != 0)
+		goto exit;
+
+	if (gulm_lock_login_rpl == x_code) {
+		do {
+			if ((err = xdr_dec_uint32 (dec, &x_error)) != 0)
+				break;
+			if ((err = xdr_dec_uint8 (dec, &x_state)) != 0)
+				break;
+		} while (0);
+		if (err != 0)
+			goto exit;
+		if (cbp->login_reply == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = cbp->login_reply (misc, x_error, x_state);
+		goto exit;
+	} else if (gulm_lock_logout_rpl == x_code) {
+		if (cbp->logout_reply != NULL) {
+			err = cbp->logout_reply (misc);
+		}
+
+		xdr_close (&lg->lock_fd);
+		xdr_enc_release (lg->lock_enc);
+		lg->lock_enc = NULL;
+		xdr_dec_release (lg->lock_dec);
+		lg->lock_dec = NULL;
+
+		goto exit;
+	} else if (gulm_lock_state_rpl == x_code) {
+		do {
+			if ((err =
+			     xdr_dec_raw_ag (dec, (void **) &lg->lfba,
+					     &lg->lfba_len, &x_keylen)) != 0)
+				break;
+         		if ((err = xdr_dec_uint64(dec, &x_subid)) != 0 )
+				break;
+         		if ((err = xdr_dec_uint64(dec, &x_start)) != 0 )
+				break;
+         		if ((err = xdr_dec_uint64(dec, &x_stop)) != 0 )
+				break;
+			if ((err = xdr_dec_uint8 (dec, &x_state)) != 0)
+				break;
+			if ((err = xdr_dec_uint32 (dec, &x_flags)) != 0)
+				break;
+			if ((err = xdr_dec_uint32 (dec, &x_error)) != 0)
+				break;
+			if (x_flags & gio_lck_fg_hasLVB) {
+				if ((err =
+				     xdr_dec_raw_ag (dec, (void **) &lg->lfbb,
+						     &lg->lfbb_len,
+						     &x_lvblen)) != 0)
+					break;
+			}
+		} while (0);
+		if (err != 0) {
+			goto exit;
+		}
+		if (x_keylen <= 4) {
+			err = -EPROTO;	/* or something */
+			goto exit;
+		}
+		if (cbp->lock_state == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = cbp->lock_state (misc, &lg->lfba[4], x_keylen - 4,
+				       x_subid, x_start, x_stop,
+				       x_state, x_flags, x_error,
+				       lg->lfbb, x_lvblen);
+		goto exit;
+	} else if (gulm_lock_action_rpl == x_code) {
+		do {
+			if ((err =
+			     xdr_dec_raw_ag (dec, (void **) &lg->lfba,
+					     &lg->lfba_len, &x_keylen)) != 0)
+				break;
+         		if ((err = xdr_dec_uint64(dec, &x_subid)) != 0 )
+				break;
+			if ((err = xdr_dec_uint8 (dec, &x_state)) != 0)
+				break;
+			if ((err = xdr_dec_uint32 (dec, &x_error)) != 0)
+				break;
+		} while (0);
+		if (err != 0) {
+			goto exit;
+		}
+		if (x_keylen <= 4) {
+			err = -EPROTO;	/* or something */
+			goto exit;
+		}
+		if (cbp->lock_action == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err =
+		    cbp->lock_action (misc, &lg->lfba[4], x_keylen - 4,
+				      x_subid, x_state, x_error);
+		goto exit;
+	} else if (gulm_lock_query_rpl == x_code) {
+		uint64_t x_c_subid=0, x_c_start=0, x_c_stop=0;
+		uint8_t x_c_state=0;
+		do {
+			if ((err =
+			     xdr_dec_raw_ag (dec, (void **) &lg->lfba,
+					     &lg->lfba_len, &x_keylen)) != 0)
+				break;
+         		if ((err = xdr_dec_uint64(dec, &x_subid)) != 0 )
+				break;
+         		if ((err = xdr_dec_uint64(dec, &x_start)) != 0 )
+				break;
+         		if ((err = xdr_dec_uint64(dec, &x_stop)) != 0 )
+				break;
+			if ((err = xdr_dec_uint8 (dec, &x_state)) != 0)
+				break;
+			if ((err = xdr_dec_uint32 (dec, &x_error)) != 0)
+				break;
+			/* i realize that I'm pretty much ignoring the fact that
+			 * this is can be a list of items.  As of current, there
+			 * is never more than one item on this list.
+			 * I think I made it a list so it could be in the future,
+			 * even though I cannot think of why.
+			 */
+			if ((err = xdr_dec_list_start(dec)) != 0)
+				break;
+			while (xdr_dec_list_stop(dec) != 0) {
+				if((err = xdr_dec_string_ag(dec, &lg->lfbb, &lg->lfbb_len)) != 0) break;
+				if((err = xdr_dec_uint64(dec, &x_c_subid)) != 0 ) break;
+				if((err = xdr_dec_uint64(dec, &x_c_start)) != 0 ) break;
+				if((err = xdr_dec_uint64(dec, &x_c_stop)) != 0 ) break;
+				if((err = xdr_dec_uint8(dec, &x_c_state)) != 0) break;
+			}
+		} while (0);
+		if (err != 0) {
+			goto exit;
+		}
+		if (x_keylen <= 4) {
+			err = -EPROTO;	/* or something */
+			goto exit;
+		}
+		if (cbp->lock_query == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = cbp->lock_query (misc, &lg->lfba[4], x_keylen - 4,
+				       x_subid, x_start, x_stop, x_state,
+				       x_error, lg->lfbb, x_c_subid,
+				       x_c_start, x_c_stop, x_c_state);
+		goto exit;
+	} else if (gulm_lock_cb_state == x_code) {
+		do {
+			if ((err =
+			     xdr_dec_raw_ag (dec, (void **) &lg->lfba,
+					     &lg->lfba_len, &x_keylen)) != 0)
+				break;
+         		if ((err = xdr_dec_uint64(dec, &x_subid)) != 0 )
+				break;
+			if ((err = xdr_dec_uint8 (dec, &x_state)) != 0)
+				break;
+		} while (0);
+		if (err != 0) {
+			goto exit;
+		}
+		if (cbp->drop_lock_req == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err =
+		    cbp->drop_lock_req (misc, &lg->lfba[4], x_keylen - 4,
+					x_subid, x_state);
+		goto exit;
+	} else if (gulm_lock_cb_dropall == x_code) {
+		if (cbp->drop_all == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = cbp->drop_all (misc);
+		goto exit;
+	} else if (gulm_info_stats_rpl == x_code) {
+		do {
+			if ((err = xdr_dec_list_start (dec)) != 0)
+				break;
+			while (xdr_dec_list_stop (dec) != 0) {
+				if ((err =
+				     xdr_dec_string_ag (dec, &lg->lfba,
+							&lg->lfba_len)) != 0)
+					break;
+				if ((err =
+				     xdr_dec_string_ag (dec, &lg->lfbb,
+							&lg->lfbb_len)) != 0)
+					break;
+			}
+		} while (0);
+		goto exit;
+	} else if (gulm_err_reply == x_code) {
+		do {
+			if ((err = xdr_dec_uint32 (dec, &x_code)) != 0)
+				break;
+			if ((err = xdr_dec_uint32 (dec, &x_error)) != 0)
+				break;
+		} while (0);
+		if (err != 0)
+			goto exit;
+		if (cbp->error == NULL) {
+			err = 0;
+			goto exit;
+		}
+		err = cbp->error (misc, x_error);
+		goto exit;
+	} else {
+		err = -EPROTO;
+		goto exit;
+	}
+
+      exit:
+	lg->in_lock_hm = FALSE;
+	return err;
+}
+
+/**
+ * lg_lock_login - 
+ * @ulm_interface_p: 
+ * @4: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_lock_login (gulm_interface_p lgp, uint8_t lockspace[4])
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	struct sockaddr_in6 adr;
+	int err;
+	xdr_socket cfd;
+	xdr_enc_t *enc;
+	xdr_dec_t *dec;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	adr.sin6_family = AF_INET6;
+	adr.sin6_addr = lg_in6addr_loopback;
+	adr.sin6_port = htons (lg->lock_port);
+
+	if ((err = xdr_open (&cfd)) < 0) {
+		return err;
+	}
+
+	if ((err = xdr_connect (&adr, cfd)) < 0) {
+		xdr_close (&cfd);
+		return err;
+	}
+
+	enc = xdr_enc_init (cfd, 512);
+	if (enc == NULL) {
+		xdr_close (&cfd);
+		return -ENOMEM;
+	}
+
+	dec = xdr_dec_init (cfd, 512);
+	if (enc == NULL) {
+		xdr_enc_release (enc);
+		xdr_close (&cfd);
+		return -ENOMEM;
+	}
+
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_lock_login_req)) < 0)
+			break;
+		if ((err = xdr_enc_uint32 (enc, GIO_WIREPROT_VERS)) < 0)
+			break;
+		if ((err = xdr_enc_string (enc, lg->service_name)) < 0)
+			break;
+		if ((err = xdr_enc_uint8 (enc, gio_lck_st_Client)) < 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) < 0)
+			break;
+
+		if ((err = xdr_enc_uint32 (enc, gulm_lock_sel_lckspc)) < 0)
+			break;
+		if ((err = xdr_enc_raw (enc, lockspace, 4)) < 0)
+			break;
+		/* don't flush here.
+		 * dumb programmer stunt.  This way, the lockspace selection won't
+		 * happen until the next thing the user of this lib sends.  Which
+		 * means it will be after we have received the login reply.
+		 *
+		 * Is there really a good reason not to flush here?
+		 */
+	} while (0);
+	if (err != 0) {
+		xdr_dec_release (dec);
+		xdr_enc_release (enc);
+		xdr_close (&cfd);
+		return err;
+	}
+
+	down (&lg->lock_sender);
+	lg->lock_fd = cfd;
+	lg->lock_enc = enc;
+	lg->lock_dec = dec;
+
+	memcpy (lg->lockspace, lockspace, 4);
+	up (&lg->lock_sender);
+
+	return 0;
+}
+
+/**
+ * lg_lock_logout - 
+ * @ulm_interface_p: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_lock_logout (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->lock_fd < 0 || lg->lock_enc == NULL || lg->lock_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->lock_enc;
+
+	down (&lg->lock_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_lock_logout_req)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->lock_sender);
+	return err;
+}
+
+/**
+ * lg_lock_state_req - 
+ * @lgp: 
+ * @key: 
+ * @keylen: 
+ * @state: 
+ * @flags: 
+ * @LVB: 
+ * @LVBlen: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_lock_state_req (gulm_interface_p lgp, uint8_t * key, uint16_t keylen,
+		   uint64_t subid, uint64_t start, uint64_t stop,
+		   uint8_t state, uint32_t flags, uint8_t * LVB,
+		   uint16_t LVBlen)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	struct iovec iov[2];
+	xdr_enc_t *enc;
+	uint32_t iflgs = 0;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->lock_fd < 0 || lg->lock_enc == NULL || lg->lock_dec == NULL)
+		return -EINVAL;
+
+	if (state != lg_lock_state_Unlock &&
+	    state != lg_lock_state_Exclusive &&
+	    state != lg_lock_state_Deferred && state != lg_lock_state_Shared)
+		return -EINVAL;
+
+	/* make sure only the accepted flags get passed through. */
+	if ((flags & lg_lock_flag_DoCB) == lg_lock_flag_DoCB)
+		iflgs |= lg_lock_flag_DoCB;
+	if ((flags & lg_lock_flag_Try) == lg_lock_flag_Try)
+		iflgs |= lg_lock_flag_Try;
+	if ((flags & lg_lock_flag_Any) == lg_lock_flag_Any)
+		iflgs |= lg_lock_flag_Any;
+	if ((flags & lg_lock_flag_IgnoreExp) == lg_lock_flag_IgnoreExp)
+		iflgs |= lg_lock_flag_IgnoreExp;
+	if ((flags & lg_lock_flag_Piority) == lg_lock_flag_Piority)
+		iflgs |= lg_lock_flag_Piority;
+
+	enc = lg->lock_enc;
+
+	if (LVB != NULL && LVBlen > 0)
+		iflgs |= gio_lck_fg_hasLVB;
+
+	iov[0].iov_base = lg->lockspace;
+	iov[0].iov_len = 4;
+	iov[1].iov_base = key;
+	iov[1].iov_len = keylen;
+
+	down (&lg->lock_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_lock_state_req)) != 0)
+			break;
+		if ((err = xdr_enc_raw_iov (enc, 2, iov)) != 0)
+			break;
+		if ((err = xdr_enc_uint64 (enc, subid)) != 0)
+			break;
+		if ((err = xdr_enc_uint64 (enc, start)) != 0)
+			break;
+		if ((err = xdr_enc_uint64 (enc, stop)) != 0)
+			break;
+		if ((err = xdr_enc_uint8 (enc, state)) != 0)
+			break;
+		if ((err = xdr_enc_uint32 (enc, iflgs)) != 0)
+			break;
+		if (iflgs & gio_lck_fg_hasLVB)
+			if ((err = xdr_enc_raw (enc, LVB, LVBlen)) != 0)
+				break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->lock_sender);
+	return err;
+}
+
+/**
+ * lg_lock_cancel_req - 
+ * @lgp: 
+ * @key: 
+ * @keylen: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_lock_cancel_req (gulm_interface_p lgp, uint8_t * key, uint16_t keylen,
+		uint64_t subid)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	struct iovec iov[2];
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->lock_fd < 0 || lg->lock_enc == NULL || lg->lock_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->lock_enc;
+
+	iov[0].iov_base = lg->lockspace;
+	iov[0].iov_len = 4;
+	iov[1].iov_base = key;
+	iov[1].iov_len = keylen;
+
+	down (&lg->lock_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_lock_action_req)) != 0)
+			break;
+		if ((err = xdr_enc_raw_iov (enc, 2, iov)) != 0)
+			break;
+		if ((err = xdr_enc_uint64 (enc, subid)) != 0)
+			break;
+		if ((err = xdr_enc_uint8 (enc, gio_lck_st_Cancel)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->lock_sender);
+	return err;
+}
+
+/**
+ * lg_lock_action_req - 
+ * @lgp: 
+ * @key: 
+ * @keylen: 
+ * @action: 
+ * @LVB: 
+ * @LVBlen: 
+ * 
+ * XXX
+ * I wonder if I should actually break this into three seperate calls for
+ * the lvb stuff.  Does it really matter?
+ * 
+ * Returns: int
+ */
+int
+lg_lock_action_req (gulm_interface_p lgp, uint8_t * key, uint16_t keylen,
+		    uint64_t subid, uint8_t action, uint8_t * LVB,
+		    uint16_t LVBlen)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	struct iovec iov[2];
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->lock_fd < 0 || lg->lock_enc == NULL || lg->lock_dec == NULL)
+		return -EINVAL;
+
+	if (action != lg_lock_act_HoldLVB &&
+	    action != lg_lock_act_UnHoldLVB && action != lg_lock_act_SyncLVB)
+		return -EINVAL;
+
+	enc = lg->lock_enc;
+
+	iov[0].iov_base = lg->lockspace;
+	iov[0].iov_len = 4;
+	iov[1].iov_base = key;
+	iov[1].iov_len = keylen;
+
+	down (&lg->lock_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_lock_action_req)) != 0)
+			break;
+		if ((err = xdr_enc_raw_iov (enc, 2, iov)) != 0)
+			break;
+		if ((err = xdr_enc_uint64 (enc, subid)) != 0)
+			break;
+		if ((err = xdr_enc_uint8 (enc, action)) != 0)
+			break;
+		if (action == gio_lck_st_SyncLVB)
+			if ((err = xdr_enc_raw (enc, LVB, LVBlen)) != 0)
+				break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->lock_sender);
+	return err;
+}
+
+/**
+ * lg_lock_query_req - 
+ * @lgp: 
+ * @key: 
+ * @keylen: 
+ * @subid: 
+ * @start: 
+ * @stop: 
+ * @state: 
+ * 
+ * 
+ * Returns: int
+ */
+int lg_lock_query_req(gulm_interface_p lgp, uint8_t *key, uint16_t keylen,
+      uint64_t subid, uint64_t start, uint64_t stop, uint8_t state)
+{
+   gulm_interface_t *lg = (gulm_interface_t *)lgp;
+   struct iovec iov[2];
+   xdr_enc_t *enc;
+   int err;
+
+   /* make sure it is a gulm_interface_p. */
+   if( lg == NULL ) return -EINVAL;
+   if( lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC ) return -EINVAL;
+
+   if( lg->lock_fd < 0 || lg->lock_enc == NULL || lg->lock_dec == NULL)
+      return -EINVAL;
+
+   if( state != lg_lock_state_Unlock &&
+       state != lg_lock_state_Exclusive &&
+       state != lg_lock_state_Deferred &&
+       state != lg_lock_state_Shared )
+      return -EINVAL;
+
+   if( stop < start ) return -EINVAL;
+
+   enc = lg->lock_enc;
+
+   iov[0].iov_base = lg->lockspace;
+   iov[0].iov_len = 4;
+   iov[1].iov_base = key;
+   iov[1].iov_len = keylen;
+
+	down (&lg->lock_sender);
+   do{
+      if((err = xdr_enc_uint32(enc, gulm_lock_query_req)) != 0 ) break;
+      if((err = xdr_enc_raw_iov(enc, 2, iov)) != 0 ) break;
+      if((err = xdr_enc_uint64(enc, subid)) != 0) break;
+      if((err = xdr_enc_uint64(enc, start)) != 0) break;
+      if((err = xdr_enc_uint64(enc, stop)) != 0) break;
+      if((err = xdr_enc_uint8(enc, state)) != 0 ) break;
+      if((err = xdr_enc_flush(enc)) != 0 ) break;
+   }while(0);
+	up (&lg->lock_sender);
+   return err;
+}
+
+/**
+ * lg_lock_drop_exp - 
+ * @ulm_interface_p: 
+ * @holder: 
+ * @keymask: 
+ * @kmlen: 
+ * 
+ * holder is the node name of the expired holder that you want to clear.
+ * Only locks matching the keymask will be looked at. (most of the time you
+ * will just set key to a bunch of 0xff to match all) The keymask lets you
+ * basically subdivide your lockspace into smaller seperate parts.
+ * (example, there is one gfs lockspace, but each filesystem gets its own
+ * subpart of that larger space)
+ *
+ * If holder is NULL, all expired holders in your lockspace will get
+ * dropped.
+ * 
+ * Returns: int
+ */
+int
+lg_lock_drop_exp (gulm_interface_p lgp, uint8_t * holder, uint8_t * key,
+		  uint16_t keylen)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	struct iovec iov[2];
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->lock_fd < 0 || lg->lock_enc == NULL || lg->lock_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->lock_enc;
+
+	iov[0].iov_base = lg->lockspace;
+	iov[0].iov_len = 4;
+	iov[1].iov_base = key;
+	iov[1].iov_len = (key != NULL) ? keylen : 0;
+
+	down (&lg->lock_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_lock_drop_exp)) != 0)
+			break;
+		if ((err = xdr_enc_string (enc, holder)) != 0)
+			break;
+		if ((err = xdr_enc_raw_iov (enc, 2, iov)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->lock_sender);
+	return err;
+}
+
+int
+lg_lock_expire (gulm_interface_p lgp, uint8_t * holder, uint8_t * key,
+		  uint16_t keylen)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	struct iovec iov[2];
+	xdr_enc_t *enc;
+	int err;
+
+	/* make sure it is a gulm_interface_p. */
+	if (lg == NULL)
+		return -EINVAL;
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	if (lg->lock_fd < 0 || lg->lock_enc == NULL || lg->lock_dec == NULL)
+		return -EINVAL;
+
+	enc = lg->lock_enc;
+
+	iov[0].iov_base = lg->lockspace;
+	iov[0].iov_len = 4;
+	iov[1].iov_base = key;
+	iov[1].iov_len = (key != NULL) ? keylen : 0;
+
+	down (&lg->lock_sender);
+	do {
+		if ((err = xdr_enc_uint32 (enc, gulm_lock_expire)) != 0)
+			break;
+		if ((err = xdr_enc_string (enc, holder)) != 0)
+			break;
+		if ((err = xdr_enc_raw_iov (enc, 2, iov)) != 0)
+			break;
+		if ((err = xdr_enc_flush (enc)) != 0)
+			break;
+	} while (0);
+	up (&lg->lock_sender);
+	return err;
+}
+
+/* vim: set ai cin noet sw=8 ts=8 : */
+
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/lg_main.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/lg_main.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/lg_main.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/lg_main.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,211 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/* This is where all of the library specific functions exist.
+ * Not many, but keeps things clean.
+ */
+
+#include "lg_priv.h"
+#include "gulm.h"
+extern gulm_cm_t gulm_cm;
+
+const struct in6_addr lg_in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+
+/**
+ * lg_initialize - 
+ * @gulm_interface_p:
+ * @cluster_name:
+ * @service_name: 
+ * 
+ * if returning an error, nothing was done to the value of gulm_interface_p
+ * 
+ * Returns: gulm_interface_p
+ */
+int
+lg_initialize (gulm_interface_p * ret, char *cluster_name, char *service_name)
+{
+	gulm_interface_t *lg;
+	int err, len;
+
+	lg = kmalloc (sizeof (gulm_interface_t), GFP_KERNEL);
+	if (lg == NULL)
+		return -ENOMEM;
+
+	memset (lg, 0, sizeof (gulm_interface_t));
+	lg->first_magic = LGMAGIC;
+	lg->last_magic = LGMAGIC;
+
+	if (cluster_name == NULL)
+		cluster_name = "cluster";
+	len = strlen (cluster_name) + 1;
+	lg->clusterID = kmalloc (len, GFP_KERNEL);
+	if (lg->clusterID == NULL) {
+		err = -ENOMEM;
+		goto fail_nomem;
+	}
+	memcpy (lg->clusterID, cluster_name, len);
+
+	len = strlen (service_name) + 1;
+	lg->service_name = kmalloc (len, GFP_KERNEL);
+	if (lg->service_name == NULL) {
+		err = -ENOMEM;
+		goto fail_nomem;
+	}
+	memcpy (lg->service_name, service_name, len);
+
+	/* set up flutter bufs. */
+	lg->cfba_len = 64;
+	lg->cfba = kmalloc (lg->cfba_len, GFP_KERNEL);
+	if (lg->cfba == NULL) {
+		err = -ENOMEM;
+		goto fail_nomem;
+	}
+
+	lg->cfbb_len = 64;
+	lg->cfbb = kmalloc (lg->cfbb_len, GFP_KERNEL);
+	if (lg->cfbb == NULL) {
+		err = -ENOMEM;
+		goto fail_nomem;
+	}
+
+	lg->lfba_len = 128;
+	lg->lfba = kmalloc (lg->lfba_len, GFP_KERNEL);
+	if (lg->lfba == NULL) {
+		err = -ENOMEM;
+		goto fail_nomem;
+	}
+
+	lg->lfbb_len = 128;
+	lg->lfbb = kmalloc (lg->lfbb_len, GFP_KERNEL);
+	if (lg->lfbb == NULL) {
+		err = -ENOMEM;
+		goto fail_nomem;
+	}
+
+	/* setup mutexes */
+	init_MUTEX (&lg->core_sender);
+	init_MUTEX (&lg->core_recver);
+	init_MUTEX (&lg->lock_sender);
+	init_MUTEX (&lg->lock_recver);
+
+	lg->core_port = 40040;
+	lg->lock_port = 40042;
+
+	*ret = lg;
+	return 0;
+      fail_nomem:
+	if (lg->clusterID != NULL)
+		kfree (lg->clusterID);
+	if (lg->service_name != NULL)
+		kfree (lg->service_name);
+	if (lg->cfba != NULL)
+		kfree (lg->cfba);
+	if (lg->cfbb != NULL)
+		kfree (lg->cfbb);
+	if (lg->lfba != NULL)
+		kfree (lg->lfba);
+	if (lg->lfbb != NULL)
+		kfree (lg->lfbb);
+	kfree (lg);
+	return err;
+}
+
+/**
+ * lg_release - 
+ * @lg: 
+ * 
+ */
+void
+lg_release (gulm_interface_p lgp)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	if (lgp == NULL)
+		return;
+	/* make sure it is a gulm_interface_p. */
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return;
+
+	if (lg->service_name != NULL)
+		kfree (lg->service_name);
+	if (lg->clusterID != NULL)
+		kfree (lg->clusterID);
+
+	/* wonder if I should send a logout packet? */
+	if (lg->core_enc != NULL)
+		xdr_enc_release (lg->core_enc);
+	if (lg->core_dec != NULL)
+		xdr_dec_release (lg->core_dec);
+	xdr_close (&lg->core_fd);
+
+	if (lg->lock_enc != NULL)
+		xdr_enc_release (lg->lock_enc);
+	if (lg->lock_dec != NULL)
+		xdr_dec_release (lg->lock_dec);
+	xdr_close (&lg->lock_fd);
+
+	if (lg->cfba != NULL)
+		kfree (lg->cfba);
+	if (lg->cfbb != NULL)
+		kfree (lg->cfbb);
+	if (lg->lfba != NULL)
+		kfree (lg->lfba);
+	if (lg->lfbb != NULL)
+		kfree (lg->lfbb);
+
+	kfree (lg);
+}
+
+/**
+ * lg_set_core_port - 
+ * @lgp: 
+ * @new: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_set_core_port (gulm_interface_p lgp, uint16_t new)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	if (lgp == NULL)
+		return -EINVAL;
+	/* make sure it is a gulm_interface_p. */
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	lg->core_port = new;
+	return 0;
+}
+
+/**
+ * lg_set_ltpx_port - 
+ * @lgp: 
+ * @new: 
+ * 
+ * 
+ * Returns: int
+ */
+int
+lg_set_lock_port (gulm_interface_p lgp, uint16_t new)
+{
+	gulm_interface_t *lg = (gulm_interface_t *) lgp;
+	if (lgp == NULL)
+		return -EINVAL;
+	/* make sure it is a gulm_interface_p. */
+	if (lg->first_magic != LGMAGIC || lg->last_magic != LGMAGIC)
+		return -EINVAL;
+
+	lg->lock_port = new;
+
+	return 0;
+}
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/lg_priv.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/lg_priv.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/lg_priv.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/lg_priv.h	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,88 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __lg_priv_h__
+#define __lg_priv_h__
+/* private details that we don't want to give the users of this lib access
+ * to go here.
+ */
+
+#ifdef __linux__
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#endif /*__linux__*/
+
+#include "xdr.h"
+#include "gio_wiretypes.h"
+#include "libgulm.h"
+
+#define LGMAGIC (0x474d4354)
+
+struct gulm_interface_s {
+	/* since we've masked this to a void* to the users, it is a nice safty
+	 * net to put a little magic in here so we know things stay good.
+	 */
+	uint32_t first_magic;
+
+	/* WHAT IS YOUR NAME?!? */
+	char *service_name;
+
+	char *clusterID;
+
+	uint16_t core_port;
+	xdr_socket core_fd;
+	xdr_enc_t *core_enc;
+	xdr_dec_t *core_dec;
+	struct semaphore core_sender;
+	struct semaphore core_recver;
+	int in_core_hm;
+
+	uint16_t lock_port;
+	xdr_socket lock_fd;
+	xdr_enc_t *lock_enc;
+	xdr_dec_t *lock_dec;
+	struct semaphore lock_sender;
+	struct semaphore lock_recver;
+	int in_lock_hm;
+	uint8_t lockspace[4];
+
+	/* in the message recver func, we read data into these buffers and pass
+	 * them to the callback function.  This way we avoid doinf mallocs and
+	 * frees on every callback.
+	 */
+	uint16_t cfba_len;
+	uint8_t *cfba;
+	uint16_t cfbb_len;
+	uint8_t *cfbb;
+	uint16_t lfba_len;
+	uint8_t *lfba;
+	uint16_t lfbb_len;
+	uint8_t *lfbb;
+
+	uint32_t last_magic;
+};
+typedef struct gulm_interface_s gulm_interface_t;
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+extern const struct in6_addr lg_in6addr_loopback;
+
+#endif /*__lg_priv_h__*/
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/libgulm.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/libgulm.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/libgulm.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/libgulm.h	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,199 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __libgulm_h__
+#define __libgulm_h__
+
+/* bit messy, but we need this to be rather seemless in both kernel and
+ * userspace. and this seems the easiest way to do it.
+ */
+
+#ifdef __linux__
+#include <linux/in6.h>
+typedef struct socket *lg_socket;
+#endif /*__linux__*/
+
+typedef void *gulm_interface_p;
+
+/* mallocs the interface structure.
+ */
+int lg_initialize (gulm_interface_p *, char *cluster_name, char *service_name);
+/* frees struct.
+ */
+void lg_release (gulm_interface_p);
+
+/* Determins where we are with a itemlist callback */
+typedef enum { lglcb_start, lglcb_item, lglcb_stop } lglcb_t;
+
+/****** Core specifics ******/
+
+/* leaving a callback pointer as NULL, will cause that message type to 
+ * be ignored. */
+typedef struct lg_core_callbacks_s {
+	int (*login_reply) (void *misc, uint64_t gen, uint32_t error,
+			    uint32_t rank, uint8_t corestate);
+	int (*logout_reply) (void *misc);
+	int (*nodelist) (void *misc, lglcb_t type, char *name,
+			 struct in6_addr * ip, uint8_t state);
+	int (*statechange) (void *misc, uint8_t corestate, uint8_t quorate,
+			    struct in6_addr * masterip, char *mastername);
+	int (*nodechange) (void *misc, char *nodename,
+			   struct in6_addr * nodeip, uint8_t nodestate);
+	int (*service_list) (void *misc, lglcb_t type, char *service);
+	int (*error) (void *misc, uint32_t err);
+} lg_core_callbacks_t;
+
+/* this will trigger a callback from gulm_core_callbacks_t 
+ * handles one message! Either stick this inside of a thread,
+ * or in a poll()/select() loop using the function below.
+ * This will block until there is a message sent from core. 
+ */
+int lg_core_handle_messages (gulm_interface_p, lg_core_callbacks_t *,
+			     void *misc);
+
+/* this returns the filedescriptor that the library is using to 
+ * communicate with the core. This is only for using in a poll() 
+ * or select() call to avoid having the gulm_core_handle_messages()
+ * call block. 
+ */
+lg_socket lg_core_selector (gulm_interface_p);
+
+/* Queue requests. */
+int lg_core_login (gulm_interface_p, int important);
+int lg_core_logout (gulm_interface_p);
+int lg_core_nodeinfo (gulm_interface_p, char *nodename);
+int lg_core_nodelist (gulm_interface_p);
+int lg_core_servicelist (gulm_interface_p);
+int lg_core_corestate (gulm_interface_p);
+
+/* for completeness mostly. */
+int lg_core_shutdown (gulm_interface_p);
+int lg_core_forceexpire (gulm_interface_p, char *node_name);
+int lg_core_forcepending (gulm_interface_p);
+
+/* Node states
+ * First three are actual states, as well as changes.  Last is only a node
+ * change message.
+ * */
+#define lg_core_Logged_in  (0x05)
+#define lg_core_Logged_out (0x06)
+#define lg_core_Expired    (0x07)
+#define lg_core_Fenced     (0x08)
+/* Core states */
+#define lg_core_Slave       (0x01)
+#define lg_core_Master      (0x02)
+#define lg_core_Pending     (0x03)
+#define lg_core_Arbitrating (0x04)
+#define lg_core_Client      (0x06)
+
+/****** lock space specifics *****/
+/* note that this library masks out the lock table seperation. 
+ */
+
+typedef struct lg_lockspace_callbacks_s {
+	int (*login_reply) (void *misc, uint32_t error, uint8_t which);
+	int (*logout_reply) (void *misc);
+	int (*lock_state) (void *misc, uint8_t * key, uint16_t keylen,
+			   uint64_t subid, uint64_t start, uint64_t stop,
+			   uint8_t state, uint32_t flags, uint32_t error,
+			   uint8_t * LVB, uint16_t LVBlen);
+	int (*lock_action) (void *misc, uint8_t * key, uint16_t keylen,
+			    uint64_t subid, uint8_t action, uint32_t error);
+	int (*drop_lock_req) (void *misc, uint8_t * key, uint16_t keylen,
+			      uint64_t subid, uint8_t state);
+	int (*lock_query) (void *misc, uint8_t * key, uint16_t keylen,
+			   uint64_t subid, uint64_t start, uint64_t stop,
+			   uint8_t state, uint32_t error, uint8_t * cnode,
+			   uint64_t csubid, uint64_t cstart, uint64_t cstop,
+			   uint8_t cstate);
+	int (*drop_all) (void *misc);
+	int (*error) (void *misc, uint32_t err);
+} lg_lockspace_callbacks_t;
+
+/* Like the core handle messages function, but for the lockspace.
+ * Handles one message, blocks.
+ */
+
+int lg_lock_handle_messages (gulm_interface_p, lg_lockspace_callbacks_t *,
+			     void *misc);
+
+/* this returns the filedescriptor that the library is using to 
+ * communicate with the ltpx. This is only for using in a poll() 
+ * or select() call to avoid having the gulm_lock_handle_messages()
+ * call block. 
+ */
+lg_socket lg_lock_selector (gulm_interface_p);
+
+/* Lockspace request calls */
+int lg_lock_login (gulm_interface_p, uint8_t lockspace[4]);
+int lg_lock_logout (gulm_interface_p);
+int lg_lock_state_req (gulm_interface_p, uint8_t * key, uint16_t keylen,
+                       uint64_t subid, uint64_t start, uint64_t stop,
+		       uint8_t state, uint32_t flags, uint8_t * LVB,
+		       uint16_t LVBlen);
+int lg_lock_cancel_req (gulm_interface_p, uint8_t * key, uint16_t keylen,
+			uint64_t subid);
+int lg_lock_action_req (gulm_interface_p, uint8_t * key, uint16_t keylen,
+			uint64_t subid, uint8_t action,
+			uint8_t * LVB, uint16_t LVBlen);
+int lg_lock_query_req(gulm_interface_p lgp, uint8_t *key, uint16_t keylen,
+      uint64_t subid, uint64_t start, uint64_t stop, uint8_t state);
+int lg_lock_drop_exp (gulm_interface_p, uint8_t * holder,
+		      uint8_t * keymask, uint16_t kmlen);
+int lg_lock_expire (gulm_interface_p lgp, uint8_t * holder, uint8_t * key,
+		  uint16_t keylen);
+
+/* state requests */
+#define lg_lock_state_Unlock    (0x00)
+#define lg_lock_state_Exclusive (0x01)
+#define lg_lock_state_Deferred  (0x02)
+#define lg_lock_state_Shared    (0x03)
+
+/* actions */
+#define lg_lock_act_HoldLVB     (0x0b)
+#define lg_lock_act_UnHoldLVB   (0x0c)
+#define lg_lock_act_SyncLVB     (0x0d)
+
+/* flags */
+#define lg_lock_flag_DoCB        (0x00000001)
+#define lg_lock_flag_Try         (0x00000002)
+#define lg_lock_flag_Any         (0x00000004)
+#define lg_lock_flag_IgnoreExp   (0x00000008)
+#define lg_lock_flag_Cachable    (0x00000020)
+#define lg_lock_flag_Piority     (0x00000040)
+#define lg_lock_flag_NoCallBacks (0x00000100)
+
+/* These are the possible values that can be in the error fields. */
+#define lg_err_Ok              (0)
+#define lg_err_BadLogin        (1001)
+#define lg_err_BadCluster      (1003)
+#define lg_err_BadConfig       (1004)
+#define lg_err_BadGeneration   (1005)
+#define lg_err_BadWireProto    (1019)
+
+#define lg_err_NotAllowed      (1006)
+#define lg_err_Unknown_Cs      (1007)
+#define lg_err_BadStateChg     (1008)
+#define lg_err_MemoryIssues    (1009)
+
+#define lg_err_TryFailed       (1011)
+#define lg_err_AlreadyPend     (1013)
+#define lg_err_Canceled        (1015)
+
+#define lg_err_NoSuchFS        (1016)
+#define lg_err_NoSuchJID       (1017)
+#define lg_err_NoSuchName      (1018)
+
+#endif /*__libgulm_h__*/
+
+/* vim: set ai cin noet sw=8 ts=8 : */
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/utils_tostr.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/utils_tostr.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/utils_tostr.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/utils_tostr.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,66 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include "gio_wiretypes.h"
+
+char *
+gio_Err_to_str (int x)
+{
+	char *t = "Unknown GULM Err";
+	switch (x) {
+	case gio_Err_Ok:
+		t = "Ok";
+		break;
+
+	case gio_Err_BadLogin:
+		t = "Bad Login";
+		break;
+	case gio_Err_BadCluster:
+		t = "Bad Cluster ID";
+		break;
+	case gio_Err_BadConfig:
+		t = "Incompatible configurations";
+		break;
+	case gio_Err_BadGeneration:
+		t = "Bad Generation ID";
+		break;
+	case gio_Err_BadWireProto:
+		t = "Bad Wire Protocol Version";
+		break;
+
+	case gio_Err_NotAllowed:
+		t = "Not Allowed";
+		break;
+	case gio_Err_Unknown_Cs:
+		t = "Uknown Client";
+		break;
+	case gio_Err_BadStateChg:
+		t = "Bad State Change";
+		break;
+	case gio_Err_MemoryIssues:
+		t = "Memory Problems";
+		break;
+
+	case gio_Err_TryFailed:
+		t = "Try Failed";
+		break;
+	case gio_Err_AlreadyPend:
+		t = "Request Already Pending";
+		break;
+	case gio_Err_Canceled:
+		t = "Request Canceled";
+		break;
+	}
+	return t;
+}
+
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/utils_tostr.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/utils_tostr.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/utils_tostr.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/utils_tostr.h	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,17 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __utils_tostr_h__
+#define __utils_tostr_h__
+char *gio_Err_to_str (int x);
+#endif /*__utils_tostr_h__*/
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/xdr.h linux-2.6.9.debug/fs/gfs_locking/lock_gulm/xdr.h
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/xdr.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/xdr.h	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,98 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __gulm_xdr_h__
+#define __gulm_xdr_h__
+typedef struct xdr_enc_s xdr_enc_t;
+typedef struct xdr_dec_s xdr_dec_t;
+
+/* sockets in kernel space are done a bit different than socket in
+ * userspace.  But we need to have them appear to be the same.
+ */
+#ifdef __KERNEL__
+
+#ifdef __linux__
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/socket.h>
+#include <net/sock.h>
+
+typedef struct socket *xdr_socket;
+#endif /*__linux__*/
+#else /*__KERNEL__*/
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#include <errno.h>
+typedef int xdr_socket;
+#endif /*__KERNEL__*/
+
+/* start things up */
+int xdr_open (xdr_socket * sk);
+int xdr_connect (struct sockaddr_in6 *adr, xdr_socket sk);
+void xdr_close (xdr_socket * sk);
+
+/* deep, basic io */
+#ifdef __KERNEL__
+#ifdef __linux__
+size_t xdr_send (struct socket *sock, void *buf, size_t size);
+size_t xdr_recv (struct socket *sock, void *buf, size_t size);
+#endif /*__linux__*/
+#else /*__KERNEL__*/
+ssize_t xdr_recv (int fd, void *buf, size_t len);
+ssize_t xdr_send (int fd, void *buf, size_t len);
+#endif /*__KERNEL__*/
+
+xdr_enc_t *xdr_enc_init (xdr_socket sk, int buffer_size);
+xdr_dec_t *xdr_dec_init (xdr_socket sk, int buffer_size);
+int xdr_enc_flush (xdr_enc_t * xdr);
+int xdr_enc_release (xdr_enc_t * xdr);	/* calls xdr_enc_flush() */
+void xdr_enc_force_release (xdr_enc_t * xdr);	/* doesn't call xdr_enc_flush() */
+void xdr_dec_release (xdr_dec_t * xdr);
+/* xdr_enc_force_release() is for when you get and error sending and you
+ * want to free that stuff up right away.  If you use the regular release
+ * for enc, it will fail if it cannot send data over the filedesciptor.
+ */
+
+/* encoders add to a stream */
+int xdr_enc_uint64 (xdr_enc_t * xdr, uint64_t i);
+int xdr_enc_uint32 (xdr_enc_t * xdr, uint32_t i);
+int xdr_enc_uint16 (xdr_enc_t * xdr, uint16_t i);
+int xdr_enc_uint8 (xdr_enc_t * xdr, uint8_t i);
+int xdr_enc_ipv6 (xdr_enc_t * enc, struct in6_addr *ip);
+int xdr_enc_raw (xdr_enc_t * xdr, void *pointer, uint16_t len);
+int xdr_enc_raw_iov (xdr_enc_t * xdr, int count, struct iovec *iov);
+int xdr_enc_string (xdr_enc_t * xdr, uint8_t * s);
+int xdr_enc_list_start (xdr_enc_t * xdr);
+int xdr_enc_list_stop (xdr_enc_t * xdr);
+
+/* decoders remove from stream */
+int xdr_dec_uint64 (xdr_dec_t * xdr, uint64_t * i);
+int xdr_dec_uint32 (xdr_dec_t * xdr, uint32_t * i);
+int xdr_dec_uint16 (xdr_dec_t * xdr, uint16_t * i);
+int xdr_dec_uint8 (xdr_dec_t * xdr, uint8_t * i);
+int xdr_dec_ipv6 (xdr_dec_t * xdr, struct in6_addr *ip);
+int xdr_dec_raw (xdr_dec_t * xdr, void *p, uint16_t * l);	/* no malloc */
+int xdr_dec_raw_m (xdr_dec_t * xdr, void **p, uint16_t * l);	/* mallocs p */
+int xdr_dec_raw_ag (xdr_dec_t * xdr, void **p, uint16_t * bl, uint16_t * rl);
+int xdr_dec_string (xdr_dec_t * xdr, uint8_t ** strp);	/* mallocs s */
+int xdr_dec_string_nm (xdr_dec_t * xdr, uint8_t * strp, size_t l);	/* no malloc */
+int xdr_dec_string_ag (xdr_dec_t * xdr, uint8_t ** s, uint16_t * bl);
+int xdr_dec_list_start (xdr_dec_t * xdr);
+int xdr_dec_list_stop (xdr_dec_t * xdr);
+
+#endif /*__gulm_xdr_h__*/
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/xdr_base.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/xdr_base.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/xdr_base.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/xdr_base.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,907 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+ * This is a bit of an abstraction layer to get this working in both kernel
+ * and userspace.
+ */
+#define TRUE  (1)
+#define FALSE (0)
+#define MIN(a,b) ((a<b)?a:b)
+
+#ifdef __linux__
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#endif /*__linux__*/
+
+#include "xdr.h"
+
+/**
+ * xdr_realloc - a realloc for kernel space.
+ * @a: < pointer to realloc
+ * @nl: < desired new size
+ * @ol: < current old size
+ * 
+ * Not as good as the real realloc, since it always moves memory.  But good
+ * enough for as little as it will get used here.
+ *
+ * XXX this is broken.
+ * 
+ * Returns: void*
+ */
+static void *
+xdr_realloc (void *a, size_t nl, size_t ol)
+{
+	if (nl == ol) {
+		return a;
+	} else if (nl == 0) {
+		kfree (a);
+		return NULL;
+	} else if (a == NULL && nl > 0) {
+		return kmalloc (nl, GFP_KERNEL);
+	} else {
+		void *tmp;
+		tmp = kmalloc (nl, GFP_KERNEL);
+		if (tmp == NULL)
+			return NULL;
+		memcpy (tmp, a, MIN (nl, ol));
+		kfree (a);
+		return tmp;
+	}
+}
+
+typedef enum { xdr_enc, xdr_dec } xdr_type;
+
+/* encoders have this sorta non-blocking, growing buffering stunt.
+ * makes them a bit different from the decoders now.
+ */
+struct xdr_enc_s {
+	size_t default_buf_size;
+	xdr_socket fd;
+	xdr_type type;
+	size_t length;
+	size_t curloc;
+	uint8_t *stream;
+};
+
+/* decoders only pull a single item off of the socket at a time.
+ * so this is all they need.
+ */
+struct xdr_dec_s {
+	size_t length;		/* total byte length of the stream */
+	size_t curloc;		/* current byte offset from start */
+	uint8_t *stream;	/* start of the encoded stream. */
+	xdr_socket fd;
+	xdr_type type;
+};
+
+/* the types of data we support. */
+
+#define XDR_NULL          0x00	/* NOT A VALID TAG!!! used in dec code. */
+#define XDR_LIST_START    0x01
+#define XDR_LIST_STOP     0x02
+/* list is a variable length device.  It is a start tag, some number of
+ * xdr_enc_*, then an stop tag.  It's main purpose is to provide a method
+ * of encasing data.
+ * */
+#define XDR_STRING        0x04
+/* string tag is followed by a uint16 which is the byte length */
+#define XDR_RAW           0x05
+/* raw tag is followed by a uint16 which is the byte length
+ * if 65535 bytes isn't enough, split your data and put multiples of these
+ * back to back.  (idea of xdr is to avoid this twit.)
+ * */
+
+/* note, if the size of these should variate, I'm screwed.  Should consider
+ * changing this all to the bit shift and array access to be more concrete.
+ * later.
+ */
+#define XDR_UINT64        0x06
+#define XDR_UINT32        0x07
+#define XDR_UINT16        0x08
+#define XDR_UINT8         0x09
+/* should add signed ints */
+
+#define XDR_IPv6          0x0a	/* 16 bytes, IPv6 address */
+
+/* any other base types?
+ */
+
+#define XDR_DEFAULT_BUFFER_SIZE 4096
+/*****************************************************************************/
+
+/**
+ * xdr_enc_init - 
+ * @fd: 
+ * @buffer_size: 
+ * 
+ * 
+ * Returns: xdr_enc_t*
+ */
+xdr_enc_t *
+xdr_enc_init (xdr_socket fd, int buffer_size)
+{
+	xdr_enc_t *xdr;
+
+	if (buffer_size <= 0)
+		buffer_size = XDR_DEFAULT_BUFFER_SIZE;
+
+	xdr = kmalloc (sizeof (xdr_enc_t), GFP_KERNEL);
+	if (xdr == NULL)
+		return NULL;
+	xdr->stream = kmalloc (buffer_size, GFP_KERNEL);
+	if (xdr->stream == NULL) {
+		kfree (xdr);
+		return NULL;
+	}
+	xdr->fd = fd;
+	xdr->type = xdr_enc;
+	xdr->default_buf_size = buffer_size;
+	xdr->length = buffer_size;
+	xdr->curloc = 0;
+
+	return xdr;
+}
+
+/**
+ * xdr_dec_init - 
+ * @fd: 
+ * @buffer_size: 
+ * 
+ * 
+ * Returns: xdr_dec_t*
+ */
+xdr_dec_t *
+xdr_dec_init (xdr_socket fd, int buffer_size)
+{
+	xdr_dec_t *xdr;
+
+	if (buffer_size <= 0)
+		buffer_size = XDR_DEFAULT_BUFFER_SIZE;
+
+	xdr = kmalloc (sizeof (xdr_dec_t), GFP_KERNEL);
+	if (xdr == NULL)
+		return NULL;
+	xdr->length = buffer_size;
+	xdr->curloc = 0;
+	xdr->stream = kmalloc (buffer_size, GFP_KERNEL);
+	xdr->fd = fd;
+	xdr->type = xdr_dec;
+	if (xdr->stream == NULL) {
+		kfree (xdr);
+		return NULL;
+	}
+	*(xdr->stream) = XDR_NULL;	/* so the first dec_call will call get_next */
+	return xdr;
+}
+
+/*****************************************************************************/
+/**
+ * xdr_enc_flush - 
+ * @xdr: 
+ * 
+ * Returns: int
+ */
+int
+xdr_enc_flush (xdr_enc_t * xdr)
+{
+	int err;
+	if (xdr == NULL)
+		return -EINVAL;
+	if (xdr->type != xdr_enc)
+		return -EINVAL;
+	if (xdr->curloc == 0)
+		return 0;
+
+	err = xdr_send (xdr->fd, xdr->stream, xdr->curloc);
+	if (err < 0)
+		return err;
+	if (err == 0)
+		return -EPROTO;	/* why? */
+	xdr->curloc = 0;
+
+	return 0;
+}
+
+/**
+ * xdr_release - 
+ * @xdr: 
+ *
+ * Free the memory, losing whatever may be there.
+ */
+void
+xdr_dec_release (xdr_dec_t * xdr)
+{
+	if (xdr == NULL)
+		return;
+	kfree (xdr->stream);
+	kfree (xdr);
+}
+
+/**
+ * xdr_enc_force_release - 
+ * @xdr: 
+ * 
+ * Free the memory, losing whatever may be there.
+ */
+void
+xdr_enc_force_release (xdr_enc_t * xdr)
+{
+	if (xdr == NULL)
+		return;
+	if (xdr->stream != NULL)
+		kfree (xdr->stream);
+	kfree (xdr);
+}
+
+/**
+ * xdr_enc_release - 
+ * @xdr: 
+ * 
+ * Free things up, trying to send any possible leftover data first.
+ * 
+ * Returns: int
+ */
+int
+xdr_enc_release (xdr_enc_t * xdr)
+{
+	int e;
+	if (xdr == NULL)
+		return -EINVAL;
+	if ((e = xdr_enc_flush (xdr)) != 0)
+		return e;
+	xdr_enc_force_release (xdr);
+	return 0;
+}
+
+/*****************************************************************************/
+/**
+ * grow_stream - 
+ * @xdr: 
+ * @len: 
+ * 
+ * each single encoded call needs to fit within a buffer.  So we make sure
+ * the buffer is big enough.
+ *
+ * If the buffer is big enough, but just doesn't have room, we send the
+ * data in the buffer, emptying it, first.
+ * 
+ * Returns: int
+ */
+static int
+grow_stream (xdr_enc_t * enc, size_t len)
+{
+	int err;
+	uint8_t *c;
+
+	/* buffer must be big enough for one type entry. */
+	if (len > enc->length) {
+		c = xdr_realloc (enc->stream, len, enc->length);
+		if (c == NULL)
+			return -ENOMEM;
+		enc->stream = c;
+		enc->length = len;
+	}
+
+	/* if there isn't room on the end of this chunk,
+	 * try sending what we've got.
+	 */
+	if (enc->curloc + len > enc->length) {
+		err = xdr_enc_flush (enc);
+		if (err != 0) {
+			/* error, better pass this up. */
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * append_bytes - 
+ * @xdr: 
+ * @xdr_type: 
+ * @bytes: 
+ * @len: 
+ * 
+ * 
+ * Returns: int
+ */
+static int
+append_bytes (xdr_enc_t * xdr, uint8_t xdr_type, void *bytes, size_t len)
+{
+	int e;
+	if (xdr == NULL)
+		return -EINVAL;
+	if (xdr->type != xdr_enc)
+		return -EINVAL;
+
+	/* len + 1; need the one byte for the type code. */
+	if ((e = grow_stream (xdr, len + 1)) != 0)
+		return e;
+	*(xdr->stream + xdr->curloc) = xdr_type;
+	xdr->curloc += 1;
+	memcpy ((xdr->stream + xdr->curloc), bytes, len);
+	xdr->curloc += len;
+
+	return 0;
+}
+
+int
+xdr_enc_uint64 (xdr_enc_t * xdr, uint64_t i)
+{
+	uint64_t b = cpu_to_be64 (i);
+	return append_bytes (xdr, XDR_UINT64, &b, sizeof (uint64_t));
+}
+
+int
+xdr_enc_uint32 (xdr_enc_t * xdr, uint32_t i)
+{
+	uint32_t b = cpu_to_be32 (i);
+	return append_bytes (xdr, XDR_UINT32, &b, sizeof (uint32_t));
+}
+
+int
+xdr_enc_uint16 (xdr_enc_t * xdr, uint16_t i)
+{
+	uint16_t b = cpu_to_be16 (i);
+	return append_bytes (xdr, XDR_UINT16, &b, sizeof (uint16_t));
+}
+
+int
+xdr_enc_uint8 (xdr_enc_t * xdr, uint8_t i)
+{
+	return append_bytes (xdr, XDR_UINT8, &i, sizeof (uint8_t));
+}
+
+int
+xdr_enc_ipv6 (xdr_enc_t * xdr, struct in6_addr *ip)
+{				/* bytes should already be in the right order. */
+	return append_bytes (xdr, XDR_IPv6, ip->s6_addr, 16);
+}
+
+int
+xdr_enc_raw (xdr_enc_t * xdr, void *p, uint16_t len)
+{
+	int e;
+	uint16_t temp;
+	if (xdr == NULL)
+		return -EINVAL;
+	if ((e = grow_stream (xdr, len + 3)) != 0)
+		return e;
+	*(xdr->stream + xdr->curloc) = XDR_RAW;
+	xdr->curloc += 1;
+	temp = cpu_to_be16(len);
+	memcpy((xdr->stream + xdr->curloc), &temp, 2);
+	xdr->curloc += 2;
+	memcpy ((xdr->stream + xdr->curloc), p, len);
+	xdr->curloc += len;
+	return 0;
+}
+
+int
+xdr_enc_raw_iov (xdr_enc_t * xdr, int count, struct iovec *iov)
+{
+	size_t total = 0;
+	int i, err;
+	uint16_t temp;
+	if (xdr == NULL || count < 1 || iov == NULL)
+		return -EINVAL;
+	for (i = 0; i < count; i++)
+		total += iov[i].iov_len;
+	/* make sure it fits in a uint16_t */
+	if (total > 0xffff)
+		return -EFBIG;
+	/* grow to fit */
+	if ((err = grow_stream (xdr, total + 3)) != 0)
+		return err;
+	/* copy in header and size */
+	*(xdr->stream + xdr->curloc) = XDR_RAW;
+	xdr->curloc += 1;
+	temp = cpu_to_be16(total);
+	memcpy((xdr->stream + xdr->curloc), &temp, 2);
+	xdr->curloc += 2;
+	/* copy in all iovbufs */
+	for (i = 0; i < count; i++) {
+		if (iov[i].iov_base == NULL)
+			continue;
+		memcpy ((xdr->stream + xdr->curloc), iov[i].iov_base,
+			iov[i].iov_len);
+		xdr->curloc += iov[i].iov_len;
+	}
+	return 0;
+}
+
+int
+xdr_enc_string (xdr_enc_t * xdr, uint8_t * s)
+{
+	int len, e;
+	uint16_t temp;
+	if (xdr == NULL)
+		return -EINVAL;
+	if (s == NULL)
+		len = 0;
+	else
+		len = strlen (s);
+	if ((e = grow_stream (xdr, len + 3)) != 0)
+		return e;
+	*(xdr->stream + xdr->curloc) = XDR_STRING;
+	xdr->curloc += 1;
+	temp = cpu_to_be16(len);
+	memcpy((xdr->stream + xdr->curloc), &temp, 2);
+	xdr->curloc += 2;
+	if (len > 0) {
+		memcpy ((xdr->stream + xdr->curloc), s, len);
+		xdr->curloc += len;
+	}
+	return 0;
+}
+
+int
+xdr_enc_list_start (xdr_enc_t * xdr)
+{
+	int e;
+	if (xdr == NULL)
+		return -EINVAL;
+	if ((e = grow_stream (xdr, 1)) != 0)
+		return e;
+	*(xdr->stream + xdr->curloc) = XDR_LIST_START;
+	xdr->curloc += 1;
+	return 0;
+}
+
+int
+xdr_enc_list_stop (xdr_enc_t * xdr)
+{
+	int e;
+	if (xdr == NULL)
+		return -EINVAL;
+	if ((e = grow_stream (xdr, 1)) != 0)
+		return e;
+	*(xdr->stream + xdr->curloc) = XDR_LIST_STOP;
+	xdr->curloc += 1;
+	return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ * get_next - 
+ * @xdr: 
+ * 
+ * get what ever may be next, and put it into the buffer.
+ * 
+ * Returns: int
+ */
+static int
+get_next (xdr_dec_t * xdr)
+{
+	int err;
+	uint16_t len;
+	if ((err = xdr_recv (xdr->fd, xdr->stream, 1)) < 0)
+		return err;
+	if (err == 0)
+		return -EPROTO;
+	xdr->curloc = 1;
+	if (*(xdr->stream) == XDR_UINT64) {
+		len = sizeof (uint64_t);
+	} else if (*(xdr->stream) == XDR_UINT32) {
+		len = sizeof (uint32_t);
+	} else if (*(xdr->stream) == XDR_UINT16) {
+		len = sizeof (uint16_t);
+	} else if (*(xdr->stream) == XDR_UINT8) {
+		len = sizeof (uint8_t);
+	} else if (*(xdr->stream) == XDR_IPv6) {
+		len = 16;
+	} else if (*(xdr->stream) == XDR_STRING) {
+		if ((err = xdr_recv (xdr->fd, (xdr->stream + 1), 2)) < 0)
+			return err;
+		if (err == 0)
+			return -EPROTO;
+		len = be16_to_cpu (*((uint16_t *) (xdr->stream + xdr->curloc)));
+		xdr->curloc += 2;
+	} else if (*(xdr->stream) == XDR_RAW) {
+		if ((err = xdr_recv (xdr->fd, (xdr->stream + 1), 2)) < 0)
+			return err;
+		if (err == 0)
+			return -EPROTO;
+		len = be16_to_cpu (*((uint16_t *) (xdr->stream + xdr->curloc)));
+		xdr->curloc += 2;
+	} else if (*(xdr->stream) == XDR_LIST_START) {
+		xdr->curloc = 0;
+		return 0;
+	} else if (*(xdr->stream) == XDR_LIST_STOP) {
+		xdr->curloc = 0;
+		return 0;
+	} else {
+		return -1;
+	}
+
+	/* grow buffer if need be. */
+	if (xdr->curloc + len > xdr->length) {
+		uint8_t *c;
+		c = xdr_realloc (xdr->stream, xdr->curloc + len, xdr->length);
+		if (c == NULL)
+			return -ENOMEM;
+		xdr->stream = c;
+		xdr->length = xdr->curloc + len;
+	}
+
+	if (len > 0) {
+		if ((err =
+		     xdr_recv (xdr->fd, (xdr->stream + xdr->curloc), len)) < 0)
+			return err;
+		if (err == 0)
+			return -EPROTO;
+	}
+	xdr->curloc = 0;
+	return 0;
+}
+
+int
+xdr_dec_uint64 (xdr_dec_t * xdr, uint64_t * i)
+{
+	int err;
+	if (xdr == NULL || i == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_UINT64)
+		return -ENOMSG;
+	*i = be64_to_cpu (*((uint64_t *) (xdr->stream + 1)));
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+int
+xdr_dec_uint32 (xdr_dec_t * xdr, uint32_t * i)
+{
+	int err;
+	if (xdr == NULL || i == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_UINT32)
+		return -ENOMSG;
+	*i = be32_to_cpu (*((uint32_t *) (xdr->stream + 1)));
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+int
+xdr_dec_uint16 (xdr_dec_t * xdr, uint16_t * i)
+{
+	int err;
+	if (xdr == NULL || i == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_UINT16)
+		return -ENOMSG;
+	*i = be16_to_cpu (*((uint16_t *) (xdr->stream + 1)));
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+int
+xdr_dec_uint8 (xdr_dec_t * xdr, uint8_t * i)
+{
+	int err;
+	if (xdr == NULL || i == NULL)
+		return -EINVAL;
+
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_UINT8)
+		return -ENOMSG;
+	*i = *((uint8_t *) (xdr->stream + 1));
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+int
+xdr_dec_ipv6 (xdr_dec_t * xdr, struct in6_addr *ip)
+{
+	int err;
+	if (xdr == NULL || ip == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_IPv6)
+		return -ENOMSG;
+	memcpy (ip, xdr->stream + 1, 16);
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+/* mallocing version */
+int
+xdr_dec_raw_m (xdr_dec_t * xdr, void **p, uint16_t * l)
+{
+	int len;
+	void *str;
+	int err;
+
+	if (xdr == NULL || p == NULL || l == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_RAW)
+		return -ENOMSG;
+	xdr->curloc = 1;
+
+	len = be16_to_cpu (*((uint16_t *) (xdr->stream + xdr->curloc)));
+	xdr->curloc += 2;
+
+	str = kmalloc (len, GFP_KERNEL);
+	if (str == NULL)
+		return -ENOMEM;
+	memcpy (str, (xdr->stream + xdr->curloc), len);
+	xdr->curloc += len;
+
+	*p = str;
+	*l = len;
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+/* non-mallocing version */
+int
+xdr_dec_raw (xdr_dec_t * xdr, void *p, uint16_t * l)
+{
+	int len;
+	int err;
+
+	if (xdr == NULL || p == NULL || l == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_RAW)
+		return -ENOMSG;
+	xdr->curloc = 1;
+
+	len = be16_to_cpu (*((uint16_t *) (xdr->stream + xdr->curloc)));
+	xdr->curloc += 2;
+
+	if (len > *l)
+		return -1;
+
+	memcpy (p, (xdr->stream + xdr->curloc), len);
+	xdr->curloc += len;
+
+	*l = len;
+
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+/**
+ * xdr_dec_raw_ag - auto-growing version
+ * @xdr: 
+ * @p: <> pointer to buffer
+ * @bl: <> size of the buffer
+ * @rl: > size of data read from stream
+ * 
+ * This form of xdr_dec_raw will increase the size of a pre-malloced buffer
+ * to fit the data it is reading.  It is kind of a merger of the
+ * non-mallocing and mallocing versions.
+ * 
+ * Returns: int
+ */
+int
+xdr_dec_raw_ag (xdr_dec_t * xdr, void **p, uint16_t * bl, uint16_t * rl)
+{
+	int len;
+	int err;
+
+	if (xdr == NULL || p == NULL || bl == NULL || rl == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_RAW)
+		return -ENOMSG;
+	xdr->curloc = 1;
+
+	len = be16_to_cpu (*((uint16_t *) (xdr->stream + xdr->curloc)));
+	xdr->curloc += 2;
+
+	if (len > *bl) {	/* grow p */
+		void *temp;
+		temp = xdr_realloc (*p, len, *bl);
+		if (temp == NULL)
+			return -ENOMEM;
+		*bl = len;
+		*p = temp;
+	}
+
+	memcpy (*p, (xdr->stream + xdr->curloc), len);
+	xdr->curloc += len;
+
+	*rl = len;
+
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+/* mallocing version */
+int
+xdr_dec_string (xdr_dec_t * xdr, uint8_t ** strp)
+{
+	int len;
+	char *str;
+	int err;
+	if (xdr == NULL || strp == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_STRING)
+		return -ENOMSG;
+	xdr->curloc = 1;
+
+	len = be16_to_cpu (*((uint16_t *) (xdr->stream + xdr->curloc)));
+	xdr->curloc += 2;
+
+	if (len > 0) {
+		str = kmalloc (len + 1, GFP_KERNEL);
+		if (str == NULL)
+			return -ENOMEM;
+		str[len] = '\0';
+		memcpy (str, (xdr->stream + xdr->curloc), len);
+		xdr->curloc += len;
+
+		*strp = str;
+	} else {
+		*strp = NULL;
+	}
+
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+/* non-mallocing version */
+int
+xdr_dec_string_nm (xdr_dec_t * xdr, uint8_t * string, size_t l)
+{
+	int len;
+	int err;
+	if (xdr == NULL || string == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_STRING)
+		return -ENOMSG;
+	xdr->curloc = 1;
+
+	len = be16_to_cpu (*((uint16_t *) (xdr->stream + xdr->curloc)));
+	xdr->curloc += 2;
+
+	if (len > 0) {
+		memcpy (string, (xdr->stream + xdr->curloc), MIN (len, l));
+		if (l > len) {
+			string[len] = '\0';
+		}
+		string[l - 1] = '\0';
+	} else {
+		string[0] = '\0';
+	}
+
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+int
+xdr_dec_string_ag (xdr_dec_t * xdr, uint8_t ** s, uint16_t * bl)
+{
+	int len;
+	int err;
+	if (xdr == NULL || s == NULL || bl == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_STRING)
+		return -ENOMSG;
+	xdr->curloc = 1;
+
+	len = be16_to_cpu (*((uint16_t *) (xdr->stream + xdr->curloc)));
+	xdr->curloc += 2;
+
+	if (len == 0) {		/* empty string */
+		**s = '\0';
+		*(xdr->stream) = XDR_NULL;
+		return 0;
+	}
+
+	if (len >= *bl) {	/* grow s */
+		void *temp;
+		temp = xdr_realloc (*s, len + 1, *bl);
+		if (temp == NULL)
+			return -ENOMEM;
+		*bl = len + 1;
+		*s = temp;
+	}
+
+	memcpy (*s, (xdr->stream + xdr->curloc), len);
+	(*s)[len] = '\0';
+
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+int
+xdr_dec_list_start (xdr_dec_t * xdr)
+{
+	int err;
+	if (xdr == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_LIST_START)
+		return -ENOMSG;
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
+
+int
+xdr_dec_list_stop (xdr_dec_t * xdr)
+{
+	int err;
+	if (xdr == NULL)
+		return -EINVAL;
+	if (*(xdr->stream) == XDR_NULL) {
+		if ((err = get_next (xdr)) != 0)
+			return err;
+	}
+	if (*(xdr->stream) != XDR_LIST_STOP)
+		return -ENOMSG;
+	/* read the item out, mark that */
+	*(xdr->stream) = XDR_NULL;
+	return 0;
+}
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/xdr_io.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/xdr_io.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/xdr_io.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/xdr_io.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,169 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+ * does the lowest level of reads and writes.
+ * In kernel and/or userspace.
+ */
+
+#include "xdr.h"
+
+#ifdef __KERNEL__
+#ifdef __linux__
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/socket.h>
+#include <net/sock.h>
+#include "asm/uaccess.h"
+
+/**
+ * do_tfer - transfers data over a socket
+ * @sock: < socket
+ * @iov: <> iovec of buffers
+ * @n:    < how many iovecs
+ * @size: < total data size to send/recv
+ * @dir:  < send or recv
+ * @timeout: < how many sec to wait. 0 == forever.
+ * 
+ * Returns: <0: Error
+ *         >=0: Bytes transfered
+ */
+static int
+do_tfer (struct socket *sock, struct iovec *iov, int n, int size, int dir)
+{
+	unsigned long flags;
+	sigset_t oldset;
+	struct msghdr m;
+	mm_segment_t fs;
+	int rv, moved = 0;
+
+	fs = get_fs ();
+	set_fs (get_ds ());
+
+	/* XXX do I still want the signal stuff? */
+	spin_lock_irqsave (&current->sighand->siglock, flags);
+	oldset = current->blocked;
+	siginitsetinv (&current->blocked,
+		       sigmask (SIGKILL) | sigmask (SIGTERM));
+	recalc_sigpending ();
+	spin_unlock_irqrestore (&current->sighand->siglock, flags);
+
+	memset (&m, 0, sizeof (struct msghdr));
+	for (;;) {
+		m.msg_iov = iov;
+		m.msg_iovlen = n;
+		m.msg_flags = MSG_NOSIGNAL;
+
+		if (dir)
+			rv = sock_sendmsg (sock, &m, size - moved);
+		else
+			rv = sock_recvmsg (sock, &m, size - moved, 0);
+
+		if (rv <= 0)
+			goto out_err;
+		moved += rv;
+
+		if (moved >= size)
+			break;
+
+		/* adjust iov's for next transfer */
+		while (iov->iov_len == 0) {
+			iov++;
+			n--;
+		}
+
+	}
+	rv = moved;
+      out_err:
+	spin_lock_irqsave (&current->sighand->siglock, flags);
+	current->blocked = oldset;
+	recalc_sigpending ();
+	spin_unlock_irqrestore (&current->sighand->siglock, flags);
+
+	set_fs (fs);
+
+	return rv;
+}
+
+size_t
+xdr_send (struct socket * sock, void *buf, size_t size)
+{
+	struct iovec iov;
+	int res;
+
+	iov.iov_base = buf;
+	iov.iov_len = size;
+
+	res = do_tfer (sock, &iov, 1, size, 1);
+
+	return res;
+}
+
+size_t
+xdr_recv (struct socket * sock, void *buf, size_t size)
+{
+	struct iovec iov;
+	int res;
+
+	iov.iov_base = buf;
+	iov.iov_len = size;
+
+	res = do_tfer (sock, &iov, 1, size, 0);
+
+	return res;
+}
+
+#endif /*__linux__*/
+#else /*__KERNEL__*/
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+ssize_t
+xdr_recv (int fd, void *buf, size_t len)
+{
+	ssize_t cnt = 0;
+	size_t ttl = 0;
+	while (len > 0) {
+		cnt = recv (fd, buf, len, 0);
+		if (cnt == 0)
+			return 0;
+		if (cnt < 0)
+			return -errno;
+		len -= cnt;
+		buf += cnt;
+		ttl += cnt;
+	}
+	return ttl;
+}
+
+ssize_t
+xdr_send (int fd, void *buf, size_t len)
+{
+	ssize_t cnt = 0;
+	size_t ttl = 0;
+	while (len > 0) {
+		cnt = send (fd, buf, len, 0);
+		if (cnt == 0)
+			return 0;
+		if (cnt < 0)
+			return -errno;
+		len -= cnt;
+		buf += cnt;
+		ttl += cnt;
+	}
+	return ttl;
+}
+
+#endif /*__KERNEL__*/
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_gulm/xdr_socket.c linux-2.6.9.debug/fs/gfs_locking/lock_gulm/xdr_socket.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_gulm/xdr_socket.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_gulm/xdr_socket.c	2006-12-20 17:07:58.000000000 +0300
@@ -0,0 +1,82 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+ * This file opens and closes a socket.
+ * In kernel and/or userspace.
+ */
+
+#include "xdr.h"
+
+#ifdef __KERNEL__
+#ifdef __linux__
+
+int
+xdr_open (xdr_socket * xsk)
+{
+	return sock_create (AF_INET6, SOCK_STREAM, 0, xsk);
+}
+
+int
+xdr_connect (struct sockaddr_in6 *adr, xdr_socket xsk)
+{
+	return xsk->ops->connect (xsk,
+				  (struct sockaddr *) adr,
+				  sizeof (struct sockaddr_in6), 0);
+}
+
+void
+xdr_close (xdr_socket * xsk)
+{
+	if (*xsk == NULL)
+		return;
+	sock_release (*xsk);
+	*xsk = NULL;
+}
+
+#endif /*__linux__*/
+#else /*__KERNEL__*/
+
+int
+xdr_open (xdr_socket * xsk)
+{
+	int sk;
+	sk = socket (AF_INET6, SOCK_STREAM, 0);
+	if (sk < 0)
+		return -errno;
+	*xsk = sk;
+	return 0;
+}
+
+int
+xdr_connect (struct sockaddr_in6 *adr, xdr_socket xsk)
+{
+	int err;
+	err =
+	    connect (xsk, (struct sockaddr *) adr,
+		     sizeof (struct sockaddr_in6));
+	if (err < 0)
+		return -errno;
+	return 0;
+}
+
+void
+xdr_close (xdr_socket * xsk)
+{
+	if (*xsk < 0)
+		return;
+	close (*xsk);
+	*xsk = -1;
+}
+
+#endif /*__KERNEL__*/
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_harness/Makefile linux-2.6.9.debug/fs/gfs_locking/lock_harness/Makefile
--- linux-2.6.9.orig/fs/gfs_locking/lock_harness/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_harness/Makefile	2006-12-20 17:07:21.000000000 +0300
@@ -0,0 +1,16 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+##
+##  This copyrighted material is made available to anyone wishing to use,
+##  modify, copy, or redistribute it subject to the terms and conditions
+##  of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+obj-$(CONFIG_LOCK_HARNESS) += lock_harness.o
+
+lock_harness-y	:= main.o
+
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_harness/main.c linux-2.6.9.debug/fs/gfs_locking/lock_harness/main.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_harness/main.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_harness/main.c	2006-12-20 17:08:00.000000000 +0300
@@ -0,0 +1,244 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/kmod.h>
+#include <linux/fs.h>
+#include <linux/lm_interface.h>
+
+#define RELEASE_NAME "2.6.9-60.3"
+
+struct lmh_wrapper {
+	struct list_head lw_list;
+	struct lm_lockops *lw_ops;
+};
+
+static struct semaphore lmh_lock;
+static struct list_head lmh_list;
+
+/**
+ * lm_register_proto - Register a low-level locking protocol
+ * @proto: the protocol definition
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int
+lm_register_proto(struct lm_lockops *proto)
+{
+	struct list_head *tmp, *head;
+	struct lmh_wrapper *lw;
+
+	down(&lmh_lock);
+
+	for (head = &lmh_list, tmp = head->next; tmp != head; tmp = tmp->next) {
+		lw = list_entry(tmp, struct lmh_wrapper, lw_list);
+
+		if (strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name) == 0) {
+			up(&lmh_lock);
+			printk("lock_harness:  protocol %s already exists\n",
+			       proto->lm_proto_name);
+			return -EEXIST;
+		}
+	}
+
+	lw = kmalloc(sizeof (struct lmh_wrapper), GFP_KERNEL);
+	if (!lw) {
+		up(&lmh_lock);
+		return -ENOMEM;
+	}
+	memset(lw, 0, sizeof (struct lmh_wrapper));
+
+	lw->lw_ops = proto;
+	list_add(&lw->lw_list, &lmh_list);
+
+	up(&lmh_lock);
+
+	return 0;
+}
+
+/**
+ * lm_unregister_proto - Unregister a low-level locking protocol
+ * @proto: the protocol definition
+ *
+ */
+
+void
+lm_unregister_proto(struct lm_lockops *proto)
+{
+	struct list_head *tmp, *head;
+	struct lmh_wrapper *lw = NULL;
+
+	down(&lmh_lock);
+
+	for (head = &lmh_list, tmp = head->next; tmp != head; tmp = tmp->next) {
+		lw = list_entry(tmp, struct lmh_wrapper, lw_list);
+
+		if (strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name) == 0) {
+			list_del(&lw->lw_list);
+			up(&lmh_lock);
+			kfree(lw);
+			return;
+		}
+	}
+
+	up(&lmh_lock);
+
+	printk("lock_harness:  can't unregister lock protocol %s\n",
+	       proto->lm_proto_name);
+}
+
+/**
+ * lm_mount - Mount a lock protocol
+ * @proto_name - the name of the protocol
+ * @table_name - the name of the lock space
+ * @host_data - data specific to this host
+ * @cb - the callback to the code using the lock module
+ * @fsdata - data to pass back with the callback
+ * @min_lvb_size - the mininum LVB size that the caller can deal with
+ * @lockstruct - a structure returned describing the mount
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int
+lm_mount(char *proto_name, char *table_name, char *host_data,
+	 lm_callback_t cb, lm_fsdata_t * fsdata,
+	 unsigned int min_lvb_size, struct lm_lockstruct *lockstruct)
+{
+	struct list_head *tmp;
+	struct lmh_wrapper *lw = NULL;
+	int try = 0;
+	int error;
+
+      retry:
+	down(&lmh_lock);
+
+	for (tmp = lmh_list.next; tmp != &lmh_list; tmp = tmp->next) {
+		lw = list_entry(tmp, struct lmh_wrapper, lw_list);
+
+		if (strcmp(lw->lw_ops->lm_proto_name, proto_name) == 0)
+			break;
+		else
+			lw = NULL;
+	}
+
+	if (!lw) {
+		if (!try && capable(CAP_SYS_MODULE)) {
+			try = 1;
+			up(&lmh_lock);
+			request_module(proto_name);
+			goto retry;
+		}
+		printk("lock_harness:  can't find protocol %s\n", proto_name);
+		error = -ENOENT;
+		goto out;
+	}
+
+	if (!try_module_get(lw->lw_ops->lm_owner)) {
+		try = 0;
+		up(&lmh_lock);
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(HZ);
+		goto retry;
+	}
+
+	error = lw->lw_ops->lm_mount(table_name, host_data,
+				     cb, fsdata, min_lvb_size, lockstruct);
+	if (error)
+		module_put(lw->lw_ops->lm_owner);
+
+      out:
+	up(&lmh_lock);
+
+	return error;
+}
+
+/**
+ * lm_unmount - unmount a lock module
+ * @lockstruct: the lockstruct passed into mount
+ *
+ */
+
+void
+lm_unmount(struct lm_lockstruct *lockstruct)
+{
+	down(&lmh_lock);
+	lockstruct->ls_ops->lm_unmount(lockstruct->ls_lockspace);
+	if (lockstruct->ls_ops->lm_owner)
+		module_put(lockstruct->ls_ops->lm_owner);
+	up(&lmh_lock);
+}
+
+/**
+ * lm_withdraw - abnormally unmount a lock module
+ * @lockstruct: the lockstruct passed into mount
+ *
+ */
+
+void
+lm_withdraw(struct lm_lockstruct *lockstruct)
+{
+	down(&lmh_lock);
+	lockstruct->ls_ops->lm_withdraw(lockstruct->ls_lockspace);
+	if (lockstruct->ls_ops->lm_owner)
+		module_put(lockstruct->ls_ops->lm_owner);
+	up(&lmh_lock);
+}
+
+/**
+ * init_lmh - Initialize the lock module harness
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int __init
+init_lmh(void)
+{
+	init_MUTEX(&lmh_lock);
+	INIT_LIST_HEAD(&lmh_list);
+
+	printk("Lock_Harness %s (built %s %s) installed\n",
+	       RELEASE_NAME, __DATE__, __TIME__);
+
+	return 0;
+}
+
+/**
+ * exit_lmh - cleanup the Lock Module Harness
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+void __exit
+exit_lmh(void)
+{
+}
+
+module_init(init_lmh);
+module_exit(exit_lmh);
+
+MODULE_DESCRIPTION("GFS Lock Module Harness " RELEASE_NAME);
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL_GPL(lm_register_proto);
+EXPORT_SYMBOL_GPL(lm_unregister_proto);
+EXPORT_SYMBOL_GPL(lm_mount);
+EXPORT_SYMBOL_GPL(lm_unmount);
+EXPORT_SYMBOL_GPL(lm_withdraw);
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_nolock/Makefile linux-2.6.9.debug/fs/gfs_locking/lock_nolock/Makefile
--- linux-2.6.9.orig/fs/gfs_locking/lock_nolock/Makefile	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_nolock/Makefile	2006-12-20 17:07:27.000000000 +0300
@@ -0,0 +1,16 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+##
+##  This copyrighted material is made available to anyone wishing to use,
+##  modify, copy, or redistribute it subject to the terms and conditions
+##  of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+obj-$(CONFIG_LOCK_NOLOCK) += lock_nolock.o
+
+lock_nolock-y	:= main.o
+
diff -pruN linux-2.6.9.orig/fs/gfs_locking/lock_nolock/main.c linux-2.6.9.debug/fs/gfs_locking/lock_nolock/main.c
--- linux-2.6.9.orig/fs/gfs_locking/lock_nolock/main.c	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/fs/gfs_locking/lock_nolock/main.c	2006-12-20 17:08:07.000000000 +0300
@@ -0,0 +1,358 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/lm_interface.h>
+
+#define RELEASE_NAME "2.6.9-60.3"
+
+struct nolock_lockspace {
+	unsigned int nl_lvb_size;
+};
+
+struct lm_lockops nolock_ops;
+
+/**
+ * nolock_mount - mount a nolock lockspace
+ * @table_name: the name of the space to mount
+ * @host_data: host specific data
+ * @cb: the callback
+ * @lockstruct: the structure of crap to fill in
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+static int
+nolock_mount(char *table_name, char *host_data,
+	     lm_callback_t cb, lm_fsdata_t *fsdata,
+	     unsigned int min_lvb_size, struct lm_lockstruct *lockstruct)
+{
+	char *c;
+	unsigned int jid;
+	struct nolock_lockspace *nl;
+
+	/* If there is a "jid=" in the hostdata, return that jid.
+	   Otherwise, return zero. */
+
+	c = strstr(host_data, "jid=");
+	if (!c)
+		jid = 0;
+	else {
+		c += 4;
+		sscanf(c, "%u", &jid);
+	}
+
+	nl = kmalloc(sizeof(struct nolock_lockspace), GFP_KERNEL);
+	if (!nl)
+		return -ENOMEM;
+
+	memset(nl, 0, sizeof(struct nolock_lockspace));
+	nl->nl_lvb_size = min_lvb_size;
+
+	lockstruct->ls_jid = jid;
+	lockstruct->ls_first = 1;
+	lockstruct->ls_lvb_size = min_lvb_size;
+	lockstruct->ls_lockspace = (lm_lockspace_t *)nl;
+	lockstruct->ls_ops = &nolock_ops;
+	lockstruct->ls_flags = LM_LSFLAG_LOCAL;
+
+	return 0;
+}
+
+/**
+ * nolock_others_may_mount - unmount a lock space
+ * @lockspace: the lockspace to unmount
+ *
+ */
+
+static void
+nolock_others_may_mount(lm_lockspace_t *lockspace)
+{
+}
+
+/**
+ * nolock_unmount - unmount a lock space
+ * @lockspace: the lockspace to unmount
+ *
+ */
+
+static void
+nolock_unmount(lm_lockspace_t *lockspace)
+{
+	struct nolock_lockspace *nl = (struct nolock_lockspace *)lockspace;
+	kfree(nl);
+}
+
+/**
+ * nolock_withdraw - withdraw from a lock space
+ * @lockspace: the lockspace
+ *
+ */
+
+static void
+nolock_withdraw(lm_lockspace_t *lockspace)
+{
+}
+
+/**
+ * nolock_get_lock - get a lm_lock_t given a descripton of the lock
+ * @lockspace: the lockspace the lock lives in
+ * @name: the name of the lock
+ * @lockp: return the lm_lock_t here
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+static int
+nolock_get_lock(lm_lockspace_t *lockspace, struct lm_lockname *name,
+		lm_lock_t ** lockp)
+{
+	*lockp = (lm_lock_t *)lockspace;
+	return 0;
+}
+
+/**
+ * nolock_put_lock - get rid of a lock structure
+ * @lock: the lock to throw away
+ *
+ */
+
+static void
+nolock_put_lock(lm_lock_t *lock)
+{
+}
+
+/**
+ * nolock_lock - acquire a lock
+ * @lock: the lock to manipulate
+ * @cur_state: the current state
+ * @req_state: the requested state
+ * @flags: modifier flags
+ *
+ * Returns: A bitmap of LM_OUT_*
+ */
+
+static unsigned int
+nolock_lock(lm_lock_t *lock, unsigned int cur_state, unsigned int req_state,
+	    unsigned int flags)
+{
+	return req_state | LM_OUT_CACHEABLE;
+}
+
+/**
+ * nolock_unlock - unlock a lock
+ * @lock: the lock to manipulate
+ * @cur_state: the current state
+ *
+ * Returns: 0
+ */
+
+static unsigned int
+nolock_unlock(lm_lock_t *lock, unsigned int cur_state)
+{
+	return 0;
+}
+
+/**
+ * nolock_cancel - cancel a request on a lock
+ * @lock: the lock to cancel request for
+ *
+ */
+
+static void
+nolock_cancel(lm_lock_t *lock)
+{
+}
+
+/**
+ * nolock_hold_lvb - hold on to a lock value block
+ * @lock: the lock the LVB is associated with
+ * @lvbp: return the lm_lvb_t here
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+static int
+nolock_hold_lvb(lm_lock_t *lock, char **lvbp)
+{
+	struct nolock_lockspace *nl = (struct nolock_lockspace *)lock;
+	int error = 0;
+
+	*lvbp = kmalloc(nl->nl_lvb_size, GFP_KERNEL);
+	if (*lvbp)
+		memset(*lvbp, 0, nl->nl_lvb_size);
+	else
+		error = -ENOMEM;
+
+	return error;
+}
+
+/**
+ * nolock_unhold_lvb - release a LVB
+ * @lock: the lock the LVB is associated with
+ * @lvb: the lock value block
+ *
+ */
+
+static void
+nolock_unhold_lvb(lm_lock_t *lock, char *lvb)
+{
+	kfree(lvb);
+}
+
+/**
+ * nolock_sync_lvb - sync out the value of a lvb
+ * @lock: the lock the LVB is associated with
+ * @lvb: the lock value block
+ *
+ */
+
+static void
+nolock_sync_lvb(lm_lock_t *lock, char *lvb)
+{
+}
+
+/**
+ * nolock_plock_get - 
+ * @lockspace: the lockspace
+ * @name:
+ * @file:
+ * @fl:
+ *
+ * Returns: errno
+ */
+
+static int
+nolock_plock_get(lm_lockspace_t *lockspace,
+		 struct lm_lockname *name,
+		 struct file *file, struct file_lock *fl)
+{
+	return LOCK_USE_CLNT;
+}
+
+/**
+ * nolock_plock -
+ * @lockspace: the lockspace
+ * @name:
+ * @file:
+ * @cmd:
+ * @fl:
+ *
+ * Returns: errno
+ */
+
+static int
+nolock_plock(lm_lockspace_t *lockspace,
+	     struct lm_lockname *name,
+	     struct file *file, int cmd, struct file_lock *fl)
+{
+	return posix_lock_file_wait(file, fl);
+}
+
+/**
+ * nolock_punlock -
+ * @lockspace: the lockspace
+ * @name:
+ * @file:
+ * @fl:
+ *
+ * Returns: errno
+ */
+
+static int
+nolock_punlock(lm_lockspace_t *lockspace,
+	       struct lm_lockname *name,
+	       struct file *file, struct file_lock *fl)
+{
+	return posix_lock_file_wait(file, fl);
+}
+
+/**
+ * nolock_recovery_done - reset the expired locks for a given jid
+ * @lockspace: the lockspace
+ * @jid: the jid
+ *
+ */
+
+static void
+nolock_recovery_done(lm_lockspace_t *lockspace, unsigned int jid,
+		     unsigned int message)
+{
+}
+
+struct lm_lockops nolock_ops = {
+	.lm_proto_name = "lock_nolock",
+	.lm_mount = nolock_mount,
+	.lm_others_may_mount = nolock_others_may_mount,
+	.lm_unmount = nolock_unmount,
+	.lm_withdraw = nolock_withdraw,
+	.lm_get_lock = nolock_get_lock,
+	.lm_put_lock = nolock_put_lock,
+	.lm_lock = nolock_lock,
+	.lm_unlock = nolock_unlock,
+	.lm_cancel = nolock_cancel,
+	.lm_hold_lvb = nolock_hold_lvb,
+	.lm_unhold_lvb = nolock_unhold_lvb,
+	.lm_sync_lvb = nolock_sync_lvb,
+	.lm_plock_get = nolock_plock_get,
+	.lm_plock = nolock_plock,
+	.lm_punlock = nolock_punlock,
+	.lm_recovery_done = nolock_recovery_done,
+	.lm_owner = THIS_MODULE,
+};
+
+/**
+ * init_nolock - Initialize the nolock module
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+
+int __init
+init_nolock(void)
+{
+	int error;
+
+	error = lm_register_proto(&nolock_ops);
+	if (error) {
+		printk("lock_nolock: can't register protocol: %d\n", error);
+		return error;
+	}
+
+	printk("Lock_Nolock %s (built %s %s) installed\n",
+	       RELEASE_NAME, __DATE__, __TIME__);
+
+	return 0;
+}
+
+/**
+ * exit_nolock - cleanup the nolock module
+ *
+ */
+
+void __exit
+exit_nolock(void)
+{
+	lm_unregister_proto(&nolock_ops);
+}
+
+module_init(init_nolock);
+module_exit(exit_nolock);
+
+MODULE_DESCRIPTION("GFS Nolock Locking Module " RELEASE_NAME);
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
diff -pruN linux-2.6.9.orig/include/cluster/cnxman-socket.h linux-2.6.9.debug/include/cluster/cnxman-socket.h
diff -pruN linux-2.6.9.orig/include/cluster/cnxman.h linux-2.6.9.debug/include/cluster/cnxman.h
diff -pruN linux-2.6.9.orig/include/cluster/dlm.h linux-2.6.9.debug/include/cluster/dlm.h
diff -pruN linux-2.6.9.orig/include/cluster/dlm_device.h linux-2.6.9.debug/include/cluster/dlm_device.h
diff -pruN linux-2.6.9.orig/include/cluster/service.h linux-2.6.9.debug/include/cluster/service.h
diff -pruN linux-2.6.9.orig/include/linux/gfs_ioctl.h linux-2.6.9.debug/include/linux/gfs_ioctl.h
--- linux-2.6.9.orig/include/linux/gfs_ioctl.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/include/linux/gfs_ioctl.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,33 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __GFS_IOCTL_DOT_H__
+#define __GFS_IOCTL_DOT_H__
+
+#define _GFSC_(x)               (('G' << 8) | (x))
+
+/* Ioctls implemented */
+
+#define GFS_IOCTL_IDENTIFY      _GFSC_(35)
+#define GFS_IOCTL_SUPER         _GFSC_(45)
+
+struct gfs_ioctl {
+	unsigned int gi_argc;
+	char **gi_argv;
+
+        char __user *gi_data;
+	unsigned int gi_size;
+	uint64_t gi_offset;
+};
+
+#endif /* ___GFS_IOCTL_DOT_H__ */
diff -pruN linux-2.6.9.orig/include/linux/gfs_ondisk.h linux-2.6.9.debug/include/linux/gfs_ondisk.h
--- linux-2.6.9.orig/include/linux/gfs_ondisk.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/include/linux/gfs_ondisk.h	2006-12-20 17:08:19.000000000 +0300
@@ -0,0 +1,1896 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+ * On-disk structures.
+ *
+ * THE BIG PICTURE of on-disk layout:
+ *
+ * GFS filesystem code views the entire filesystem, including journals, as
+ * one contiguous group of blocks on one (perhaps virtual) storage device.
+ * The filesystem space is shared, not distributed; each node in the cluster
+ * must see the entire filesystem space.
+ *
+ * If the filesystem is spread across multiple physical storage devices,
+ * volume management (device mapping) must be used to present the fileystem
+ * space to GFS as one (virtual) device, with contiguous blocks.
+ *
+ * The superblock contains basic information about the filesytem, and appears
+ * at a location 64 KBytes into the filesystem.  The first 64 KBytes of the
+ * filesystem are empty, providing a safety buffer against wayward volume
+ * management software (that sometimes write data into the first few bytes of
+ * a device) or administrators.
+ *
+ * After the superblock, the rest of the filesystem is divided into multiple
+ * Resource Groups and several journals.
+ *
+ * The Resource Groups (RGs or rgrps) contain the allocatable blocks that are
+ * used for storing files, directories, etc., and all of the associated
+ * metadata.  Each RG has its own set of block allocation statistics (within
+ * the RG header), a number of blocks containing the block allocation bitmap,
+ * and a large number of allocatable blocks for file data and metadata.
+ * Multiple RGs allow multiple nodes to simultaneously allocate blocks from the 
+ * filesystem (using different RGs), enhancing parallel access.  RG size and
+ * number of RGs are determined by gfs_mkfs when creating the filesystem.
+ * An administrator can specify RG size (see man gfs_mkfs).
+ *
+ * The journals contain temporary copies of metadata blocks, along with
+ * other data, that allow GFS to recover the filesystem to a consistent state
+ * (at least as far as metadata is concerned) if a node fails in the midst
+ * of performing a write transaction.  There must be one journal for each node
+ * in the cluster.  Since access to the entire filesystem space is shared,
+ * if a node crashes, another node will be able to read the crashed node's
+ * journal, and perform recovery.
+ *
+ * Currently, gfs_mkfs places the journals right in the middle of a freshly
+ * created filesystem space, between 2 large groups of RGs.  From a filesystem
+ * layout perspective, this placement is not a requirement; the journals
+ * could be placed anywhere within the filesystem space.
+ *
+ * New Resource Groups and Journals may be added to the filesystem after the
+ * filesystem has been created, if the filesystem's (virtual) device is made
+ * larger.  See man gfs_grow and gfs_jadd.
+ *
+ * A few special hidden inodes are contained in a GFS filesystem.  They do
+ * not appear in any directories; instead, the superblock points to them
+ * using block numbers for their location.  The special inodes are:
+ *
+ *   Root inode:  Root directory of the filesystem
+ *   Resource Group Index:  A file containing block numbers and sizes of all RGs
+ *   Journal Index:  A file containing block numbers and sizes of all journals
+ *   Quota:  A file containing all quota information for the filesystem
+ *   License:  A file containing license information
+ *
+ * Note that there is NOTHING RELATED TO INTER-NODE LOCK MANAGEMENT ON-DISK.
+ * Locking is handled completely off-disk, typically via LAN.
+ *
+ * NOTE:
+ * If you add 8 byte fields to these structures, they must be 8 byte
+ * aligned.  4 byte field must be 4 byte aligned, etc...
+ *
+ * All structures must be a multiple of 8 bytes long.
+ *
+ * GRIPES:
+ * We should have forgetten about supporting 512B FS block sizes 
+ * and made the di_reserved field in the struct gfs_dinode structure
+ * much bigger.
+ *
+ * de_rec_len in struct gfs_dirent should really have been a 32-bit value
+ * as it now limits us to a 64k FS block size (with the current code
+ * in dir.c).
+ */
+
+#ifndef __GFS_ONDISK_DOT_H__
+#define __GFS_ONDISK_DOT_H__
+
+#define GFS_MAGIC               (0x01161970) /* for all on-disk headers */
+#define GFS_BASIC_BLOCK         (512)  /* "basic block" = "sector" = 512B */
+#define GFS_BASIC_BLOCK_SHIFT   (9)
+
+/*  Controls how much data can be logged in-core before dumping log to disk */
+
+#define GFS_DUMPS_PER_LOG       (4)    /* 1/4 of on-disk journal size*/
+
+/*  Lock numbers of the LM_TYPE_NONDISK type.  These protect certain
+ *  cluster-wide operations (rather than on-disk entities).
+ *  Currently, the LIVE lock is not used for any real purpose.  */
+
+#define GFS_MOUNT_LOCK          (0)    /* only one node can Mount at a time */
+#define GFS_LIVE_LOCK           (1)    /* shared by all mounted nodes */
+#define GFS_TRANS_LOCK          (2)    /* Transaction, protects jrnl recovery */
+#define GFS_RENAME_LOCK         (3)    /* only one node can Rename at a time */
+
+/*  On-disk format (version) numbers for various metadata types,
+ *  used in gfs_meta_header  */
+
+#define GFS_FORMAT_SB           (100)  /* Super-Block */
+#define GFS_FORMAT_RG           (200)  /* Resource Group Header */
+#define GFS_FORMAT_RB           (300)  /* Resource Group Block Alloc BitBlock */
+#define GFS_FORMAT_DI           (400)  /* "Disk" inode (dinode) */
+#define GFS_FORMAT_IN           (500)  /* Indirect dinode block list */
+#define GFS_FORMAT_LF           (600)  /* Leaf dinode block list */
+#define GFS_FORMAT_JD           (700)  /* Journal Data */
+#define GFS_FORMAT_LH           (800)  /* Log Header */
+#define GFS_FORMAT_LD           (900)  /* Log Descriptor */
+/*  These don't have actual struct gfs_meta_header structures to go with them */
+#define GFS_FORMAT_JI           (1000) /* Journal Index */
+#define GFS_FORMAT_RI           (1100) /* Resource Group Index */
+#define GFS_FORMAT_DE           (1200) /* Directory Entry */
+#define GFS_FORMAT_QU           (1500) /* Quota */
+#define GFS_FORMAT_EA           (1600) /* Extended Attribute */
+#define GFS_FORMAT_ED           (1700) /* Extended Attribute data */
+/*  These version #s are embedded in the superblock  */
+#define GFS_FORMAT_FS           (1309) /* Filesystem (all-encompassing) */
+#define GFS_FORMAT_MULTI        (1401) /* Multi-Host */
+
+/*
+ *  An on-disk inode number
+ *  Initially, the on-disk block address of the inode block is assigned as the
+ *  formal (permanent) ID as well.  Block address can change (to move inode
+ *  on-disk), but formal ID must stay unchanged once assigned.
+ */
+
+#define gfs_inum_equal(ino1, ino2) \
+(((ino1)->no_formal_ino == (ino2)->no_formal_ino) && \
+ ((ino1)->no_addr == (ino2)->no_addr))
+
+struct gfs_inum {
+	uint64_t no_formal_ino;        /* inode identifier */
+	uint64_t no_addr;              /* block # of dinode block */
+};
+
+/*
+ *  Generic metadata head structure
+ *
+ *  Every inplace buffer logged in the journal must start
+ *  with a struct gfs_meta_header.
+ *
+ *  In addition to telling what kind of metadata is in the block,
+ *  the metaheader contains the important generation and incarnation
+ *  numbers.
+ *
+ *  The generation number is used during journal recovery to determine
+ *  whether an in-place block on-disk is older than an on-disk journaled copy
+ *  of the block.  If so, GFS overwrites the in-place block with the journaled
+ *  version of the block.
+ *
+ *  A meta block's generation number must increment monotonically across the
+ *  cluster, each time new contents are committed to the block.  This means
+ *  that whenever GFS allocates a pre-existing metadata block, GFS must read
+ *  that block from disk (in case another node has incremented it).  It also
+ *  means that GFS must sync the block (with incremented generation number)
+ *  to disk (both log and in-place blocks), not only after changing contents
+ *  of the block, but also after de-allocating the block (GFS can't just throw
+ *  away incore metadata for a file that it's just erased).
+ *
+ *  The incarnation number is used only for on-disk (d)inodes.  GFS increments
+ *  it each time it de-allocates a dinode block (i.e. each time the dinode
+ *  loses its identity with a particular file, directory, etc.).  When the
+ *  dinode is later allocated (i.e. to be identified with a new file, etc.),
+ *  GFS copies the incarnation number into the VFS inode's i_generation member.
+ *  If GFS is used as the backing store for an NFS server, GFS uses this
+ *  i_generation number as part of the NFS filehandle, which differentiates
+ *  it from the previous identity of the dinode, and helps protect against
+ *  filesystem corruption that could happen with the use of outdated,
+ *  invalid, or malicious filehandles.  See ops_export.c.
+ *
+ *  GFS caches de-allocated meta-headers, to minimize disk reads.
+ *  See struct gfs_meta_header_cache.
+ */
+
+#define GFS_METATYPE_NONE       (0)
+#define GFS_METATYPE_SB         (1)    /* Super-Block */
+#define GFS_METATYPE_RG         (2)    /* Resource Group Header */
+#define GFS_METATYPE_RB         (3)    /* Resource Group Block Alloc BitBlock */
+#define GFS_METATYPE_DI         (4)    /* "Disk" inode (dinode) */
+#define GFS_METATYPE_IN         (5)    /* Indirect dinode block list */
+#define GFS_METATYPE_LF         (6)    /* Leaf dinode block list */
+#define GFS_METATYPE_JD         (7)    /* Journal Data */
+#define GFS_METATYPE_LH         (8)    /* Log Header (gfs_log_header) */
+#define GFS_METATYPE_LD         (9)    /* Log Descriptor (gfs_log_descriptor) */
+#define GFS_METATYPE_EA         (10)   /* Extended Attribute */
+#define GFS_METATYPE_ED         (11)   /* Extended Attribute data */
+
+#define GFS_META_CLUMP          (64)   /* # blocks to convert fm data to meta */
+
+struct gfs_meta_header {
+	uint32_t mh_magic;      /* GFS_MAGIC sanity check magic number */
+	uint32_t mh_type;       /* GFS_METATYPE_XX type of metadata block */
+	uint64_t mh_generation; /* increment before writing to journal */
+	uint32_t mh_format;     /* GFS_FORMAT_XX (version # for this type) */
+	uint32_t mh_incarn;     /* increment when marking dinode "unused" */
+};
+
+/*
+ *  super-block structure
+ *
+ *  One of these is at beginning of filesystem.
+ *  It's probably good if SIZEOF_SB <= GFS_BASIC_BLOCK (512 bytes)
+ */
+
+/*  Address of SuperBlock in GFS basic blocks.  1st 64K of filesystem is empty
+    for safety against getting clobbered by wayward volume managers, etc.
+    64k was chosen because it's the largest GFS-supported fs block size.  */
+#define GFS_SB_ADDR             (128)
+
+/*  The lock number for the superblock (must be zero)  */
+#define GFS_SB_LOCK             (0)
+#define GFS_CRAP_LOCK           (1)
+
+/*  Requirement:  GFS_LOCKNAME_LEN % 8 == 0
+    Includes: the fencing zero at the end  */
+#define GFS_LOCKNAME_LEN        (64)
+
+struct gfs_sb {
+	/*  Order is important; need to be able to read old superblocks
+	    in order to support on-disk version upgrades */
+	struct gfs_meta_header sb_header;
+
+	uint32_t sb_fs_format;         /* GFS_FORMAT_FS (on-disk version) */
+	uint32_t sb_multihost_format;  /* GFS_FORMAT_MULTI */
+	uint32_t sb_flags;             /* ?? */
+
+	uint32_t sb_bsize;             /* fundamental FS block size in bytes */
+	uint32_t sb_bsize_shift;       /* log2(sb_bsize) */
+	uint32_t sb_seg_size;          /* Journal segment size in FS blocks */
+
+	/* These special inodes do not appear in any on-disk directory. */
+	struct gfs_inum sb_jindex_di;  /* journal index inode */
+	struct gfs_inum sb_rindex_di;  /* resource group index inode */
+	struct gfs_inum sb_root_di;    /* root directory inode */
+
+	/* Default inter-node locking protocol (lock module) and namespace */
+	char sb_lockproto[GFS_LOCKNAME_LEN]; /* lock protocol name */
+	char sb_locktable[GFS_LOCKNAME_LEN]; /* unique name for this FS */
+
+	/* More special inodes */
+	struct gfs_inum sb_quota_di;   /* quota inode */
+	struct gfs_inum sb_license_di; /* license inode */
+
+	char sb_reserved[96];
+};
+
+/*
+ *  journal index structure 
+ *
+ *  One for each journal used by the filesystem.
+ *  These descriptors are packed contiguously within the jindex inode (file).
+ */
+
+struct gfs_jindex {
+	uint64_t ji_addr;       /* starting block of the journal */
+	uint32_t ji_nsegment;   /* number (quantity) of segments in journal */
+	uint32_t ji_pad;
+
+	char ji_reserved[64];
+};
+
+/*
+ *  resource index structure 
+ *
+ *  One of these for each resource group in the filesystem.
+ *  These descriptors are packed contiguously within the rindex inode (file).
+ *  Also see struct gfs_rgrp.
+ */
+
+struct gfs_rindex {
+	uint64_t ri_addr;     /* block # of 1st block (header) in rgrp */
+	uint32_t ri_length;   /* # fs blocks containing rgrp header & bitmap */
+	uint32_t ri_pad;
+
+	uint64_t ri_data1;    /* block # of first data/meta block in rgrp */
+	uint32_t ri_data;     /* number (qty) of data/meta blocks in rgrp */
+
+	uint32_t ri_bitbytes; /* total # bytes used by block alloc bitmap */
+
+	char ri_reserved[64];
+};
+
+/*
+ *  resource group header structure
+ *
+ *  One of these at beginning of the first block of an rgrp,
+ *     followed by block alloc bitmap data in remainder of first block.
+ *  Each resource group contains:
+ *    Header block, including block allocation statistics (struct gfs_rgrp)
+ *       and first part of block alloc bitmap.
+ *    Bitmap block(s), continuing block alloc bitmap started in header block.
+ *    Data/meta blocks, allocatable blocks containing file data and metadata.
+ *  
+ *  In older versions, now-unused (but previously allocated) dinodes were
+ *  saved for re-use in an on-disk linked list (chain).  This is no longer
+ *  done, but support still exists for reclaiming dinodes from this list,
+ *  to support upgrades from older on-disk formats.
+ */
+
+/* Each data block within rgrp is represented by 2 bits in the alloc bitmap */
+#define GFS_NBBY                (4)  /* # blocks represented by 1 bitmap byte */
+#define GFS_BIT_SIZE            (2)
+#define GFS_BIT_MASK            (0x00000003)
+
+/*
+ * 4 possible block allocation states:
+ *   bit 0 = alloc(1)/free(0)
+ *   bit 1 = metadata(1)/data(0)
+ */
+#define GFS_BLKST_FREE          (0)
+#define GFS_BLKST_USED          (1)
+#define GFS_BLKST_FREEMETA      (2)
+#define GFS_BLKST_USEDMETA      (3)
+
+struct gfs_rgrp {
+	struct gfs_meta_header rg_header;
+
+	uint32_t rg_flags;      /* ?? */
+
+	uint32_t rg_free;       /* Number (qty) of free data blocks */
+
+	/* Dinodes are USEDMETA, but are handled separately from other METAs */
+	uint32_t rg_useddi;     /* Number (qty) of dinodes (used or free) */
+	uint32_t rg_freedi;     /* Number (qty) of unused (free) dinodes */
+	struct gfs_inum rg_freedi_list; /* 1st block in chain of free dinodes */
+
+	/* These META statistics do not include dinodes (used or free) */
+	uint32_t rg_usedmeta;   /* Number (qty) of used metadata blocks */
+	uint32_t rg_freemeta;   /* Number (qty) of unused metadata blocks */
+
+	char rg_reserved[64];
+};
+
+/*
+ *  quota structure
+ */
+
+struct gfs_quota {
+	uint64_t qu_limit;
+	uint64_t qu_warn;
+	int64_t qu_value;
+
+	char qu_reserved[64];
+};
+
+/*
+ *  dinode (disk inode) structure
+ *  The ondisk representation of inodes
+ *  One for each file, directory, etc.
+ *  GFS does not put more than one inode in a single block.
+ *  The inode may be "stuffed", carrying file data along with metadata,
+ *    if the file data is small enough.
+ *  Otherwise, the inode block contains pointers to other blocks that contain
+ *    either file data or other pointers to other blocks (indirect addressing
+ *    via a metadata tree).
+ */
+
+#define GFS_MAX_META_HEIGHT     (10)
+#define GFS_DIR_MAX_DEPTH       (17)
+
+/*  Dinode types  */
+#define GFS_FILE_NON            (0)
+#define GFS_FILE_REG            (1)    /* regular file */
+#define GFS_FILE_DIR            (2)    /* directory */
+#define GFS_FILE_LNK            (5)    /* link */
+#define GFS_FILE_BLK            (7)    /* block device node */
+#define GFS_FILE_CHR            (8)    /* character device node */
+#define GFS_FILE_FIFO           (101)  /* fifo/pipe */
+#define GFS_FILE_SOCK           (102)  /* socket */
+
+/*  Dinode flags  */
+#define GFS_DIF_JDATA             (0x00000001) /* jrnl all data for this file */
+#define GFS_DIF_EXHASH            (0x00000002) /* hashed directory (leaves) */
+#define GFS_DIF_UNUSED            (0x00000004) /* unused dinode */
+#define GFS_DIF_EA_INDIRECT       (0x00000008) /* extended attribute, indirect*/
+#define GFS_DIF_DIRECTIO          (0x00000010)
+#define GFS_DIF_IMMUTABLE         (0x00000020) /* Can't change file */
+#define GFS_DIF_APPENDONLY        (0x00000040) /* Can only add to end of file */
+#define GFS_DIF_NOATIME           (0x00000080) /* Don't update access time
+						  (currently unused/ignored) */
+#define GFS_DIF_SYNC              (0x00000100) /* Flush to disk, don't cache
+						  (currently unused/ignored) */
+#define GFS_DIF_INHERIT_DIRECTIO  (0x40000000) /* new files get DIRECTIO flag */
+#define GFS_DIF_INHERIT_JDATA     (0x80000000) /* new files get JDATA flag */
+
+struct gfs_dinode {
+	struct gfs_meta_header di_header;
+
+	struct gfs_inum di_num; /* formal inode # and block address */
+
+	uint32_t di_mode;	/* mode of file */
+	uint32_t di_uid;	/* owner's user id */
+	uint32_t di_gid;	/* owner's group id */
+	uint32_t di_nlink;	/* number (qty) of links to this file */
+	uint64_t di_size;	/* number (qty) of bytes in file */
+	uint64_t di_blocks;	/* number (qty) of blocks in file */
+	int64_t di_atime;	/* time last accessed */
+	int64_t di_mtime;	/* time last modified */
+	int64_t di_ctime;	/* time last changed */
+
+	/*  Non-zero only for character or block device nodes  */
+	uint32_t di_major;	/* device major number */
+	uint32_t di_minor;	/* device minor number */
+
+	/*  Block allocation strategy  */
+	uint64_t di_rgrp;	/* dinode rgrp block number */
+	uint64_t di_goal_rgrp;	/* rgrp to alloc from next */
+	uint32_t di_goal_dblk;	/* data block goal */
+	uint32_t di_goal_mblk;	/* metadata block goal */
+
+	uint32_t di_flags;	/* GFS_DIF_... */
+
+	/*  struct gfs_rindex, struct gfs_jindex, or struct gfs_dirent */
+	uint32_t di_payload_format;  /* GFS_FORMAT_... */
+	uint16_t di_type;	/* GFS_FILE_... type of file */
+	uint16_t di_height;	/* height of metadata (0 == stuffed) */
+	uint32_t di_incarn;	/* incarnation (unused, see gfs_meta_header) */
+	uint16_t di_pad;
+
+	/*  These only apply to directories  */
+	uint16_t di_depth;	/* Number of bits in the table */
+	uint32_t di_entries;	/* The # (qty) of entries in the directory */
+
+	/*  This formed an on-disk chain of unused dinodes  */
+	struct gfs_inum di_next_unused;  /* used in old versions only */
+
+	uint64_t di_eattr;	/* extended attribute block number */
+
+	char di_reserved[56];
+};
+
+/*
+ *  indirect block header
+ *
+ *  A component of a dinode's indirect addressing metadata tree.
+ *  These are pointed to by pointers in dinodes or other indirect blocks.
+ */
+
+struct gfs_indirect {
+	struct gfs_meta_header in_header;
+
+	char in_reserved[64];
+};
+
+/*
+ *  directory structure - many of these per directory file
+ *
+ * See comments at beginning of dir.c
+ */
+
+#define GFS_FNAMESIZE               (255)
+#define GFS_DIRENT_SIZE(name_len) ((sizeof(struct gfs_dirent) + (name_len) + 7) & ~7)
+
+struct gfs_dirent {
+	struct gfs_inum de_inum;    /* formal inode number and block address */
+	uint32_t de_hash;           /* hash of the filename */
+	uint16_t de_rec_len;        /* the length of the dirent */
+	uint16_t de_name_len;       /* the length of the name */
+	uint16_t de_type;           /* GFS_FILE_... type of dinode this points to */
+
+	char de_reserved[14];
+};
+
+/*
+ *  Header of leaf directory nodes
+ *
+ * See comments at beginning of dir.c
+ */
+
+struct gfs_leaf {
+	struct gfs_meta_header lf_header;
+
+	uint16_t lf_depth;          /* Depth of leaf */
+	uint16_t lf_entries;        /* Number of dirents in leaf */
+	uint32_t lf_dirent_format;  /* GFS_FORMAT_DE (version #) */
+	uint64_t lf_next;           /* Next leaf, if overflow */
+
+	char lf_reserved[64];
+};
+
+/*
+ *  Log header structure
+ *
+ *  Two of these are in the first block of a transaction log:
+ *    1)  at beginning of block
+ *    2)  at end of first 512-byte sector within block
+ */
+
+#define GFS_LOG_HEAD_UNMOUNT    (0x00000001)  /* log is clean, can unmount fs */
+
+struct gfs_log_header {
+	struct gfs_meta_header lh_header;
+
+	uint32_t lh_flags;	/* GFS_LOG_HEAD_... */
+	uint32_t lh_pad;
+
+	uint64_t lh_first;	/* Block number of first header in this trans */
+	uint64_t lh_sequence;	/* Sequence number of this transaction */
+
+	uint64_t lh_tail;	/* Block number of log tail */
+	uint64_t lh_last_dump;	/* Block number of last dump */
+
+	char lh_reserved[64];
+};
+
+/*
+ *  Log type descriptor
+ *
+ *  One of these for each chunk in a transaction
+ */
+
+#define GFS_LOG_DESC_METADATA   (300)    /* metadata */
+/*  ld_data1 is the number (quantity) of metadata blocks in the descriptor.
+    ld_data2 is unused.
+    */
+
+#define GFS_LOG_DESC_IUL        (400)    /* unlinked inode */
+/*  ld_data1 is TRUE if this is a dump.
+    ld_data2 is unused.
+    FixMe!!!  ld_data1 should be the number (quantity) of entries.
+              ld_data2 should be "TRUE if this is a dump".
+    */
+
+#define GFS_LOG_DESC_IDA        (401)    /* de-allocated inode */
+/*  ld_data1 is unused.
+    ld_data2 is unused.
+    FixMe!!!  ld_data1 should be the number (quantity) of entries.
+    */
+
+#define GFS_LOG_DESC_Q          (402)    /* quota */
+/*  ld_data1 is the number of quota changes in the descriptor.
+    ld_data2 is TRUE if this is a dump.
+    */
+
+#define GFS_LOG_DESC_LAST       (500)    /* final in a logged transaction */
+/*  ld_data1 is unused.
+    ld_data2 is unused.
+    */
+
+struct gfs_log_descriptor {
+	struct gfs_meta_header ld_header;
+
+	uint32_t ld_type;	/* GFS_LOG_DESC_... Type of this log chunk */
+	uint32_t ld_length;	/* Number of buffers in this chunk */
+	uint32_t ld_data1;	/* descriptor-specific field */
+	uint32_t ld_data2;	/* descriptor-specific field */
+
+	char ld_reserved[64];
+};
+
+/*
+ *  Metadata block tags
+ *
+ *  One for each logged block.  Tells where block really belongs on-disk.
+ *  These descriptor tags are packed contiguously after a gfs_log_descriptor.
+ */
+
+struct gfs_block_tag {
+	uint64_t bt_blkno;	/* inplace block number */
+	uint32_t bt_flags;	/* ?? */
+	uint32_t bt_pad;
+};
+
+/*
+ *  Quota Journal Tag
+ */
+
+#define GFS_QTF_USER            (0x00000001)
+
+struct gfs_quota_tag {
+	int64_t qt_change;
+	uint32_t qt_flags;      /* GFS_QTF_... */
+	uint32_t qt_id;
+};
+
+/*
+ *  Extended attribute header format
+ */
+
+#define GFS_EA_MAX_NAME_LEN     (255)
+#define GFS_EA_MAX_DATA_LEN     (65536)
+
+#define GFS_EATYPE_UNUSED       (0)
+#define GFS_EATYPE_USR          (1)     /* user attribute */
+#define GFS_EATYPE_SYS          (2)     /* system attribute */
+
+#define GFS_EATYPE_LAST         (2)
+#define GFS_EATYPE_VALID(x)     ((x) <= GFS_EATYPE_LAST)
+
+#define GFS_EAFLAG_LAST         (0x01)	/* last ea in block */
+
+struct gfs_ea_header {
+	uint32_t ea_rec_len;    /* total record length: hdr + name + data */
+	uint32_t ea_data_len;   /* data length, in bytes */
+	uint8_t ea_name_len;    /* no NULL pointer after the string */
+	uint8_t ea_type;        /* GFS_EATYPE_... */
+	uint8_t ea_flags;       /* GFS_EAFLAG_... */
+	uint8_t ea_num_ptrs;    /* # fs blocks needed for EA */
+	uint32_t ea_pad;
+};
+
+/*  Endian functions  */
+
+#define GFS_ENDIAN_BIG
+
+#ifdef GFS_ENDIAN_BIG
+
+#define gfs16_to_cpu be16_to_cpu
+#define gfs32_to_cpu be32_to_cpu
+#define gfs64_to_cpu be64_to_cpu
+
+#define cpu_to_gfs16 cpu_to_be16
+#define cpu_to_gfs32 cpu_to_be32
+#define cpu_to_gfs64 cpu_to_be64
+
+#else				/*  GFS_ENDIAN_BIG  */
+
+#define gfs16_to_cpu le16_to_cpu
+#define gfs32_to_cpu le32_to_cpu
+#define gfs64_to_cpu le64_to_cpu
+
+#define cpu_to_gfs16 cpu_to_le16
+#define cpu_to_gfs32 cpu_to_le32
+#define cpu_to_gfs64 cpu_to_le64
+
+#endif				/*  GFS_ENDIAN_BIG  */
+
+/*  Translation functions  */
+
+void gfs_inum_in(struct gfs_inum *no, char *buf);
+void gfs_inum_out(struct gfs_inum *no, char *buf);
+void gfs_meta_header_in(struct gfs_meta_header *mh, char *buf);
+void gfs_meta_header_out(struct gfs_meta_header *mh, char *buf);
+void gfs_sb_in(struct gfs_sb *sb, char *buf);
+void gfs_sb_out(struct gfs_sb *sb, char *buf);
+void gfs_jindex_in(struct gfs_jindex *jindex, char *buf);
+void gfs_jindex_out(struct gfs_jindex *jindex, char *buf);
+void gfs_rindex_in(struct gfs_rindex *rindex, char *buf);
+void gfs_rindex_out(struct gfs_rindex *rindex, char *buf);
+void gfs_rgrp_in(struct gfs_rgrp *rgrp, char *buf);
+void gfs_rgrp_out(struct gfs_rgrp *rgrp, char *buf);
+void gfs_quota_in(struct gfs_quota *quota, char *buf);
+void gfs_quota_out(struct gfs_quota *quota, char *buf);
+void gfs_dinode_in(struct gfs_dinode *dinode, char *buf);
+void gfs_dinode_out(struct gfs_dinode *dinode, char *buf);
+void gfs_indirect_in(struct gfs_indirect *indirect, char *buf);
+void gfs_indirect_out(struct gfs_indirect *indirect, char *buf);
+void gfs_dirent_in(struct gfs_dirent *dirent, char *buf);
+void gfs_dirent_out(struct gfs_dirent *dirent, char *buf);
+void gfs_leaf_in(struct gfs_leaf *leaf, char *buf);
+void gfs_leaf_out(struct gfs_leaf *leaf, char *buf);
+void gfs_log_header_in(struct gfs_log_header *head, char *buf);
+void gfs_log_header_out(struct gfs_log_header *head, char *buf);
+void gfs_desc_in(struct gfs_log_descriptor *desc, char *buf);
+void gfs_desc_out(struct gfs_log_descriptor *desc, char *buf);
+void gfs_block_tag_in(struct gfs_block_tag *btag, char *buf);
+void gfs_block_tag_out(struct gfs_block_tag *btag, char *buf);
+void gfs_quota_tag_in(struct gfs_quota_tag *qtag, char *buf);
+void gfs_quota_tag_out(struct gfs_quota_tag *qtag, char *buf);
+void gfs_ea_header_in(struct gfs_ea_header *qtag, char *buf);
+void gfs_ea_header_out(struct gfs_ea_header *qtag, char *buf);
+
+/*  Printing functions  */
+
+void gfs_inum_print(struct gfs_inum *no);
+void gfs_meta_header_print(struct gfs_meta_header *mh);
+void gfs_sb_print(struct gfs_sb *sb);
+void gfs_jindex_print(struct gfs_jindex *jindex);
+void gfs_rindex_print(struct gfs_rindex *rindex);
+void gfs_rgrp_print(struct gfs_rgrp *rgrp);
+void gfs_quota_print(struct gfs_quota *quota);
+void gfs_dinode_print(struct gfs_dinode *dinode);
+void gfs_indirect_print(struct gfs_indirect *indirect);
+void gfs_dirent_print(struct gfs_dirent *dirent, char *name);
+void gfs_leaf_print(struct gfs_leaf *leaf);
+void gfs_log_header_print(struct gfs_log_header *head);
+void gfs_desc_print(struct gfs_log_descriptor *desc);
+void gfs_block_tag_print(struct gfs_block_tag *tag);
+void gfs_quota_tag_print(struct gfs_quota_tag *tag);
+void gfs_ea_header_print(struct gfs_ea_header *ea, char *name);
+
+/*  The hash function for ExHash directories  */
+
+uint32_t gfs_dir_hash(const char *data, int len);
+
+#endif /* __GFS_ONDISK_DOT_H__ */
+
+
+
+#ifdef WANT_GFS_CONVERSION_FUNCTIONS
+
+#define CPIN_08(s1, s2, member, count) {memcpy((s1->member), (s2->member), (count));}
+#define CPOUT_08(s1, s2, member, count) {memcpy((s2->member), (s1->member), (count));}
+#define CPIN_16(s1, s2, member) {(s1->member) = gfs16_to_cpu((s2->member));}
+#define CPOUT_16(s1, s2, member) {(s2->member) = cpu_to_gfs16((s1->member));}
+#define CPIN_32(s1, s2, member) {(s1->member) = gfs32_to_cpu((s2->member));}
+#define CPOUT_32(s1, s2, member) {(s2->member) = cpu_to_gfs32((s1->member));}
+#define CPIN_64(s1, s2, member) {(s1->member) = gfs64_to_cpu((s2->member));}
+#define CPOUT_64(s1, s2, member) {(s2->member) = cpu_to_gfs64((s1->member));}
+
+#define pa(struct, member, count) print_array(#member, struct->member, count);
+
+/**
+ * print_array - Print out an array of bytes
+ * @title: what to print before the array
+ * @buf: the array
+ * @count: the number of bytes
+ *
+ */
+
+static void
+print_array(char *title, char *buf, int count)
+{
+	int x;
+
+	printk("  %s =\n", title);
+	for (x = 0; x < count; x++) {
+		printk("%.2X ", (unsigned char)buf[x]);
+		if (x % 16 == 15)
+			printk("\n");
+	}
+	if (x % 16)
+		printk("\n");
+}
+
+/**
+ * gfs_inum_in - Read in an inode number
+ * @no: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_inum_in(struct gfs_inum *no, char *buf)
+{
+	struct gfs_inum *str = (struct gfs_inum *)buf;
+
+	CPIN_64(no, str, no_formal_ino);
+	CPIN_64(no, str, no_addr);
+}
+
+/**
+ * gfs_inum_out - Write out an inode number
+ * @no: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_inum_out(struct gfs_inum *no, char *buf)
+{
+	struct gfs_inum *str = (struct gfs_inum *)buf;
+
+	CPOUT_64(no, str, no_formal_ino);
+	CPOUT_64(no, str, no_addr);
+}
+
+/**
+ * gfs_inum_print - Print out a inode number
+ * @no: the cpu-order buffer
+ *
+ */
+
+void
+gfs_inum_print(struct gfs_inum *no)
+{
+	pv(no, no_formal_ino, "%"PRIu64);
+	pv(no, no_addr, "%"PRIu64);
+}
+
+/**
+ * gfs_meta_header_in - Read in a metadata header
+ * @mh: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_meta_header_in(struct gfs_meta_header *mh, char *buf)
+{
+	struct gfs_meta_header *str = (struct gfs_meta_header *)buf;
+
+	CPIN_32(mh, str, mh_magic);
+	CPIN_32(mh, str, mh_type);
+	CPIN_64(mh, str, mh_generation);
+	CPIN_32(mh, str, mh_format);
+	CPIN_32(mh, str, mh_incarn);
+}
+
+/**
+ * gfs_meta_header_in - Write out a metadata header
+ * @mh: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ * Don't ever change the generation number in this routine.
+ * It's done manually in increment_generation().
+ */
+
+void
+gfs_meta_header_out(struct gfs_meta_header *mh, char *buf)
+{
+	struct gfs_meta_header *str = (struct gfs_meta_header *)buf;
+
+	CPOUT_32(mh, str, mh_magic);
+	CPOUT_32(mh, str, mh_type);
+#if 0
+	/* Don't do this!
+	   Mh_generation should only be change manually. */
+	CPOUT_64(mh, str, mh_generation);
+#endif
+	CPOUT_32(mh, str, mh_format);
+	CPOUT_32(mh, str, mh_incarn);
+}
+
+/**
+ * gfs_meta_header_print - Print out a metadata header
+ * @mh: the cpu-order buffer
+ *
+ */
+
+void
+gfs_meta_header_print(struct gfs_meta_header *mh)
+{
+	pv(mh, mh_magic, "0x%.8X");
+	pv(mh, mh_type, "%u");
+	pv(mh, mh_generation, "%"PRIu64);
+	pv(mh, mh_format, "%u");
+	pv(mh, mh_incarn, "%u");
+}
+
+/**
+ * gfs_sb_in - Read in a superblock
+ * @sb: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_sb_in(struct gfs_sb *sb, char *buf)
+{
+	struct gfs_sb *str = (struct gfs_sb *)buf;
+
+	gfs_meta_header_in(&sb->sb_header, buf);
+
+	CPIN_32(sb, str, sb_fs_format);
+	CPIN_32(sb, str, sb_multihost_format);
+	CPIN_32(sb, str, sb_flags);
+
+	CPIN_32(sb, str, sb_bsize);
+	CPIN_32(sb, str, sb_bsize_shift);
+	CPIN_32(sb, str, sb_seg_size);
+
+	gfs_inum_in(&sb->sb_jindex_di, (char *)&str->sb_jindex_di);
+	gfs_inum_in(&sb->sb_rindex_di, (char *)&str->sb_rindex_di);
+	gfs_inum_in(&sb->sb_root_di, (char *)&str->sb_root_di);
+
+	CPIN_08(sb, str, sb_lockproto, GFS_LOCKNAME_LEN);
+	CPIN_08(sb, str, sb_locktable, GFS_LOCKNAME_LEN);
+
+	gfs_inum_in(&sb->sb_quota_di, (char *)&str->sb_quota_di);
+	gfs_inum_in(&sb->sb_license_di, (char *)&str->sb_license_di);
+
+	CPIN_08(sb, str, sb_reserved, 96);
+}
+
+/**
+ * gfs_sb_out - Write out a superblock
+ * @sb: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_sb_out(struct gfs_sb *sb, char *buf)
+{
+	struct gfs_sb *str = (struct gfs_sb *)buf;
+
+	gfs_meta_header_out(&sb->sb_header, buf);
+
+	CPOUT_32(sb, str, sb_fs_format);
+	CPOUT_32(sb, str, sb_multihost_format);
+	CPOUT_32(sb, str, sb_flags);
+
+	CPOUT_32(sb, str, sb_bsize);
+	CPOUT_32(sb, str, sb_bsize_shift);
+	CPOUT_32(sb, str, sb_seg_size);
+
+	gfs_inum_out(&sb->sb_jindex_di, (char *)&str->sb_jindex_di);
+	gfs_inum_out(&sb->sb_rindex_di, (char *)&str->sb_rindex_di);
+	gfs_inum_out(&sb->sb_root_di, (char *)&str->sb_root_di);
+
+	CPOUT_08(sb, str, sb_lockproto, GFS_LOCKNAME_LEN);
+	CPOUT_08(sb, str, sb_locktable, GFS_LOCKNAME_LEN);
+
+	gfs_inum_out(&sb->sb_quota_di, (char *)&str->sb_quota_di);
+	gfs_inum_out(&sb->sb_license_di, (char *)&str->sb_license_di);
+
+	CPOUT_08(sb, str, sb_reserved, 96);
+}
+
+/**
+ * gfs_sb_print - Print out a superblock
+ * @sb: the cpu-order buffer
+ *
+ */
+
+void
+gfs_sb_print(struct gfs_sb *sb)
+{
+	gfs_meta_header_print(&sb->sb_header);
+
+	pv(sb, sb_fs_format, "%u");
+	pv(sb, sb_multihost_format, "%u");
+	pv(sb, sb_flags, "%u");
+
+	pv(sb, sb_bsize, "%u");
+	pv(sb, sb_bsize_shift, "%u");
+	pv(sb, sb_seg_size, "%u");
+
+	gfs_inum_print(&sb->sb_jindex_di);
+	gfs_inum_print(&sb->sb_rindex_di);
+	gfs_inum_print(&sb->sb_root_di);
+
+	pv(sb, sb_lockproto, "%s");
+	pv(sb, sb_locktable, "%s");
+
+	gfs_inum_print(&sb->sb_quota_di);
+	gfs_inum_print(&sb->sb_license_di);
+
+	pa(sb, sb_reserved, 96);
+}
+
+/**
+ * gfs_jindex_in - Read in a journal index structure
+ * @jindex: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_jindex_in(struct gfs_jindex *jindex, char *buf)
+{
+	struct gfs_jindex *str = (struct gfs_jindex *)buf;
+
+	CPIN_64(jindex, str, ji_addr);
+	CPIN_32(jindex, str, ji_nsegment);
+	CPIN_32(jindex, str, ji_pad);
+
+	CPIN_08(jindex, str, ji_reserved, 64);
+}
+
+/**
+ * gfs_jindex_out - Write out a journal index structure
+ * @jindex: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_jindex_out(struct gfs_jindex *jindex, char *buf)
+{
+	struct gfs_jindex *str = (struct gfs_jindex *)buf;
+
+	CPOUT_64(jindex, str, ji_addr);
+	CPOUT_32(jindex, str, ji_nsegment);
+	CPOUT_32(jindex, str, ji_pad);
+
+	CPOUT_08(jindex, str, ji_reserved, 64);
+}
+
+/**
+ * gfs_jindex_print - Print out a journal index structure
+ * @ji: the cpu-order buffer
+ *
+ */
+
+void
+gfs_jindex_print(struct gfs_jindex *ji)
+{
+	pv(ji, ji_addr, "%"PRIu64);
+	pv(ji, ji_nsegment, "%u");
+	pv(ji, ji_pad, "%u");
+
+	pa(ji, ji_reserved, 64);
+}
+
+/**
+ * gfs_rindex_in - Read in a resource index structure
+ * @rindex: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_rindex_in(struct gfs_rindex *rindex, char *buf)
+{
+	struct gfs_rindex *str = (struct gfs_rindex *)buf;
+
+	CPIN_64(rindex, str, ri_addr);
+	CPIN_32(rindex, str, ri_length);
+	CPIN_32(rindex, str, ri_pad);
+
+	CPIN_64(rindex, str, ri_data1);
+	CPIN_32(rindex, str, ri_data);
+
+	CPIN_32(rindex, str, ri_bitbytes);
+
+	CPIN_08(rindex, str, ri_reserved, 64);
+}
+
+/**
+ * gfs_rindex_out - Write out a resource index structure
+ * @rindex: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_rindex_out(struct gfs_rindex *rindex, char *buf)
+{
+	struct gfs_rindex *str = (struct gfs_rindex *)buf;
+
+	CPOUT_64(rindex, str, ri_addr);
+	CPOUT_32(rindex, str, ri_length);
+	CPOUT_32(rindex, str, ri_pad);
+
+	CPOUT_64(rindex, str, ri_data1);
+	CPOUT_32(rindex, str, ri_data);
+
+	CPOUT_32(rindex, str, ri_bitbytes);
+
+	CPOUT_08(rindex, str, ri_reserved, 64);
+}
+
+/**
+ * gfs_rindex_print - Print out a resource index structure
+ * @ri: the cpu-order buffer
+ *
+ */
+
+void
+gfs_rindex_print(struct gfs_rindex *ri)
+{
+	pv(ri, ri_addr, "%"PRIu64);
+	pv(ri, ri_length, "%u");
+	pv(ri, ri_pad, "%u");
+
+	pv(ri, ri_data1, "%"PRIu64);
+	pv(ri, ri_data, "%u");
+
+	pv(ri, ri_bitbytes, "%u");
+
+	pa(ri, ri_reserved, 64);
+}
+
+/**
+ * gfs_rgrp_in - Read in a resource group header
+ * @rgrp: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_rgrp_in(struct gfs_rgrp *rgrp, char *buf)
+{
+	struct gfs_rgrp *str = (struct gfs_rgrp *)buf;
+
+	gfs_meta_header_in(&rgrp->rg_header, buf);
+
+	CPIN_32(rgrp, str, rg_flags);
+
+	CPIN_32(rgrp, str, rg_free);
+
+	CPIN_32(rgrp, str, rg_useddi);
+	CPIN_32(rgrp, str, rg_freedi);
+	gfs_inum_in(&rgrp->rg_freedi_list, (char *)&str->rg_freedi_list);
+
+	CPIN_32(rgrp, str, rg_usedmeta);
+	CPIN_32(rgrp, str, rg_freemeta);
+
+	CPIN_08(rgrp, str, rg_reserved, 64);
+}
+
+/**
+ * gfs_rgrp_out - Write out a resource group header
+ * @rgrp: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_rgrp_out(struct gfs_rgrp *rgrp, char *buf)
+{
+	struct gfs_rgrp *str = (struct gfs_rgrp *)buf;
+
+	gfs_meta_header_out(&rgrp->rg_header, buf);
+
+	CPOUT_32(rgrp, str, rg_flags);
+
+	CPOUT_32(rgrp, str, rg_free);
+
+	CPOUT_32(rgrp, str, rg_useddi);
+	CPOUT_32(rgrp, str, rg_freedi);
+	gfs_inum_out(&rgrp->rg_freedi_list, (char *)&str->rg_freedi_list);
+
+	CPOUT_32(rgrp, str, rg_usedmeta);
+	CPOUT_32(rgrp, str, rg_freemeta);
+
+	CPOUT_08(rgrp, str, rg_reserved, 64);
+}
+
+/**
+ * gfs_rgrp_print - Print out a resource group header
+ * @rg: the cpu-order buffer
+ *
+ */
+
+void
+gfs_rgrp_print(struct gfs_rgrp *rg)
+{
+	gfs_meta_header_print(&rg->rg_header);
+
+	pv(rg, rg_flags, "%u");
+
+	pv(rg, rg_free, "%u");
+
+	pv(rg, rg_useddi, "%u");
+	pv(rg, rg_freedi, "%u");
+	gfs_inum_print(&rg->rg_freedi_list);
+
+	pv(rg, rg_usedmeta, "%u");
+	pv(rg, rg_freemeta, "%u");
+
+	pa(rg, rg_reserved, 64);
+}
+
+/**
+ * gfs_quota_in - Read in a quota structures
+ * @quota: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_quota_in(struct gfs_quota *quota, char *buf)
+{
+	struct gfs_quota *str = (struct gfs_quota *)buf;
+
+	CPIN_64(quota, str, qu_limit);
+	CPIN_64(quota, str, qu_warn);
+	CPIN_64(quota, str, qu_value);
+
+	CPIN_08(quota, str, qu_reserved, 64);
+}
+
+/**
+ * gfs_quota_out - Write out a quota structure
+ * @quota: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_quota_out(struct gfs_quota *quota, char *buf)
+{
+	struct gfs_quota *str = (struct gfs_quota *)buf;
+
+	CPOUT_64(quota, str, qu_limit);
+	CPOUT_64(quota, str, qu_warn);
+	CPOUT_64(quota, str, qu_value);
+
+	CPOUT_08(quota, str, qu_reserved, 64);
+}
+
+/**
+ * gfs_quota_print - Print out a quota structure
+ * @quota: the cpu-order buffer
+ *
+ */
+
+void
+gfs_quota_print(struct gfs_quota *quota)
+{
+	pv(quota, qu_limit, "%"PRIu64);
+	pv(quota, qu_warn, "%"PRIu64);
+	pv(quota, qu_value, "%"PRId64);
+
+	pa(quota, qu_reserved, 64);
+}
+
+/**
+ * gfs_dinode_in - Read in a dinode
+ * @dinode: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_dinode_in(struct gfs_dinode *dinode, char *buf)
+{
+	struct gfs_dinode *str = (struct gfs_dinode *)buf;
+
+	gfs_meta_header_in(&dinode->di_header, buf);
+
+	gfs_inum_in(&dinode->di_num, (char *)&str->di_num);
+
+	CPIN_32(dinode, str, di_mode);
+	CPIN_32(dinode, str, di_uid);
+	CPIN_32(dinode, str, di_gid);
+	CPIN_32(dinode, str, di_nlink);
+	CPIN_64(dinode, str, di_size);
+	CPIN_64(dinode, str, di_blocks);
+	CPIN_64(dinode, str, di_atime);
+	CPIN_64(dinode, str, di_mtime);
+	CPIN_64(dinode, str, di_ctime);
+	CPIN_32(dinode, str, di_major);
+	CPIN_32(dinode, str, di_minor);
+
+	CPIN_64(dinode, str, di_rgrp);
+	CPIN_64(dinode, str, di_goal_rgrp);
+	CPIN_32(dinode, str, di_goal_dblk);
+	CPIN_32(dinode, str, di_goal_mblk);
+	CPIN_32(dinode, str, di_flags);
+	CPIN_32(dinode, str, di_payload_format);
+	CPIN_16(dinode, str, di_type);
+	CPIN_16(dinode, str, di_height);
+	CPIN_32(dinode, str, di_incarn);
+	CPIN_16(dinode, str, di_pad);
+
+	CPIN_16(dinode, str, di_depth);
+	CPIN_32(dinode, str, di_entries);
+
+	gfs_inum_in(&dinode->di_next_unused, (char *)&str->di_next_unused);
+
+	CPIN_64(dinode, str, di_eattr);
+
+	CPIN_08(dinode, str, di_reserved, 56);
+}
+
+/**
+ * gfs_dinode_out - Write out a dinode
+ * @dinode: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_dinode_out(struct gfs_dinode *dinode, char *buf)
+{
+	struct gfs_dinode *str = (struct gfs_dinode *)buf;
+
+	gfs_meta_header_out(&dinode->di_header, buf);
+
+	gfs_inum_out(&dinode->di_num, (char *)&str->di_num);
+
+	CPOUT_32(dinode, str, di_mode);
+	CPOUT_32(dinode, str, di_uid);
+	CPOUT_32(dinode, str, di_gid);
+	CPOUT_32(dinode, str, di_nlink);
+	CPOUT_64(dinode, str, di_size);
+	CPOUT_64(dinode, str, di_blocks);
+	CPOUT_64(dinode, str, di_atime);
+	CPOUT_64(dinode, str, di_mtime);
+	CPOUT_64(dinode, str, di_ctime);
+	CPOUT_32(dinode, str, di_major);
+	CPOUT_32(dinode, str, di_minor);
+
+	CPOUT_64(dinode, str, di_rgrp);
+	CPOUT_64(dinode, str, di_goal_rgrp);
+	CPOUT_32(dinode, str, di_goal_dblk);
+	CPOUT_32(dinode, str, di_goal_mblk);
+	CPOUT_32(dinode, str, di_flags);
+	CPOUT_32(dinode, str, di_payload_format);
+	CPOUT_16(dinode, str, di_type);
+	CPOUT_16(dinode, str, di_height);
+	CPOUT_32(dinode, str, di_incarn);
+	CPOUT_16(dinode, str, di_pad);
+
+	CPOUT_16(dinode, str, di_depth);
+	CPOUT_32(dinode, str, di_entries);
+
+	gfs_inum_out(&dinode->di_next_unused, (char *)&str->di_next_unused);
+
+	CPOUT_64(dinode, str, di_eattr);
+
+	CPOUT_08(dinode, str, di_reserved, 56);
+}
+
+/**
+ * gfs_dinode_print - Print out a dinode
+ * @di: the cpu-order buffer
+ *
+ */
+
+void
+gfs_dinode_print(struct gfs_dinode *di)
+{
+	gfs_meta_header_print(&di->di_header);
+
+	gfs_inum_print(&di->di_num);
+
+	pv(di, di_mode, "0%o");
+	pv(di, di_uid, "%u");
+	pv(di, di_gid, "%u");
+	pv(di, di_nlink, "%u");
+	pv(di, di_size, "%"PRIu64);
+	pv(di, di_blocks, "%"PRIu64);
+	pv(di, di_atime, "%"PRId64);
+	pv(di, di_mtime, "%"PRId64);
+	pv(di, di_ctime, "%"PRId64);
+	pv(di, di_major, "%u");
+	pv(di, di_minor, "%u");
+
+	pv(di, di_rgrp, "%"PRIu64);
+	pv(di, di_goal_rgrp, "%"PRIu64);
+	pv(di, di_goal_dblk, "%u");
+	pv(di, di_goal_mblk, "%u");
+	pv(di, di_flags, "0x%.8X");
+	pv(di, di_payload_format, "%u");
+	pv(di, di_type, "%u");
+	pv(di, di_height, "%u");
+	pv(di, di_incarn, "%u");
+	pv(di, di_pad, "%u");
+
+	pv(di, di_depth, "%u");
+	pv(di, di_entries, "%u");
+
+	gfs_inum_print(&di->di_next_unused);
+
+	pv(di, di_eattr, "%"PRIu64);
+
+	pa(di, di_reserved, 56);
+}
+
+/**
+ * gfs_indirect_in - copy in the header of an indirect block
+ * @indirect: the in memory copy
+ * @buf: the buffer copy
+ *
+ */
+
+void
+gfs_indirect_in(struct gfs_indirect *indirect, char *buf)
+{
+	struct gfs_indirect *str = (struct gfs_indirect *)buf;
+
+	gfs_meta_header_in(&indirect->in_header, buf);
+
+	CPIN_08(indirect, str, in_reserved, 64);
+}
+
+/**
+ * gfs_indirect_out - copy out the header of an indirect block
+ * @indirect: the in memory copy
+ * @buf: the buffer copy
+ *
+ */
+
+void
+gfs_indirect_out(struct gfs_indirect *indirect, char *buf)
+{
+	struct gfs_indirect *str = (struct gfs_indirect *)buf;
+
+	gfs_meta_header_out(&indirect->in_header, buf);
+
+	CPOUT_08(indirect, str, in_reserved, 64);
+}
+
+/**
+ * gfs_indirect_print - Print out a indirect block header
+ * @indirect: the cpu-order buffer
+ *
+ */
+
+void
+gfs_indirect_print(struct gfs_indirect *indirect)
+{
+	gfs_meta_header_print(&indirect->in_header);
+
+	pa(indirect, in_reserved, 64);
+}
+
+/**
+ * gfs_dirent_in - Read in a directory entry
+ * @dirent: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_dirent_in(struct gfs_dirent *dirent, char *buf)
+{
+	struct gfs_dirent *str = (struct gfs_dirent *)buf;
+
+	gfs_inum_in(&dirent->de_inum, (char *)&str->de_inum);
+	CPIN_32(dirent, str, de_hash);
+	CPIN_16(dirent, str, de_rec_len);
+	CPIN_16(dirent, str, de_name_len);
+	CPIN_16(dirent, str, de_type);
+
+	CPIN_08(dirent, str, de_reserved, 14);
+}
+
+/**
+ * gfs_dirent_out - Write out a directory entry
+ * @dirent: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_dirent_out(struct gfs_dirent *dirent, char *buf)
+{
+	struct gfs_dirent *str = (struct gfs_dirent *)buf;
+
+	gfs_inum_out(&dirent->de_inum, (char *)&str->de_inum);
+	CPOUT_32(dirent, str, de_hash);
+	CPOUT_16(dirent, str, de_rec_len);
+	CPOUT_16(dirent, str, de_name_len);
+	CPOUT_16(dirent, str, de_type);
+
+	CPOUT_08(dirent, str, de_reserved, 14);
+}
+
+/**
+ * gfs_dirent_print - Print out a directory entry
+ * @de: the cpu-order buffer
+ * @name: the filename
+ *
+ */
+
+void
+gfs_dirent_print(struct gfs_dirent *de, char *name)
+{
+	char buf[GFS_FNAMESIZE + 1];
+
+	gfs_inum_print(&de->de_inum);
+	pv(de, de_hash, "0x%.8X");
+	pv(de, de_rec_len, "%u");
+	pv(de, de_name_len, "%u");
+	pv(de, de_type, "%u");
+
+	pa(de, de_reserved, 14);
+
+	memset(buf, 0, GFS_FNAMESIZE + 1);
+	memcpy(buf, name, de->de_name_len);
+	printk("  name = %s\n", buf);
+}
+
+/**
+ * gfs_leaf_in - Read in a directory leaf header
+ * @leaf: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_leaf_in(struct gfs_leaf *leaf, char *buf)
+{
+	struct gfs_leaf *str = (struct gfs_leaf *)buf;
+
+	gfs_meta_header_in(&leaf->lf_header, buf);
+
+	CPIN_16(leaf, str, lf_depth);
+	CPIN_16(leaf, str, lf_entries);
+	CPIN_32(leaf, str, lf_dirent_format);
+	CPIN_64(leaf, str, lf_next);
+
+	CPIN_08(leaf, str, lf_reserved, 64);
+}
+
+/**
+ * gfs_leaf_out - Write out a directory leaf header
+ * @leaf: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_leaf_out(struct gfs_leaf *leaf, char *buf)
+{
+	struct gfs_leaf *str = (struct gfs_leaf *)buf;
+
+	gfs_meta_header_out(&leaf->lf_header, buf);
+
+	CPOUT_16(leaf, str, lf_depth);
+	CPOUT_16(leaf, str, lf_entries);
+	CPOUT_32(leaf, str, lf_dirent_format);
+	CPOUT_64(leaf, str, lf_next);
+
+	CPOUT_08(leaf, str, lf_reserved, 64);
+}
+
+/**
+ * gfs_leaf_print - Print out a directory leaf header
+ * @lf: the cpu-order buffer
+ *
+ */
+
+void
+gfs_leaf_print(struct gfs_leaf *lf)
+{
+	gfs_meta_header_print(&lf->lf_header);
+
+	pv(lf, lf_depth, "%u");
+	pv(lf, lf_entries, "%u");
+	pv(lf, lf_dirent_format, "%u");
+	pv(lf, lf_next, "%"PRIu64);
+
+	pa(lf, lf_reserved, 64);
+}
+
+/**
+ * gfs_log_header_in - Read in a log header
+ * @head: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_log_header_in(struct gfs_log_header *head, char *buf)
+{
+	struct gfs_log_header *str = (struct gfs_log_header *)buf;
+
+	gfs_meta_header_in(&head->lh_header, buf);
+
+	CPIN_32(head, str, lh_flags);
+	CPIN_32(head, str, lh_pad);
+
+	CPIN_64(head, str, lh_first);
+	CPIN_64(head, str, lh_sequence);
+
+	CPIN_64(head, str, lh_tail);
+	CPIN_64(head, str, lh_last_dump);
+
+	CPIN_08(head, str, lh_reserved, 64);
+}
+
+/**
+ * gfs_log_header_out - Write out a log header
+ * @head: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_log_header_out(struct gfs_log_header *head, char *buf)
+{
+	struct gfs_log_header *str = (struct gfs_log_header *)buf;
+
+	gfs_meta_header_out(&head->lh_header, buf);
+
+	CPOUT_32(head, str, lh_flags);
+	CPOUT_32(head, str, lh_pad);
+
+	CPOUT_64(head, str, lh_first);
+	CPOUT_64(head, str, lh_sequence);
+
+	CPOUT_64(head, str, lh_tail);
+	CPOUT_64(head, str, lh_last_dump);
+
+	CPOUT_08(head, str, lh_reserved, 64);
+}
+
+/**
+ * gfs_log_header_print - Print out a log header
+ * @head: the cpu-order buffer
+ *
+ */
+
+void
+gfs_log_header_print(struct gfs_log_header *lh)
+{
+	gfs_meta_header_print(&lh->lh_header);
+
+	pv(lh, lh_flags, "0x%.8X");
+	pv(lh, lh_pad, "%u");
+
+	pv(lh, lh_first, "%"PRIu64);
+	pv(lh, lh_sequence, "%"PRIu64);
+
+	pv(lh, lh_tail, "%"PRIu64);
+	pv(lh, lh_last_dump, "%"PRIu64);
+
+	pa(lh, lh_reserved, 64);
+}
+
+/**
+ * gfs_desc_in - Read in a log descriptor
+ * @desc: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_desc_in(struct gfs_log_descriptor *desc, char *buf)
+{
+	struct gfs_log_descriptor *str = (struct gfs_log_descriptor *)buf;
+
+	gfs_meta_header_in(&desc->ld_header, buf);
+
+	CPIN_32(desc, str, ld_type);
+	CPIN_32(desc, str, ld_length);
+	CPIN_32(desc, str, ld_data1);
+	CPIN_32(desc, str, ld_data2);
+
+	CPIN_08(desc, str, ld_reserved, 64);
+}
+
+/**
+ * gfs_desc_out - Write out a log descriptor
+ * @desc: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_desc_out(struct gfs_log_descriptor *desc, char *buf)
+{
+	struct gfs_log_descriptor *str = (struct gfs_log_descriptor *)buf;
+
+	gfs_meta_header_out(&desc->ld_header, buf);
+
+	CPOUT_32(desc, str, ld_type);
+	CPOUT_32(desc, str, ld_length);
+	CPOUT_32(desc, str, ld_data1);
+	CPOUT_32(desc, str, ld_data2);
+
+	CPOUT_08(desc, str, ld_reserved, 64);
+}
+
+/**
+ * gfs_desc_print - Print out a log descriptor
+ * @ld: the cpu-order buffer
+ *
+ */
+
+void
+gfs_desc_print(struct gfs_log_descriptor *ld)
+{
+	gfs_meta_header_print(&ld->ld_header);
+
+	pv(ld, ld_type, "%u");
+	pv(ld, ld_length, "%u");
+	pv(ld, ld_data1, "%u");
+	pv(ld, ld_data2, "%u");
+
+	pa(ld, ld_reserved, 64);
+}
+
+/**
+ * gfs_block_tag_in - Read in a block tag
+ * @tag: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_block_tag_in(struct gfs_block_tag *tag, char *buf)
+{
+	struct gfs_block_tag *str = (struct gfs_block_tag *)buf;
+
+	CPIN_64(tag, str, bt_blkno);
+	CPIN_32(tag, str, bt_flags);
+	CPIN_32(tag, str, bt_pad);
+}
+
+/**
+ * gfs_block_tag_out - Write out a block tag
+ * @tag: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_block_tag_out(struct gfs_block_tag *tag, char *buf)
+{
+	struct gfs_block_tag *str = (struct gfs_block_tag *)buf;
+
+	CPOUT_64(tag, str, bt_blkno);
+	CPOUT_32(tag, str, bt_flags);
+	CPOUT_32(tag, str, bt_pad);
+}
+
+/**
+ * gfs_block_tag_print - Print out a block tag
+ * @tag: the cpu-order buffer
+ *
+ */
+
+void
+gfs_block_tag_print(struct gfs_block_tag *tag)
+{
+	pv(tag, bt_blkno, "%"PRIu64);
+	pv(tag, bt_flags, "%u");
+	pv(tag, bt_pad, "%u");
+}
+
+/**
+ * gfs_quota_tag_in - Read in a quota tag
+ * @tag: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_quota_tag_in(struct gfs_quota_tag *tag, char *buf)
+{
+	struct gfs_quota_tag *str = (struct gfs_quota_tag *)buf;
+
+	CPIN_64(tag, str, qt_change);
+	CPIN_32(tag, str, qt_flags);
+	CPIN_32(tag, str, qt_id);
+}
+
+/**
+ * gfs_quota_tag_out - Write out a quota tag
+ * @tag: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_quota_tag_out(struct gfs_quota_tag *tag, char *buf)
+{
+	struct gfs_quota_tag *str = (struct gfs_quota_tag *)buf;
+
+	CPOUT_64(tag, str, qt_change);
+	CPOUT_32(tag, str, qt_flags);
+	CPOUT_32(tag, str, qt_id);
+}
+
+/**
+ * gfs_quota_tag_print - Print out a quota tag
+ * @tag: the cpu-order buffer
+ *
+ */
+
+void
+gfs_quota_tag_print(struct gfs_quota_tag *tag)
+{
+	pv(tag, qt_change, "%"PRId64);
+	pv(tag, qt_flags, "0x%.8X");
+	pv(tag, qt_id, "%u");
+}
+
+/**
+ * gfs_ea_header_in - Read in a Extended Attribute header
+ * @tag: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_ea_header_in(struct gfs_ea_header *ea, char *buf)
+{
+	struct gfs_ea_header *str = (struct gfs_ea_header *)buf;
+
+	CPIN_32(ea, str, ea_rec_len);
+	CPIN_32(ea, str, ea_data_len);
+	ea->ea_name_len = str->ea_name_len;
+	ea->ea_type = str->ea_type;
+	ea->ea_flags = str->ea_flags;
+	ea->ea_num_ptrs = str->ea_num_ptrs;
+	CPIN_32(ea, str, ea_pad);
+}
+
+/**
+ * gfs_ea_header_out - Write out a Extended Attribute header
+ * @ea: the cpu-order structure
+ * @buf: the disk-order buffer
+ *
+ */
+
+void
+gfs_ea_header_out(struct gfs_ea_header *ea, char *buf)
+{
+	struct gfs_ea_header *str = (struct gfs_ea_header *)buf;
+
+	CPOUT_32(ea, str, ea_rec_len);
+	CPOUT_32(ea, str, ea_data_len);
+	str->ea_name_len = ea->ea_name_len;
+	str->ea_type = ea->ea_type;
+	str->ea_flags = ea->ea_flags;
+	str->ea_num_ptrs = ea->ea_num_ptrs;
+	CPOUT_32(ea, str, ea_pad);
+}
+
+/**
+ * gfs_ea_header_printt - Print out a Extended Attribute header
+ * @ea: the cpu-order buffer
+ *
+ */
+
+void
+gfs_ea_header_print(struct gfs_ea_header *ea, char *name)
+{
+	char buf[GFS_EA_MAX_NAME_LEN + 1];
+
+	pv(ea, ea_rec_len, "%u");
+	pv(ea, ea_data_len, "%u");
+	pv(ea, ea_name_len, "%u");
+	pv(ea, ea_type, "%u");
+	pv(ea, ea_flags, "%u");
+	pv(ea, ea_num_ptrs, "%u");
+	pv(ea, ea_pad, "%u");
+
+	memset(buf, 0, GFS_EA_MAX_NAME_LEN + 1);
+	memcpy(buf, name, ea->ea_name_len);
+	printk("  name = %s\n", buf);
+}
+
+static const uint32_t crc_32_tab[] =
+{
+  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+  0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+  0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+  0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+  0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+  0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+  0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+  0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+  0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+  0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+  0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+  0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+  0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+  0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+  0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+  0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+  0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+  0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+  0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+  0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+  0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+  0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+/**
+ * gfs_dir_hash - hash an array of data
+ * @data: the data to be hashed
+ * @len: the length of data to be hashed
+ *
+ * Take some data and convert it to a 32-bit hash.
+ *
+ * The hash function is a 32-bit CRC of the data.  The algorithm uses
+ * the crc_32_tab table above.
+ *
+ * This may not be the fastest hash function, but it does a fair bit better
+ * at providing uniform results than the others I've looked at.  That's
+ * really important for efficient directories.
+ *
+ * Returns: the hash
+ */
+
+uint32_t
+gfs_dir_hash(const char *data, int len)
+{
+	uint32_t hash = 0xFFFFFFFF;
+
+	for (; len--; data++)
+		hash = crc_32_tab[(hash ^ *data) & 0xFF] ^ (hash >> 8);
+
+	hash = ~hash;
+
+	return hash;
+}
+
+#endif  /* WANT_GFS_CONVERSION_FUNCTIONS */
+
diff -pruN linux-2.6.9.orig/include/linux/lm_interface.h linux-2.6.9.debug/include/linux/lm_interface.h
--- linux-2.6.9.orig/include/linux/lm_interface.h	1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.9.debug/include/linux/lm_interface.h	2006-12-20 17:08:00.000000000 +0300
@@ -0,0 +1,247 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+/*
+
+   Sooner or later, I need to put all the documentation back into this file.
+   In the mean time, here are some notes.
+
+   -  The lock module is now responsible for STOMITHing the an expired
+   client before calling the callback with type LM_CB_NEED_RECOVERY.
+
+   -  If mount() operation returns first == TRUE, GFS will check all the
+   journals.  GFS itself can't/shouldn't stomith the machines, so the lock module
+   needs to make sure that there are no zombie machines on any of the
+   journals.  (i.e. this should probably be on the first mount of the lock
+   space where all mounts by other machines are blocked.)  GFS will call
+   others_may_mount() when the filesystem is in a consistent state.
+
+   -  GFS can issue multiple simultaneous get_lock()s for the same lockname.
+   The lock module needs to deal with it, either by 1)  building a hash table
+   to lookup the structures and keeping a reference count so there is only
+   on lm_lock_t for a given lockname. or 2) just dealing with multiple 
+   lm_lock_t structures for a given lockname.
+
+*/
+
+#ifndef __LM_INTERFACE_DOT_H__
+#define __LM_INTERFACE_DOT_H__
+
+/*
+ * Lock-module- or filesystem-specific opaque handles for various things.
+ * Lock module gives lockspace and lock handles to GFS, which uses them
+ *   to identify LM instance and lock when calling LM functions.
+ * GFS gives fsdata to lock module, so LM can identify filesystem instance
+ *   when doing callbacks to GFS.
+ */
+typedef void lm_lockspace_t;  /* lockspace; lock module instance structure */
+typedef void lm_lock_t;       /* lock module's internal lock structure */
+typedef void lm_fsdata_t;     /* filesystem; GFS instance (superblock struct) */
+
+typedef void (*lm_callback_t) (lm_fsdata_t *fsdata, unsigned int type,
+			       void *data);
+
+/*
+ * Flags for the struct lm_lockstruct->ls_flags field.
+ * The nolock module is useful for single-node mounting of GFS; it sets the
+ *   LOCAL flag to allow GFS to perform caching and other optimizations that
+ *   it cannot do when in a cluster.
+ */
+
+#define LM_LSFLAG_LOCAL        (0x00000001) /* Local filesystem (no locks) */
+
+/* Lock types */
+
+#define LM_TYPE_RESERVED       (0x00)
+#define LM_TYPE_NONDISK        (0x01)  /* Non-disk cluster-wide, e.g. TRANS */
+#define LM_TYPE_INODE          (0x02)  /* Inode, e.g. files */
+#define LM_TYPE_RGRP           (0x03)  /* Resource Group (block allocation) */
+#define LM_TYPE_META           (0x04)  /* Metadata, e.g. superblock, journals */
+#define LM_TYPE_IOPEN          (0x05)
+#define LM_TYPE_FLOCK          (0x06)  /* Linux file lock */
+#define LM_TYPE_PLOCK          (0x07)  /* POSIX file lock */
+#define LM_TYPE_QUOTA          (0x08)  /* User or group block usage quota */
+
+/* States passed to lock() */
+
+#define LM_ST_UNLOCKED         (0)  /* Not locked */
+#define LM_ST_EXCLUSIVE        (1)  /* Allow writes */
+#define LM_ST_DEFERRED         (2)  /* Locking deferred to application level */
+#define LM_ST_SHARED           (3)  /* Allow reads, protected from writes */
+
+/* Flags passed to lock() */
+
+#define LM_FLAG_TRY            (0x00000001) /* Don't block if not immediately
+                                             *   grantable */
+#define LM_FLAG_TRY_1CB        (0x00000002) /* Don't block if not grantable
+                                             *   after request to other node */
+#define LM_FLAG_NOEXP          (0x00000004) /* Don't grant if held by expired
+                                             *   (crashed/dead) node */
+#define LM_FLAG_ANY            (0x00000008) /* Grant if lock state is any
+                                             *   other than LM_ST_UNLOCKED */
+#define LM_FLAG_PRIORITY       (0x00000010) /* High priority lock request,
+                                             *   put at top of wait queue */
+
+/* Flags returned by lock() */
+
+#define LM_OUT_ST_MASK         (0x00000003) /* 4-state LM_ST_XX mask */
+#define LM_OUT_CACHEABLE       (0x00000004)
+#define LM_OUT_CANCELED        (0x00000008) /* Lock request was cancelled */
+#define LM_OUT_ASYNC           (0x00000080)
+
+/* Callback types */
+
+#define LM_CB_NEED_E           (257)  /* Other node needs EXCLUSIVE lock */
+#define LM_CB_NEED_D           (258)  /* Other node needs DEFERRED lock */
+#define LM_CB_NEED_S           (259)  /* Other node needs SHARED lock */
+#define LM_CB_NEED_RECOVERY    (260)  /* A node crashed, needs jrnl recovery */
+#define LM_CB_DROPLOCKS        (261)  /* Locking system running out of space */
+#define LM_CB_ASYNC            (262)  /* Asynchronous lock request results */
+
+/* Reset_exp messages */
+
+#define LM_RD_GAVEUP           (308)
+#define LM_RD_SUCCESS          (309)
+
+struct lm_lockname {
+	uint64_t ln_number;           /* Lock number */
+	unsigned int ln_type;         /* LM_TYPE_XXX lock type */
+};
+
+#define lm_name_equal(name1, name2) \
+(((name1)->ln_number == (name2)->ln_number) && \
+ ((name1)->ln_type == (name2)->ln_type)) \
+
+struct lm_async_cb {
+	struct lm_lockname lc_name;
+	int lc_ret;
+};
+
+struct lm_lockstruct;
+
+/*
+ * Operations that form the interface between GFS' glock layer
+ * and the lock plug-in module that supports inter-node locks.
+ */
+struct lm_lockops {
+	char lm_proto_name[256];
+
+	/*
+	 * Mount/Unmount
+	 */
+
+	/* Mount the lock module on lock harness, so GFS can use it */
+	int (*lm_mount) (char *table_name, char *host_data,
+			 lm_callback_t cb, lm_fsdata_t *fsdata,
+			 unsigned int min_lvb_size,
+			 struct lm_lockstruct *lockstruct);
+
+	/* We've completed mount operations for this GFS filesystem/lockspace,
+	     other nodes may now mount it */
+	void (*lm_others_may_mount) (lm_lockspace_t *lockspace);
+
+	/* Unmount the lock module */
+	void (*lm_unmount) (lm_lockspace_t *lockspace);
+
+	/* Abnormal unmount */
+	void (*lm_withdraw) (lm_lockspace_t *lockspace);
+
+	/*
+	 * Lock oriented operations
+	 */
+
+	/* Find or create structures, etc. for lock (but don't lock it yet) */
+	int (*lm_get_lock) (lm_lockspace_t *lockspace,
+			    struct lm_lockname *name, lm_lock_t **lockp);
+
+	/* Done with structure */
+	void (*lm_put_lock) (lm_lock_t *lock);
+
+	/* Lock inter-node lock in requested state */
+	unsigned int (*lm_lock) (lm_lock_t *lock, unsigned int cur_state,
+				 unsigned int req_state, unsigned int flags);
+
+	/* Unlock inter-node lock */
+	unsigned int (*lm_unlock) (lm_lock_t *lock, unsigned int cur_state);
+
+	/* Cancel a lock request */
+	void (*lm_cancel) (lm_lock_t *lock);
+
+	/* Lock Value Block operations */
+	int (*lm_hold_lvb) (lm_lock_t *lock, char **lvbp);
+	void (*lm_unhold_lvb) (lm_lock_t *lock, char *lvb);
+
+	/* Make new LVB contents visible to other nodes */
+	void (*lm_sync_lvb) (lm_lock_t *lock, char *lvb);
+
+	/*
+	 * Posix Lock oriented operations
+	 */
+
+	int (*lm_plock_get) (lm_lockspace_t *lockspace,
+			     struct lm_lockname *name,
+			     struct file *file, struct file_lock *fl);
+
+	int (*lm_plock) (lm_lockspace_t *lockspace,
+			 struct lm_lockname *name,
+			 struct file *file, int cmd, struct file_lock *fl);
+
+	int (*lm_punlock) (lm_lockspace_t *lockspace,
+			   struct lm_lockname *name,
+			   struct file *file, struct file_lock *fl);
+
+	/*
+	 * Client oriented operations
+	 */
+
+	/* This node has completed journal recovery for a crashed node */
+	void (*lm_recovery_done) (lm_lockspace_t *lockspace, unsigned int jid,
+				  unsigned int message);
+
+	struct module *lm_owner;
+};
+
+/*
+ * GFS passes this structure to the lock module to fill when mounting.
+ */
+struct lm_lockstruct {
+	unsigned int ls_jid;           /* Journal ID # for this node */
+	unsigned int ls_first;         /* This node is first to mount this FS */
+	unsigned int ls_lvb_size;      /* Size (bytes) of Lock Value Block */
+	lm_lockspace_t *ls_lockspace;  /* Lock module instance handle */
+	struct lm_lockops *ls_ops;     /* Pointers to functions in lock module*/
+	int ls_flags;                  /* LM_LSFLAG_XXX, e.g. local filesystem*/
+};
+
+/*
+ * Lock Module Bottom interface.
+ * Each lock module makes itself known or unknown to the lock harness
+ *   via these functions.
+ */
+
+int lm_register_proto(struct lm_lockops *proto);
+void lm_unregister_proto(struct lm_lockops *proto);
+
+/*
+ * Lock Module Top interface.
+ * GFS calls these functions to mount or unmount a particular lock module.
+ */
+
+int lm_mount(char *proto_name,
+	     char *table_name, char *host_data,
+	     lm_callback_t cb, lm_fsdata_t *fsdata,
+	     unsigned int min_lvb_size, struct lm_lockstruct *lockstruct);
+void lm_unmount(struct lm_lockstruct *lockstruct);
+void lm_withdraw(struct lm_lockstruct *lockstruct);
+
+#endif /* __LM_INTERFACE_DOT_H__ */
