[GFS2] Further updates to dir and logging code

This reduces the size of the directory code by about 3k and gets
readdir() to use the functions which were introduced in the previous
directory code update.

Two memory allocations are merged into one. Eliminates zeroing of some
buffers which were never used before they were initialised by
other data.

There is still scope for further improvement in the directory code.

On the logging side, a hand created mutex has been replaced by a
standard Linux mutex in the log allocation code.

Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index cd5e4d8..c772311 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -13,6 +13,7 @@
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 
 #include "gfs2.h"
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index f31f163..ba34385 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -60,6 +60,7 @@
 #include <linux/buffer_head.h>
 #include <linux/sort.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 
 #include "gfs2.h"
@@ -344,7 +345,8 @@
 }
 
 typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent,
-			    const struct qstr *name);
+			    const struct qstr *name,
+			    void *opaque);
 
 static inline int __gfs2_dirent_find(const struct gfs2_dirent *dent,
 				     const struct qstr *name, int ret)
@@ -358,13 +360,15 @@
 }
 
 static int gfs2_dirent_find(const struct gfs2_dirent *dent,
-			    const struct qstr *name)
+			    const struct qstr *name,
+			    void *opaque)
 {
 	return __gfs2_dirent_find(dent, name, 1);
 }
 
 static int gfs2_dirent_prev(const struct gfs2_dirent *dent,
-			    const struct qstr *name)
+			    const struct qstr *name,
+			    void *opaque)
 {
 	return __gfs2_dirent_find(dent, name, 2);
 }
@@ -374,7 +378,8 @@
  * name->len holds size of block.
  */
 static int gfs2_dirent_last(const struct gfs2_dirent *dent,
-			    const struct qstr *name)
+			    const struct qstr *name,
+			    void *opaque)
 {
 	const char *start = name->name;
 	const char *end = (const char *)dent + be16_to_cpu(dent->de_rec_len);
@@ -384,17 +389,36 @@
 }
 
 static int gfs2_dirent_find_space(const struct gfs2_dirent *dent,
-				  const struct qstr *name)
+				  const struct qstr *name,
+				  void *opaque)
 {
 	unsigned required = GFS2_DIRENT_SIZE(name->len);
 	unsigned actual = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
 	unsigned totlen = be16_to_cpu(dent->de_rec_len);
 
+	if (!dent->de_inum.no_addr)
+		actual = GFS2_DIRENT_SIZE(0);
 	if ((totlen - actual) >= required)
 		return 1;
 	return 0;
 }
 
+struct dirent_gather {
+	const struct gfs2_dirent **pdent;
+	unsigned offset;
+};
+
+static int gfs2_dirent_gather(const struct gfs2_dirent *dent,
+			      const struct qstr *name,
+			      void *opaque)
+{
+	struct dirent_gather *g = opaque;
+	if (dent->de_inum.no_addr) {
+		g->pdent[g->offset++] = dent;
+	}
+	return 0;
+}
+
 /*
  * Other possible things to check:
  * - Inode located within filesystem size (and on valid block)
@@ -431,19 +455,12 @@
 	return -EIO;
 }
 
-static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
-					    void *buf,
-					    unsigned int len, gfs2_dscan_t scan,
-					    const struct qstr *name)
+static int gfs2_dirent_offset(const void *buf)
 {
-	struct gfs2_meta_header *h = buf;
-	struct gfs2_dirent *dent, *prev;
-	unsigned offset;
-	unsigned size;
-	int ret = 0;
+	const struct gfs2_meta_header *h = buf;
+	int offset;
 
 	BUG_ON(buf == NULL);
-	BUG_ON(name == NULL);
 
 	switch(be16_to_cpu(h->mh_type)) {
 	case GFS2_METATYPE_LF:
@@ -455,14 +472,36 @@
 	default:
 		goto wrong_type;
 	}
+	return offset;
+wrong_type:
+	printk(KERN_WARNING "gfs2_scan_dirent: wrong block type %u\n",
+	       be16_to_cpu(h->mh_type));
+	return -1;
+}
 
+static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
+					    void *buf,
+					    unsigned int len, gfs2_dscan_t scan,
+					    const struct qstr *name,
+					    void *opaque)
+{
+	struct gfs2_dirent *dent, *prev;
+	unsigned offset;
+	unsigned size;
+	int ret = 0;
+
+	ret = gfs2_dirent_offset(buf);
+	if (ret < 0)
+		goto consist_inode;
+
+	offset = ret;
 	prev = NULL;
 	dent = (struct gfs2_dirent *)(buf + offset);
 	size = be16_to_cpu(dent->de_rec_len);
 	if (gfs2_check_dirent(dent, offset, size, len, 1))
 		goto consist_inode;
 	do {
-		ret = scan(dent, name);
+		ret = scan(dent, name, opaque);
 		if (ret)
 			break;
 		offset += size;
@@ -487,9 +526,6 @@
 		return ERR_PTR(ret);
 	}
 
-wrong_type:
-	printk(KERN_WARNING "gfs2_scan_dirent: %p wrong block type %u\n", scan,
-	       be16_to_cpu(h->mh_type));
 consist_inode:
 	gfs2_consist_inode(inode->u.generic_ip);
 	return ERR_PTR(-EIO);
@@ -651,7 +687,8 @@
 					     const struct qstr *name)
 {
 	struct gfs2_dirent *dent;
-	dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, gfs2_dirent_find_space, name);
+	dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, 
+				gfs2_dirent_find_space, name, NULL);
 	if (!dent || IS_ERR(dent))
 		return dent;
 	return gfs2_init_dirent(inode, dent, name, bh);
@@ -734,7 +771,7 @@
 			return ERR_PTR(error);
 		do {
 			dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
-						scan, name);
+						scan, name, NULL);
 			if (dent)
 				goto got_dent;
 			leaf = (struct gfs2_leaf *)bh->b_data;
@@ -751,7 +788,7 @@
 	error = gfs2_meta_inode_buffer(ip, &bh);
 	if (error)
 		return ERR_PTR(error);
-	dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name);
+	dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name, NULL);
 got_dent:
 	*pbh = bh;
 	return dent;
@@ -764,6 +801,7 @@
 	struct buffer_head *bh = gfs2_meta_new(ip->i_gl, bn);
 	struct gfs2_leaf *leaf;
 	struct gfs2_dirent *dent;
+	struct qstr name = { .name = "", .len = 0, .hash = 0 };
 	if (!bh)
 		return NULL;
 	gfs2_trans_add_bh(ip->i_gl, bh, 1);
@@ -775,12 +813,7 @@
 	leaf->lf_next = cpu_to_be64(0);
 	memset(leaf->lf_reserved, 0, sizeof(leaf->lf_reserved));
 	dent = (struct gfs2_dirent *)(leaf+1);
-	dent->de_inum.no_formal_ino = cpu_to_be64(0);
-	dent->de_inum.no_addr = cpu_to_be64(0);
-	dent->de_hash = cpu_to_be32(0);
-	dent->de_rec_len = cpu_to_be16(bh->b_size - sizeof(struct gfs2_leaf));
-	dent->de_name_len = cpu_to_be16(0);
-	dent->de_type = cpu_to_be16(0);
+	gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent);
 	*pbh = bh;
 	return leaf;
 }
@@ -831,7 +864,7 @@
 		   sizeof(struct gfs2_leaf);
 	args.name = bh->b_data;
 	dent = gfs2_dirent_scan(dip->i_vnode, bh->b_data, bh->b_size,
-				gfs2_dirent_last, &args);
+				gfs2_dirent_last, &args, NULL);
 	if (!dent) {
 		brelse(bh);
 		brelse(dibh);
@@ -939,7 +972,7 @@
 		lp[x] = cpu_to_be64(bn);
 
 	error = gfs2_dir_write_data(dip, (char *)lp, start * sizeof(uint64_t),
-				     half_len * sizeof(uint64_t));
+				    half_len * sizeof(uint64_t));
 	if (error != half_len * sizeof(uint64_t)) {
 		if (error >= 0)
 			error = -EIO;
@@ -965,7 +998,7 @@
 			str.name = (char*)(dent+1);
 			str.len = be16_to_cpu(dent->de_name_len);
 			str.hash = be32_to_cpu(dent->de_hash);
-			new = gfs2_dirent_alloc(dip->i_vnode, nbh, &str);
+			new = gfs2_dirent_alloc(inode, nbh, &str);
 			if (IS_ERR(new)) {
 				error = PTR_ERR(new);
 				break;
@@ -1154,10 +1187,10 @@
 
 static int do_filldir_main(struct gfs2_inode *dip, uint64_t *offset,
 			   void *opaque, gfs2_filldir_t filldir,
-			   struct gfs2_dirent **darr, uint32_t entries,
+			   const struct gfs2_dirent **darr, uint32_t entries,
 			   int *copied)
 {
-	struct gfs2_dirent *dent, *dent_next;
+	const struct gfs2_dirent *dent, *dent_next;
 	struct gfs2_inum inum;
 	uint64_t off, off_next;
 	unsigned int x, y;
@@ -1216,189 +1249,74 @@
 	return 0;
 }
 
-/**
- * do_filldir_single - Read directory entries out of a single block
- * @dip: The GFS2 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 gfs2_inode *dip, uint64_t *offset,
-			     void *opaque, gfs2_filldir_t filldir,
-			     struct buffer_head *bh, uint32_t entries,
-			     int *copied)
+static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque,
+			      gfs2_filldir_t filldir, int *copied,
+			      unsigned *depth, u64 leaf_no)
 {
-	struct gfs2_dirent **darr;
-	struct gfs2_dirent *de;
-	unsigned int e = 0;
-	int error;
+	struct gfs2_inode *ip = inode->u.generic_ip;
+	struct buffer_head *bh;
+	struct gfs2_leaf *lf;
+	unsigned entries = 0;
+	unsigned leaves = 0;
+	const struct gfs2_dirent **darr, *dent;
+	struct dirent_gather g;
+	struct buffer_head **larr;
+	int leaf = 0;
+	int error, i;
+	u64 lfn = leaf_no;
 
-	if (!entries)
-		return 0;
-
-	darr = kcalloc(entries, sizeof(struct gfs2_dirent *), GFP_KERNEL);
-	if (!darr)
-		return -ENOMEM;
-
-	dirent_first(dip, bh, &de);
 	do {
-		if (!de->de_inum.no_addr)
-			continue;
-		if (e >= entries) {
-			gfs2_consist_inode(dip);
-			error = -EIO;
-			goto out;
-		}
-		darr[e++] = de;
-	} while (dirent_next(dip, bh, &de) == 0);
-
-	if (e != entries) {
-		gfs2_consist_inode(dip);
-		error = -EIO;
-		goto out;
-	}
-
-	error = do_filldir_main(dip, offset, opaque, filldir, darr,
-				entries, copied);
-
- out:
-	kfree(darr);
-
-	return error;
-}
-
-/**
- * do_filldir_multi - Read directory entries out of a linked leaf list
- * @dip: The GFS2 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 gfs2_inode *dip, uint64_t *offset,
-			    void *opaque, gfs2_filldir_t filldir,
-			    struct buffer_head *bh, int *copied)
-{
-	struct buffer_head **larr = NULL;
-	struct gfs2_dirent **darr;
-	struct gfs2_leaf *leaf;
-	struct buffer_head *tmp_bh;
-	struct gfs2_dirent *de;
-	unsigned int entries, e = 0;
-	unsigned int leaves = 0, l = 0;
-	unsigned int x;
-	uint64_t ln;
-	int error = 0;
-
-	/*  Count leaves and entries  */
-
-	leaf = (struct gfs2_leaf *)bh->b_data;
-	entries = be16_to_cpu(leaf->lf_entries);
-	ln = be64_to_cpu(leaf->lf_next);
-
-	while (ln) {
-		error = get_leaf(dip, ln, &tmp_bh);
+		error = get_leaf(ip, lfn, &bh);
 		if (error)
-			return error;
-
-		leaf = (struct gfs2_leaf *)tmp_bh->b_data;
-		if (leaf->lf_entries) {
-			entries += be16_to_cpu(leaf->lf_entries);
-			leaves++;
-		}
-		ln = be64_to_cpu(leaf->lf_next);
-
-		brelse(tmp_bh);
-	}
+			goto out;
+		lf = (struct gfs2_leaf *)bh->b_data;
+		if (leaves == 0)
+			*depth = be16_to_cpu(lf->lf_depth);
+		entries += be16_to_cpu(lf->lf_entries);
+		leaves++;
+		lfn = be64_to_cpu(lf->lf_next);
+		brelse(bh);
+	} while(lfn);
 
 	if (!entries)
 		return 0;
 
-	if (leaves) {
-		larr = kcalloc(leaves, sizeof(struct buffer_head *),GFP_KERNEL);
-		if (!larr)
-			return -ENOMEM;
-	}
+	error = -ENOMEM;
+	larr = kmalloc((leaves + entries) * sizeof(void*), GFP_KERNEL);
+	if (!larr)
+		goto out;
+	darr = (const struct gfs2_dirent **)(larr + leaves);
+	g.pdent = darr;
+	g.offset = 0;
+	lfn = leaf_no;
 
-	darr = kcalloc(entries, sizeof(struct gfs2_dirent *), GFP_KERNEL);
-	if (!darr) {
-		kfree(larr);
-		return -ENOMEM;
-	}
-
-	leaf = (struct gfs2_leaf *)bh->b_data;
-	if (leaf->lf_entries) {
-		dirent_first(dip, bh, &de);
-		do {
-			if (!de->de_inum.no_addr)
-				continue;
-			if (e >= entries) {
-				gfs2_consist_inode(dip);
-				error = -EIO;
-				goto out;
-			}
-			darr[e++] = de;
-		} while (dirent_next(dip, bh, &de) == 0);
-	}
-	ln = be64_to_cpu(leaf->lf_next);
-
-	while (ln) {
-		error = get_leaf(dip, ln, &tmp_bh);
+	do {
+		error = get_leaf(ip, lfn, &bh);
 		if (error)
-			goto out;
-
-		leaf = (struct gfs2_leaf *)tmp_bh->b_data;
-		if (leaf->lf_entries) {
-			dirent_first(dip, tmp_bh, &de);
-			do {
-				if (!de->de_inum.no_addr)
-					continue;
-				if (e >= entries) {
-					gfs2_consist_inode(dip);
-					error = -EIO;
-					goto out;
-				}
-				darr[e++] = de;
-			} while (dirent_next(dip, tmp_bh, &de) == 0);
-
-			larr[l++] = tmp_bh;
-
-			ln = be64_to_cpu(leaf->lf_next);
+			goto out_kfree;
+		lf = (struct gfs2_leaf *)bh->b_data;
+		lfn = be64_to_cpu(lf->lf_next);
+		if (lf->lf_entries) {
+			dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
+						gfs2_dirent_gather, NULL, &g);
+			error = PTR_ERR(dent);
+			if (IS_ERR(dent)) {
+				goto out_kfree;
+			}
+			error = 0;
+			larr[leaf++] = bh;
 		} else {
-			ln = be64_to_cpu(leaf->lf_next);
-			brelse(tmp_bh);
+			brelse(bh);
 		}
-	}
+	} while(lfn);
 
-	if (gfs2_assert_withdraw(dip->i_sbd, l == leaves)) {
-		error = -EIO;
-		goto out;
-	}
-	if (e != entries) {
-		gfs2_consist_inode(dip);
-		error = -EIO;
-		goto out;
-	}
-
-	error = do_filldir_main(dip, offset, opaque, filldir, darr,
+	error = do_filldir_main(ip, offset, opaque, filldir, darr,
 				entries, copied);
-
- out:
-	kfree(darr);
-	for (x = 0; x < l; x++)
-		brelse(larr[x]);
+out_kfree:
+	for(i = 0; i < leaf; i++)
+		brelse(larr[i]);
 	kfree(larr);
-
+out:
 	return error;
 }
 
@@ -1412,18 +1330,18 @@
  * Returns: errno
  */
 
-static int dir_e_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
+static int dir_e_read(struct inode *inode, uint64_t *offset, void *opaque,
 		      gfs2_filldir_t filldir)
 {
+	struct gfs2_inode *dip = inode->u.generic_ip;
 	struct gfs2_sbd *sdp = dip->i_sbd;
-	struct buffer_head *bh;
-	struct gfs2_leaf *leaf;
-	uint32_t hsize, len;
+	uint32_t hsize, len = 0;
 	uint32_t ht_offset, lp_offset, ht_offset_cur = -1;
 	uint32_t hash, index;
 	uint64_t *lp;
 	int copied = 0;
 	int error = 0;
+	unsigned depth;
 
 	hsize = 1 << dip->i_di.di_depth;
 	if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
@@ -1454,61 +1372,66 @@
 			ht_offset_cur = ht_offset;
 		}
 
-		error = get_leaf(dip, be64_to_cpu(lp[lp_offset]), &bh);
+		error = gfs2_dir_read_leaf(inode, offset, opaque, filldir,
+					   &copied, &depth,
+					   be64_to_cpu(lp[lp_offset]));
 		if (error)
-			goto out;
+			break;
 
-		leaf = (struct gfs2_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, 
-						  be16_to_cpu(leaf->lf_entries),
-						  &copied);
-
-		brelse(bh);
-
-		if (error) {
-			if (error > 0)
-				error = 0;
-			goto out;
-		}
-
-		len = 1 << (dip->i_di.di_depth - be16_to_cpu(leaf->lf_depth));
+		len = 1 << (dip->i_di.di_depth - depth);
 		index = (index & ~(len - 1)) + len;
 	}
 
- out:
+out:
 	kfree(lp);
-
+	if (error > 0)
+		error = 0;
 	return error;
 }
 
-static int dir_l_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
-		      gfs2_filldir_t filldir)
+int gfs2_dir_read(struct inode *inode, uint64_t *offset, void *opaque,
+		  gfs2_filldir_t filldir)
 {
+	struct gfs2_inode *dip = inode->u.generic_ip;
+	struct dirent_gather g;
+	const struct gfs2_dirent **darr, *dent;
 	struct buffer_head *dibh;
 	int copied = 0;
 	int error;
 
+	if (!dip->i_di.di_entries)
+		return 0;
+
+	if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
+		return dir_e_read(inode, offset, opaque, filldir);
+
 	if (!gfs2_is_stuffed(dip)) {
 		gfs2_consist_inode(dip);
 		return -EIO;
 	}
 
-	if (!dip->i_di.di_entries)
-		return 0;
-
 	error = gfs2_meta_inode_buffer(dip, &dibh);
 	if (error)
 		return error;
 
-	error = do_filldir_single(dip, offset,
-				  opaque, filldir,
-				  dibh, dip->i_di.di_entries,
-				  &copied);
+	error = -ENOMEM;
+	darr = kmalloc(dip->i_di.di_entries * sizeof(struct gfs2_dirent *),
+		       GFP_KERNEL);
+	if (darr) {
+		g.pdent = darr;
+		g.offset = 0;
+		dent = gfs2_dirent_scan(inode, dibh->b_data, dibh->b_size,
+					gfs2_dirent_gather, NULL, &g);
+		if (IS_ERR(dent)) {
+			error = PTR_ERR(dent);
+			goto out;
+		}
+		error = do_filldir_main(dip, offset, opaque, filldir, darr,
+					dip->i_di.di_entries, &copied);
+out:
+		kfree(darr);
+	}
+
 	if (error > 0)
 		error = 0;
 
@@ -1694,7 +1617,7 @@
 		return PTR_ERR(dent);
 	}
 	/* If not first in block, adjust pointers accordingly */
-	if (gfs2_dirent_find(dent, name) == 0) {
+	if (gfs2_dirent_find(dent, name, NULL) == 0) {
 		prev = dent;
 		dent = (struct gfs2_dirent *)((char *)dent + be16_to_cpu(prev->de_rec_len));
 	}
@@ -1724,19 +1647,6 @@
 	return error;
 }
 
-int gfs2_dir_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
-		  gfs2_filldir_t filldir)
-{
-	int error;
-
-	if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
-		error = dir_e_read(dip, offset, opaque, filldir);
-	else
-		error = dir_l_read(dip, offset, opaque, filldir);
-
-	return error;
-}
-
 /**
  * gfs2_dir_mvino - Change inode number of directory entry
  * @dip: The GFS2 inode
diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h
index 8fd4dc0..42b3a1f 100644
--- a/fs/gfs2/dir.h
+++ b/fs/gfs2/dir.h
@@ -32,7 +32,7 @@
 int gfs2_dir_add(struct inode *inode, const struct qstr *filename,
 		 const struct gfs2_inum *inum, unsigned int type);
 int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename);
-int gfs2_dir_read(struct gfs2_inode *dip, uint64_t * offset, void *opaque,
+int gfs2_dir_read(struct inode *inode, uint64_t * offset, void *opaque,
 		  gfs2_filldir_t filldir);
 int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename,
 		   struct gfs2_inum *new_inum, unsigned int new_type);
@@ -44,6 +44,19 @@
 int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
                         struct buffer_head **bhp);
 
+static inline uint32_t gfs2_disk_hash(const char *data, int len)
+{
+        return crc32_le(0xFFFFFFFF, data, len) ^ 0xFFFFFFFF;
+}
+
+
+static inline void gfs2_str2qstr(struct qstr *name, const char *fname)
+{
+	name->name = fname;
+	name->len = strlen(fname);
+	name->hash = gfs2_disk_hash(name->name, name->len);
+}
+
 /* N.B. This probably ought to take inum & type as args as well */
 static inline void gfs2_qstr2dirent(const struct qstr *name, u16 reclen, struct gfs2_dirent *dent)
 {
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 62f109e..be307185 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -634,8 +634,7 @@
 	struct list_head sd_log_le_databuf;
 
 	unsigned int sd_log_blks_free;
-	struct list_head sd_log_blks_list;
-	wait_queue_head_t sd_log_blks_wait;
+	struct mutex sd_log_reserve_mutex;
 
 	uint64_t sd_log_sequence;
 	unsigned int sd_log_head;
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index d403d51..6140c24 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -15,6 +15,7 @@
 #include <linux/posix_acl.h>
 #include <linux/sort.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 
 #include "gfs2.h"
@@ -701,9 +702,7 @@
 struct inode *gfs2_lookup_simple(struct inode *dip, const char *name)
 {
 	struct qstr qstr;
-	qstr.name = name;
-	qstr.len = strlen(name);
-	qstr.hash = gfs2_disk_hash(qstr.name, qstr.len);
+	gfs2_str2qstr(&qstr, name);
 	return gfs2_lookupi(dip, &qstr, 1, NULL);
 }
 
@@ -1389,9 +1388,7 @@
 	if (error)
 		return error;
 
-	dotname.len = 1;
-	dotname.name = ".";
-	dotname.hash = gfs2_disk_hash(dotname.name, dotname.len);
+	gfs2_str2qstr(&dotname, ".");
 	error = gfs2_dir_del(ip, &dotname);
 	if (error)
 		return error;
@@ -1487,10 +1484,7 @@
 	struct qstr dotdot;
 	int error = 0;
 
-	memset(&dotdot, 0, sizeof(struct qstr));
-	dotdot.name = "..";
-	dotdot.len = 2;
-	dotdot.hash = gfs2_disk_hash(dotdot.name, dotdot.len);
+	gfs2_str2qstr(&dotdot, "..");
 
 	igrab(dir);
 
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index e6a84f7..16c1444 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -13,6 +13,7 @@
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 
 #include "gfs2.h"
@@ -24,18 +25,13 @@
 #include "lops.h"
 #include "meta_io.h"
 #include "util.h"
+#include "dir.h"
 
 #define PULL 1
 
-static void do_lock_wait(struct gfs2_sbd *sdp, wait_queue_head_t *wq,
-			 atomic_t *a)
-{
-	wait_event(*wq, atomic_read(a) ? 0 : 1);
-}
-
 static void lock_for_trans(struct gfs2_sbd *sdp)
 {
-	do_lock_wait(sdp, &sdp->sd_log_trans_wq, &sdp->sd_log_flush_count);
+	wait_event(sdp->sd_log_trans_wq, atomic_read(&sdp->sd_log_flush_count) ? 0 : 1);
 	atomic_inc(&sdp->sd_log_trans_count);
 }
 
@@ -49,7 +45,7 @@
 static void gfs2_lock_for_flush(struct gfs2_sbd *sdp)
 {
 	atomic_inc(&sdp->sd_log_flush_count);
-	do_lock_wait(sdp, &sdp->sd_log_flush_wq, &sdp->sd_log_trans_count);
+	wait_event(sdp->sd_log_flush_wq, atomic_read(&sdp->sd_log_trans_count) ? 0 : 1);
 }
 
 static void gfs2_unlock_from_flush(struct gfs2_sbd *sdp)
@@ -191,37 +187,19 @@
 
 int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks)
 {
-	LIST_HEAD(list);
 	unsigned int try = 0;
 
 	if (gfs2_assert_warn(sdp, blks) ||
 	    gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks))
 		return -EINVAL;
 
+	mutex_lock(&sdp->sd_log_reserve_mutex);
 	for (;;) {
 		gfs2_log_lock(sdp);
-		if (list_empty(&list)) {
-			list_add_tail(&list, &sdp->sd_log_blks_list);
-			while (sdp->sd_log_blks_list.next != &list) {
-				DECLARE_WAITQUEUE(__wait_chan, current);
-				set_current_state(TASK_UNINTERRUPTIBLE);
-				add_wait_queue(&sdp->sd_log_blks_wait,
-					       &__wait_chan);
-				gfs2_log_unlock(sdp);
-				schedule();
-				gfs2_log_lock(sdp);
-				remove_wait_queue(&sdp->sd_log_blks_wait,
-						  &__wait_chan);
-				set_current_state(TASK_RUNNING);
-			}
-		}
-		/* Never give away the last block so we can
-		   always pull the tail if we need to. */
 		if (sdp->sd_log_blks_free > blks) {
 			sdp->sd_log_blks_free -= blks;
-			list_del(&list);
 			gfs2_log_unlock(sdp);
-			wake_up(&sdp->sd_log_blks_wait);
+			mutex_unlock(&sdp->sd_log_reserve_mutex);
 			break;
 		}
 
diff --git a/fs/gfs2/ops_dentry.c b/fs/gfs2/ops_dentry.c
index b54608f..9583710 100644
--- a/fs/gfs2/ops_dentry.c
+++ b/fs/gfs2/ops_dentry.c
@@ -14,6 +14,7 @@
 #include <linux/buffer_head.h>
 #include <linux/smp_lock.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 
 #include "gfs2.h"
diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c
index b27bce7..be16c68 100644
--- a/fs/gfs2/ops_export.c
+++ b/fs/gfs2/ops_export.c
@@ -13,6 +13,7 @@
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 
 #include "gfs2.h"
@@ -153,7 +154,7 @@
 	if (error)
 		return error;
 
-	error = gfs2_dir_read(dip, &offset, &gnfd, get_name_filldir);
+	error = gfs2_dir_read(dir, &offset, &gnfd, get_name_filldir);
 
 	gfs2_glock_dq_uninit(&gh);
 
@@ -165,12 +166,11 @@
 
 static struct dentry *gfs2_get_parent(struct dentry *child)
 {
-	struct qstr dotdot = { .name = "..", .len = 2 };
+	struct qstr dotdot;
 	struct inode *inode;
 	struct dentry *dentry;
 
-	dotdot.hash = gfs2_disk_hash(dotdot.name, dotdot.len);
-
+	gfs2_str2qstr(&dotdot, "..");
 	inode = gfs2_lookupi(child->d_inode, &dotdot, 1, NULL);
 
 	if (!inode)
diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c
index 6333a14..ac8e123 100644
--- a/fs/gfs2/ops_file.c
+++ b/fs/gfs2/ops_file.c
@@ -19,6 +19,8 @@
 #include <linux/smp_lock.h>
 #include <linux/fs.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/ext2_fs.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 #include <asm/uaccess.h>
 
@@ -39,6 +41,7 @@
 #include "rgrp.h"
 #include "trans.h"
 #include "util.h"
+#include "eaops.h"
 
 /* "bad" is for NFS support */
 struct filldir_bad_entry {
@@ -357,7 +360,8 @@
 
 static int readdir_reg(struct file *file, void *dirent, filldir_t filldir)
 {
-	struct gfs2_inode *dip = file->f_mapping->host->u.generic_ip;
+	struct inode *dir = file->f_mapping->host;
+	struct gfs2_inode *dip = dir->u.generic_ip;
 	struct filldir_reg fdr;
 	struct gfs2_holder d_gh;
 	uint64_t offset = file->f_pos;
@@ -375,7 +379,7 @@
 		return error;
 	}
 
-	error = gfs2_dir_read(dip, &offset, &fdr, filldir_reg_func);
+	error = gfs2_dir_read(dir, &offset, &fdr, filldir_reg_func);
 
 	gfs2_glock_dq_uninit(&d_gh);
 
@@ -446,7 +450,8 @@
 
 static int readdir_bad(struct file *file, void *dirent, filldir_t filldir)
 {
-	struct gfs2_inode *dip = file->f_mapping->host->u.generic_ip;
+	struct inode *dir = file->f_mapping->host;
+	struct gfs2_inode *dip = dir->u.generic_ip;
 	struct gfs2_sbd *sdp = dip->i_sbd;
 	struct filldir_reg fdr;
 	unsigned int entries, size;
@@ -479,7 +484,7 @@
 		goto out;
 	}
 
-	error = gfs2_dir_read(dip, &offset, fdb, filldir_bad_func);
+	error = gfs2_dir_read(dir, &offset, fdb, filldir_bad_func);
 
 	gfs2_glock_dq_uninit(&d_gh);
 
@@ -531,6 +536,210 @@
 	return error;
 }
 
+const struct gfs2_flag_eattr {
+	u32 flag;
+	u32 ext2;
+} gfs2_flag_eattrs[] = {
+	{
+		.flag = GFS2_DIF_IMMUTABLE,
+		.ext2 = EXT2_IMMUTABLE_FL,
+	}, {
+		.flag = GFS2_DIF_APPENDONLY,
+		.ext2 = EXT2_APPEND_FL,
+	}, {
+		.flag = GFS2_DIF_JDATA,
+		.ext2 = EXT2_JOURNAL_DATA_FL,
+	}, {
+		.flag = GFS2_DIF_EXHASH,
+		.ext2 = EXT2_INDEX_FL,
+	}, {
+		.flag = GFS2_DIF_EA_INDIRECT,
+	}, {
+		.flag = GFS2_DIF_DIRECTIO,
+	}, {
+		.flag = GFS2_DIF_NOATIME,
+		.ext2 = EXT2_NOATIME_FL,
+	}, {
+		.flag = GFS2_DIF_SYNC,
+		.ext2 = EXT2_SYNC_FL,
+	}, {
+		.flag = GFS2_DIF_SYSTEM,
+	}, {
+		.flag = GFS2_DIF_TRUNC_IN_PROG,
+	}, {
+		.flag = GFS2_DIF_INHERIT_JDATA,
+	}, {
+		.flag = GFS2_DIF_INHERIT_DIRECTIO,
+	}, {
+	},
+};
+
+static const struct gfs2_flag_eattr *get_by_ext2(u32 ext2)
+{
+	const struct gfs2_flag_eattr *p = gfs2_flag_eattrs;
+	for(; p->flag; p++) {
+		if (ext2 == p->ext2)
+			return p;
+	}
+	return NULL;
+}
+
+static const struct gfs2_flag_eattr *get_by_gfs2(u32 gfs2)
+{
+	const struct gfs2_flag_eattr *p = gfs2_flag_eattrs;
+	for(; p->flag; p++) {
+		if (gfs2 == p->flag)
+			return p;
+	}
+	return NULL;
+}
+
+static u32 gfs2_flags_to_ext2(u32 gfs2)
+{
+	const struct gfs2_flag_eattr *ea;
+	u32 ext2 = 0;
+	u32 mask = 1;
+
+	for(; mask != 0; mask <<=1) {
+		if (mask & gfs2) {
+			ea = get_by_gfs2(mask);
+			if (ea)
+				ext2 |= ea->ext2;
+		}
+	}
+	return ext2;
+}
+
+static int gfs2_flags_from_ext2(u32 *gfs2, u32 ext2)
+{
+	const struct gfs2_flag_eattr *ea;
+	u32 mask = 1;
+
+	for(; mask != 0; mask <<= 1) {
+		if (mask & ext2) {
+			ea = get_by_ext2(mask);
+			if (ea == NULL)
+				return -EINVAL;
+			*gfs2 |= ea->flag;
+		}
+	}
+	return 0;
+}
+
+static int get_ext2_flags(struct inode *inode, u32 __user *ptr)
+{
+	struct gfs2_inode *ip = inode->u.generic_ip;
+	struct gfs2_holder gh;
+	int error;
+	u32 ext2;
+
+	gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &gh);
+	error = gfs2_glock_nq_m_atime(1, &gh);
+	if (error)
+		return error;
+
+	ext2 = gfs2_flags_to_ext2(ip->i_di.di_flags);
+	if (put_user(ext2, ptr))
+		error = -EFAULT;
+
+	gfs2_glock_dq_m(1, &gh);
+	gfs2_holder_uninit(&gh);
+	return error;
+}
+
+/* Flags that can be set by user space */
+#define GFS2_FLAGS_USER_SET (GFS2_DIF_JDATA|			\
+			     GFS2_DIF_DIRECTIO|			\
+			     GFS2_DIF_IMMUTABLE|		\
+			     GFS2_DIF_APPENDONLY|		\
+			     GFS2_DIF_NOATIME|			\
+			     GFS2_DIF_SYNC|			\
+			     GFS2_DIF_SYSTEM|			\
+			     GFS2_DIF_INHERIT_DIRECTIO|		\
+			     GFS2_DIF_INHERIT_JDATA)
+
+/**
+ * gfs2_set_flags - set flags on an inode
+ * @inode: The inode
+ * @flags: The flags to set
+ * @mask: Indicates which flags are valid
+ *
+ */
+static int gfs2_set_flags(struct inode *inode, u32 flags, u32 mask)
+{
+	struct gfs2_inode *ip = inode->u.generic_ip;
+	struct buffer_head *bh;
+	struct gfs2_holder gh;
+	int error;
+	u32 new_flags;
+
+	gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+	if (error)
+		return error;
+
+	new_flags = (ip->i_di.di_flags & ~mask) | (flags & mask);
+	if ((new_flags ^ flags) == 0)
+		goto out;
+
+	error = -EINVAL;
+	if ((new_flags ^ flags) & ~GFS2_FLAGS_USER_SET)
+		goto out;
+
+	if (S_ISDIR(inode->i_mode)) {
+		if ((new_flags ^ flags) & (GFS2_DIF_JDATA | GFS2_DIF_DIRECTIO))
+			goto out;
+	} else if (S_ISREG(inode->i_mode)) {
+		if ((new_flags ^ flags) & (GFS2_DIF_INHERIT_DIRECTIO|
+					   GFS2_DIF_INHERIT_JDATA))
+			goto out;
+	} else
+		goto out;
+
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) && (new_flags & GFS2_DIF_IMMUTABLE))
+		goto out;
+	if (IS_APPEND(inode) && (new_flags & GFS2_DIF_APPENDONLY))
+		goto out;
+	error = gfs2_repermission(inode, MAY_WRITE, NULL);
+	if (error)
+		goto out;
+
+	error = gfs2_meta_inode_buffer(ip, &bh);
+	if (error)
+		goto out;
+	gfs2_trans_add_bh(ip->i_gl, bh, 1);
+	ip->i_di.di_flags = new_flags;
+	gfs2_dinode_out(&ip->i_di, bh->b_data);
+	brelse(bh);
+out:
+	gfs2_glock_dq_uninit(&gh);
+	return error;
+}
+
+static int set_ext2_flags(struct inode *inode, u32 __user *ptr)
+{
+	u32 ext2, gfs2;
+	if (get_user(ext2, ptr))
+		return -EFAULT;
+	if (gfs2_flags_from_ext2(&gfs2, ext2))
+		return -EINVAL;
+	return gfs2_set_flags(inode, gfs2, ~0);
+}
+
+int gfs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	switch(cmd) {
+	case EXT2_IOC_GETFLAGS:
+		return get_ext2_flags(inode, (u32 __user *)arg);
+	case EXT2_IOC_SETFLAGS:
+		return set_ext2_flags(inode, (u32 __user *)arg);
+	}
+	return -ENOTTY;
+}
+
+
 /**
  * gfs2_mmap -
  * @file: The file to map
@@ -832,6 +1041,7 @@
 	.write = generic_file_write,
 	.writev = generic_file_writev,
 	.aio_write = generic_file_aio_write,
+	.ioctl = gfs2_ioctl,
 	.mmap = gfs2_mmap,
 	.open = gfs2_open,
 	.release = gfs2_close,
@@ -843,6 +1053,7 @@
 
 struct file_operations gfs2_dir_fops = {
 	.readdir = gfs2_readdir,
+	.ioctl = gfs2_ioctl,
 	.open = gfs2_open,
 	.release = gfs2_close,
 	.fsync = gfs2_fsync,
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 8d2c557..2628bf3 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -97,9 +97,7 @@
 	INIT_LIST_HEAD(&sdp->sd_log_le_rg);
 	INIT_LIST_HEAD(&sdp->sd_log_le_databuf);
 
-	INIT_LIST_HEAD(&sdp->sd_log_blks_list);
-	init_waitqueue_head(&sdp->sd_log_blks_wait);
-
+	mutex_init(&sdp->sd_log_reserve_mutex);
 	INIT_LIST_HEAD(&sdp->sd_ail1_list);
 	INIT_LIST_HEAD(&sdp->sd_ail2_list);
 
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index e8ab9d2..1e2b709 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -18,6 +18,7 @@
 #include <linux/xattr.h>
 #include <linux/posix_acl.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 #include <asm/uaccess.h>
 
@@ -417,18 +418,16 @@
 	if (!gfs2_assert_withdraw(sdp, !error)) {
 		struct gfs2_dinode *di = (struct gfs2_dinode *)dibh->b_data;
 		struct gfs2_dirent *dent = (struct gfs2_dirent *)(di+1);
-		struct qstr str = { .name = ".", .len = 1 };
-		str.hash = gfs2_disk_hash(str.name, str.len);
+		struct qstr str;
 
+		gfs2_str2qstr(&str, ".");
 		gfs2_trans_add_bh(ip->i_gl, dibh, 1);
 		gfs2_qstr2dirent(&str, GFS2_DIRENT_SIZE(str.len), dent);
 		dent->de_inum = di->di_num; /* already GFS2 endian */
 		dent->de_type = DT_DIR;
 		di->di_entries = cpu_to_be32(1);
 
-		str.name = "..";
-		str.len = 2;
-		str.hash = gfs2_disk_hash(str.name, str.len);
+		gfs2_str2qstr(&str, "..");
 		dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1));
 		gfs2_qstr2dirent(&str, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent);
 
@@ -772,9 +771,7 @@
 
 	if (dir_rename) {
 		struct qstr name;
-		name.len = 2;
-		name.name = "..";
-		name.hash = gfs2_disk_hash(name.name, name.len);
+		gfs2_str2qstr(&name, "..");
 
 		error = gfs2_change_nlink(ndip, +1);
 		if (error)
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index 2df450e..6c7e2e8 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -13,6 +13,7 @@
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
 #include <asm/semaphore.h>
 
 #include "gfs2.h"
@@ -27,6 +28,7 @@
 #include "recovery.h"
 #include "super.h"
 #include "util.h"
+#include "dir.h"
 
 int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk,
 			   struct buffer_head **bh)
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 71cca76..a4da649 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -12,6 +12,7 @@
 #include <linux/spinlock.h>
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
+#include <linux/crc32.h>
 #include <linux/gfs2_ondisk.h>
 #include <asm/semaphore.h>
 
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index 8b22fa9..7cd9e25 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -28,11 +28,6 @@
 kmem_cache_t *gfs2_inode_cachep __read_mostly;
 kmem_cache_t *gfs2_bufdata_cachep __read_mostly;
 
-uint32_t gfs2_disk_hash(const char *data, int len)
-{
-	return crc32_le(0xFFFFFFFF, data, len) ^ 0xFFFFFFFF;
-}
-
 void gfs2_assert_i(struct gfs2_sbd *sdp)
 {
 	printk(KERN_EMERG "GFS2: fsid=%s: fatal assertion failed\n",
diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h
index 8d6eba3..addbe30 100644
--- a/fs/gfs2/util.h
+++ b/fs/gfs2/util.h
@@ -10,8 +10,6 @@
 #ifndef __UTIL_DOT_H__
 #define __UTIL_DOT_H__
 
-uint32_t gfs2_disk_hash(const char *data, int len);
-
 
 #define fs_printk(level, fs, fmt, arg...) \
 	printk(level "GFS2: fsid=%s: " fmt , (fs)->sd_fsname , ## arg)