cluster: RHEL56 - NFS over GFS issue (fatal: assertion "!bd->bd_pinned && !buffer_busy(bh)" failed)

Bob Peterson rpeterso at fedoraproject.org
Tue Sep 21 22:06:24 UTC 2010


Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=ab8fc1a2100c81e7417bc15496cf293dde6512cd
Commit:        ab8fc1a2100c81e7417bc15496cf293dde6512cd
Parent:        53dba11a565ed574c6433ed8ba8c12c274185911
Author:        Bob Peterson <rpeterso at redhat.com>
AuthorDate:    Tue Sep 21 17:05:29 2010 -0500
Committer:     Bob Peterson <rpeterso at redhat.com>
CommitterDate: Tue Sep 21 17:05:29 2010 -0500

NFS over GFS issue (fatal: assertion "!bd->bd_pinned && !buffer_busy(bh)" failed)

There were several places in the code that tried to remove buffers
from the two active items lists (transaction and glock ail) but each
one was using a different set of criteria, none of which were
consistent.  This patch introduces a new function,
gfs_bd_ail_tryremove, that checks the condition of the buffer using
uniform criteria before removing it from the ail.  All the places
that were doing this haphazardly now call the new function.

So really, not counting the scsi device driver bug, there were six
main gfs bugs found with this one scenario:

1. There was a timing window whereby a process could mark a buffer
   dirty while it was being synced to disk.  This was fixed by
   introducing a new semaphore, sd_log_flush_lock, which keeps
   that from happening.
2. Buffers were being taken off the ail list at different times
   using different criteria.  That was fixed by the new function
   as mentioned above.
3. Some buffers were not being added to the transaction, especially
   in cases where the files were journaled, which happens mostly
   with directory hash table data.  That was fixed by the
   introduction of the necessary calls to gfs_trans_add_bh.
4. The transaction glock was prematurely being released when the
   glocks hit a capacity watermark.  That's why it often took so
   long to recreate some of these problems.  To prevent this, new
   code was added to function clear_glock so that it would only
   release the transaction glock at unmount time.  I'm using the
   number of glockd daemons to determine whether the call was made
   during normal operations or at unmount time.  So in order to
   accomodate that change, I had to fix a bit of code where the
   number of glockd daemons was going negative.
5. When finding its place in the journal, function gfs_find_jhead
   was not holding the journal log lock.  That causing another
   nasty timing window where the journal was being changed while
   the proper journal location was being located.
6. This patch fixes a problem whereby GFS read-ahead was interfering
   with GFS write requests by continuously re-trying the read-aheads.
   The patch reduces the extra read-aheads so that the read-ahead for
   each block is only done once for any given write request.

rhbz#491369
---
 gfs-kernel/src/gfs/dio.c        |  135 ++++-----------------------------------
 gfs-kernel/src/gfs/dio.h        |   29 ++++++++-
 gfs-kernel/src/gfs/dir.c        |   15 ++++-
 gfs-kernel/src/gfs/file.c       |   14 +++-
 gfs-kernel/src/gfs/glock.c      |    5 ++
 gfs-kernel/src/gfs/glops.c      |   12 +--
 gfs-kernel/src/gfs/incore.h     |    2 +
 gfs-kernel/src/gfs/log.c        |   97 ++++++++++++++++++++++++++--
 gfs-kernel/src/gfs/log.h        |    2 +-
 gfs-kernel/src/gfs/ops_export.c |    3 +
 gfs-kernel/src/gfs/ops_file.c   |    8 +-
 gfs-kernel/src/gfs/ops_fstype.c |    5 +-
 gfs-kernel/src/gfs/ops_super.c  |    6 +-
 gfs-kernel/src/gfs/quota.c      |    2 +-
 gfs-kernel/src/gfs/recovery.c   |    3 +
 gfs-kernel/src/gfs/trans.c      |    9 +--
 16 files changed, 189 insertions(+), 158 deletions(-)

diff --git a/gfs-kernel/src/gfs/dio.c b/gfs-kernel/src/gfs/dio.c
index 277b93e..8000954 100644
--- a/gfs-kernel/src/gfs/dio.c
+++ b/gfs-kernel/src/gfs/dio.c
@@ -32,8 +32,6 @@
 #include "rgrp.h"
 #include "trans.h"
 
-#define buffer_busy(bh) ((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock)))
-
 /**
  * aspace_get_block - 
  * @inode:
@@ -274,20 +272,8 @@ gfs_ail_start_trans(struct gfs_sbd *sdp, struct gfs_trans *tr)
 			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);
-
+			if (bd->bd_pinned || gfs_bd_ail_tryremove(sdp, bd)) {
 				gfs_unlock_buffer(bh);
-				brelse(bh);
 				continue;
 			}
 
@@ -334,22 +320,10 @@ gfs_ail_empty_trans(struct gfs_sbd *sdp, struct gfs_trans *tr)
 		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)) {
+		if (gfs_trylock_buffer(bh) == 0) {
+			gfs_bd_ail_tryremove(sdp, bd);
 			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);
@@ -360,85 +334,6 @@ gfs_ail_empty_trans(struct gfs_sbd *sdp, struct gfs_trans *tr)
 }
 
 /**
- * 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
@@ -731,11 +626,7 @@ gfs_dpin(struct gfs_sbd *sdp, struct buffer_head *bh)
 		   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);
-		}
+		gfs_bd_ail_tryremove(sdp, bd);
 		spin_unlock(&sdp->sd_ail_lock);
 
 		clear_buffer_dirty(bh);
@@ -1081,11 +972,7 @@ gfs_wipe_buffers(struct gfs_inode *ip, struct gfs_rgrpd *rgd,
 						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);
-						}
+						gfs_bd_ail_tryremove(sdp, bd);
 						spin_unlock(&sdp->sd_ail_lock);
 					}
 				} else {
@@ -1301,21 +1188,22 @@ gfs_get_data_buffer(struct gfs_inode *ip, uint64_t block, int new,
  * @dblock: the starting disk block
  * @extlen: the number of blocks in the extent
  *
+ * returns: number of blocks for which it submitted read requests.
  */
 
-void
+int
 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;
+	int error, submitted = 0;
 
 	if (!extlen)
-		return;
+		return 0;
 	if (!max_ra)
-		return;
+		return 0;
 	if (extlen > max_ra)
 		extlen = max_ra;
 
@@ -1325,6 +1213,7 @@ gfs_start_ra(struct gfs_glock *gl, uint64_t dblock, uint32_t extlen)
 		goto out;
 	if (!buffer_locked(first_bh)) {
 		error = gfs_dreread(sdp, first_bh, DIO_START);
+		submitted++;
 		if (error)
 			goto out;
 	}
@@ -1337,6 +1226,7 @@ gfs_start_ra(struct gfs_glock *gl, uint64_t dblock, uint32_t extlen)
 
 		if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
 			error = gfs_dreread(sdp, bh, DIO_START);
+			submitted++;
 			brelse(bh);
 			if (error)
 				goto out;
@@ -1352,4 +1242,5 @@ gfs_start_ra(struct gfs_glock *gl, uint64_t dblock, uint32_t extlen)
 
  out:
 	brelse(first_bh);
+	return submitted;
 }
diff --git a/gfs-kernel/src/gfs/dio.h b/gfs-kernel/src/gfs/dio.h
index 1c4060d..afbf7d1 100644
--- a/gfs-kernel/src/gfs/dio.h
+++ b/gfs-kernel/src/gfs/dio.h
@@ -14,6 +14,8 @@
 #ifndef __DIO_DOT_H__
 #define __DIO_DOT_H__
 
+#define buffer_busy(bh) ((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock)))
+
 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);
 
@@ -73,7 +75,7 @@ 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);
+int 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)
@@ -85,7 +87,6 @@ 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);
 
@@ -167,4 +168,28 @@ gfs_buffer_copy_tail(struct buffer_head *to_bh, int to_head,
 	       from_head - to_head);
 }
 
+/*
+ * gfs_bd_ail_tryremove - try to remove a bd from the ail
+ * returns: 1 if it removed one, else 0
+ */
+static __inline__ int
+gfs_bd_ail_tryremove(struct gfs_sbd *sdp, struct gfs_bufdata *bd)
+{
+	struct buffer_head *bh;
+
+	bh = bd->bd_bh;
+	if (!bd->bd_pinned && !list_empty(&bd->bd_ail_tr_list) &&
+	    !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);
+		return 1;
+	}
+	return 0;
+}
+
 #endif /* __DIO_DOT_H__ */
diff --git a/gfs-kernel/src/gfs/dir.c b/gfs-kernel/src/gfs/dir.c
index 34263a2..02d3016 100644
--- a/gfs-kernel/src/gfs/dir.c
+++ b/gfs-kernel/src/gfs/dir.c
@@ -808,7 +808,9 @@ dir_split_leaf(struct gfs_inode *dip, uint32_t index, uint64_t leaf_no)
 		    gfs32_to_cpu(dent->de_hash) < divider) {
 			name_len = gfs16_to_cpu(dent->de_name_len);
 
-			gfs_dirent_alloc(dip, nbh, name_len, &new);
+			error = gfs_dirent_alloc(dip, nbh, name_len, &new);
+			if (error)
+				goto fail_brelse;
 
 			new->de_inum = dent->de_inum; /* No endianness worries */
 			new->de_hash = dent->de_hash; /* No endianness worries */
@@ -841,7 +843,9 @@ dir_split_leaf(struct gfs_inode *dip, uint32_t index, uint64_t leaf_no)
 	   artificially fill in the first entry. */
 
 	if (!moved) {
-		gfs_dirent_alloc(dip, nbh, 0, &new);
+		error = gfs_dirent_alloc(dip, nbh, 0, &new);
+		if (error)
+			goto fail_brelse;
 		new->de_inum.no_formal_ino = 0;
 	}
 
@@ -851,6 +855,7 @@ dir_split_leaf(struct gfs_inode *dip, uint32_t index, uint64_t leaf_no)
 
 	error = gfs_get_inode_buffer(dip, &dibh);
 	if (!gfs_assert_withdraw(dip->i_sbd, !error)) {
+		gfs_trans_add_bh(dip->i_gl, dibh);
 		dip->i_di.di_blocks++;
 		gfs_dinode_out(&dip->i_di, dibh->b_data);
 		brelse(dibh);
@@ -936,6 +941,7 @@ dir_double_exhash(struct gfs_inode *dip)
 
 	error = gfs_get_inode_buffer(dip, &dibh);
 	if (!gfs_assert_withdraw(sdp, !error)) {
+		gfs_trans_add_bh(dip->i_gl, dibh);
 		dip->i_di.di_depth++;
 		gfs_dinode_out(&dip->i_di, dibh->b_data);
 		brelse(dibh);
@@ -1461,7 +1467,9 @@ dir_e_add(struct gfs_inode *dip, struct qstr *filename,
 				nleaf->lf_depth = leaf->lf_depth;
 				nleaf->lf_dirent_format = cpu_to_gfs32(GFS_FORMAT_DE);
 
-				gfs_dirent_alloc(dip, nbh, filename->len, &dent);
+				error = gfs_dirent_alloc(dip, nbh, filename->len, &dent);
+				if (error)
+					return error;
 
 				dip->i_di.di_blocks++;
 
@@ -1752,6 +1760,7 @@ dir_l_add(struct gfs_inode *dip, struct qstr *filename,
 	if (error)
 		return error;
 
+	gfs_trans_add_bh(dip->i_gl, dibh);
 	if (gfs_dirent_alloc(dip, dibh, filename->len, &dent)) {
 		brelse(dibh);
 
diff --git a/gfs-kernel/src/gfs/file.c b/gfs-kernel/src/gfs/file.c
index c90a704..a559749 100644
--- a/gfs-kernel/src/gfs/file.c
+++ b/gfs-kernel/src/gfs/file.c
@@ -313,7 +313,7 @@ gfs_writei(struct gfs_inode *ip, void *buf,
 	struct buffer_head *dibh, *bh;
 	uint64_t lblock, dblock;
 	unsigned int o;
-	uint32_t extlen = 0;
+	uint32_t extlen = 0, blocks_ra = 0;
 	unsigned int amount;
 	int new;
 	int journaled = gfs_is_jdata(ip);
@@ -355,6 +355,7 @@ gfs_writei(struct gfs_inode *ip, void *buf,
 				error = gfs_block_map(ip, lblock, &new, &dblock, &extlen);
 				if (error)
 					goto fail;
+				blocks_ra = 0;
 			} else {
 				new = FALSE;
 				dblock = ip->i_num.no_addr;
@@ -362,8 +363,13 @@ gfs_writei(struct gfs_inode *ip, void *buf,
 			}
 		}
 
-		if (journaled && extlen > 1)
-			gfs_start_ra(ip->i_gl, dblock, extlen);
+		if (journaled && extlen > 1) {
+			if (!blocks_ra)
+				blocks_ra = gfs_start_ra(ip->i_gl, dblock,
+							 extlen);
+			if (blocks_ra)
+				blocks_ra--;
+		}
 
 		error = gfs_get_data_buffer(ip, dblock,
 					    (amount == sdp->sd_sb.sb_bsize) ? TRUE : new,
@@ -371,6 +377,8 @@ gfs_writei(struct gfs_inode *ip, void *buf,
 		if (error)
 			goto fail;
 
+		if (journaled)
+			gfs_trans_add_bh(ip->i_gl, bh);
 		error = copy_fn(ip, bh, &buf, o, amount, new);
 		brelse(bh);
 		if (error)
diff --git a/gfs-kernel/src/gfs/glock.c b/gfs-kernel/src/gfs/glock.c
index 493d2a1..54c586e 100644
--- a/gfs-kernel/src/gfs/glock.c
+++ b/gfs-kernel/src/gfs/glock.c
@@ -2718,6 +2718,11 @@ clear_glock(struct gfs_glock *gl, unsigned int *unused)
 	struct gfs_sbd *sdp = gl->gl_sbd;
 	struct gfs_gl_hash_bucket *bucket = gl->gl_bucket;
 
+	/* If this isn't shutdown, keep the transaction glock around. */
+	if (sdp->sd_glockd_num && gl == sdp->sd_trans_gl) {
+		glock_put(gl);   /* see examine_bucket() */
+		return;
+	}
 	spin_lock(&sdp->sd_reclaim_lock);
 	if (!list_empty(&gl->gl_reclaim)) {
 		list_del_init(&gl->gl_reclaim);
diff --git a/gfs-kernel/src/gfs/glops.c b/gfs-kernel/src/gfs/glops.c
index da3d1d8..c9f5ad0 100644
--- a/gfs-kernel/src/gfs/glops.c
+++ b/gfs-kernel/src/gfs/glops.c
@@ -51,10 +51,8 @@ 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);
-	}
+	if (test_bit(GLF_DIRTY, &gl->gl_flags))
+		gfs_log_flush_glock(gl, flags);
 
 	/* We've synced everything, clear SYNC request and DIRTY flags */
 	clear_bit(GLF_DIRTY, &gl->gl_flags);
@@ -234,12 +232,10 @@ inode_go_sync(struct gfs_glock *gl, int flags)
 	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_log_flush_glock(gl, flags);
 			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);
+			gfs_log_flush_glock(gl, flags);
 		} else if (data)
 			gfs_sync_page(gl, flags | DIO_START | DIO_WAIT | DIO_CHECK);
 	}
diff --git a/gfs-kernel/src/gfs/incore.h b/gfs-kernel/src/gfs/incore.h
index 9c4f467..67f7e10 100644
--- a/gfs-kernel/src/gfs/incore.h
+++ b/gfs-kernel/src/gfs/incore.h
@@ -1131,6 +1131,8 @@ struct gfs_sbd {
 	unsigned int sd_log_buffers;	/* # of buffers in the incore log */
 
 	struct rw_semaphore sd_log_lock;	/* Lock for access to log values */
+	struct semaphore sd_log_flush_lock; /* Lock for function
+						  log_flush_internal */
 
 	uint64_t sd_log_dump_last;
 	uint64_t sd_log_dump_last_wrap;
diff --git a/gfs-kernel/src/gfs/log.c b/gfs-kernel/src/gfs/log.c
index d8738fa..2402e54 100644
--- a/gfs-kernel/src/gfs/log.c
+++ b/gfs-kernel/src/gfs/log.c
@@ -38,9 +38,11 @@
 #include <asm/semaphore.h>
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
+#include <linux/mm.h>
 
 #include "gfs.h"
 #include "dio.h"
+#include "glock.h"
 #include "log.h"
 #include "lops.h"
 
@@ -1037,6 +1039,20 @@ log_flush_internal(struct gfs_sbd *sdp, struct gfs_glock *gl)
 		sdp->sd_vfs->s_dirt = FALSE;
 
 	gfs_log_unlock(sdp);
+}
+
+/**
+ * gfs_log_flush - flush the whole incore log
+ * @sdp: the filesystem
+ *
+ */
+
+void
+gfs_log_flush(struct gfs_sbd *sdp)
+{
+	down(&sdp->sd_log_flush_lock); /* unlocked in gfs_sync_buf */
+	log_flush_internal(sdp, NULL);
+	up(&sdp->sd_log_flush_lock); /* locked in log_flush_internal */
 
 	/*  Dump if we need to.  */
 
@@ -1045,15 +1061,75 @@ log_flush_internal(struct gfs_sbd *sdp, struct gfs_glock *gl)
 }
 
 /**
- * gfs_log_flush - flush the whole incore log
- * @sdp: the filesystem
+ * 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 list_head *pos, *tmp;
+
+	spin_lock(&sdp->sd_ail_lock);
+
+	list_for_each_safe(pos, tmp, &gl->gl_ail_bufs) {
+		bd = list_entry(pos, struct gfs_bufdata, bd_ail_gl_list);
+		gfs_bd_ail_tryremove(sdp, bd);
+	}
+
+	spin_unlock(&sdp->sd_ail_lock);
+}
+
+/**
+ * gfs_inval_buf - Invalidate all buffers associated with a glock
+ * @gl: the glock
  *
  */
 
 void
-gfs_log_flush(struct gfs_sbd *sdp)
+gfs_inval_buf(struct gfs_glock *gl)
 {
-	log_flush_internal(sdp, NULL);
+	struct inode *aspace = gl->gl_aspace;
+	struct address_space *mapping = gl->gl_aspace->i_mapping;
+
+	/*down(&gl->gl_sbd->sd_log_flush_lock);*/
+	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);
+	/*up(&gl->gl_sbd->sd_log_flush_lock);*/
+}
+
+/**
+ * gfs_sync_buf - Sync all buffers associated with a glock
+ * @gl: The glock
+ * @flags: DIO_START | DIO_WAIT | DIO_CHECK
+ *
+ */
+
+static void
+gfs_sync_buf(struct gfs_glock *gl, int flags)
+{
+	struct address_space *mapping = gl->gl_aspace->i_mapping;
+	int error = 0;
+
+	error = filemap_fdatawrite(mapping);
+	if (!error)
+		error = filemap_fdatawait(mapping);
+	if (!error) {
+		if (!(flags & DIO_INVISIBLE))
+			ail_empty_gl(gl);
+	}
+	if (error)
+		gfs_io_error(gl->gl_sbd);
+
 }
 
 /**
@@ -1063,9 +1139,20 @@ gfs_log_flush(struct gfs_sbd *sdp)
  */
 
 void
-gfs_log_flush_glock(struct gfs_glock *gl)
+gfs_log_flush_glock(struct gfs_glock *gl, int flags)
 {
+	struct gfs_sbd *sdp = gl->gl_sbd;
+
+	down(&sdp->sd_log_flush_lock);
 	log_flush_internal(gl->gl_sbd, gl);
+	if (flags)
+		gfs_sync_buf(gl, flags | DIO_CHECK);
+	up(&sdp->sd_log_flush_lock);
+
+	/*  Dump if we need to.  */
+
+	if (test_bit(SDF_NEED_LOG_DUMP, &sdp->sd_flags))
+		gfs_log_dump(sdp, FALSE);
 }
 
 /**
diff --git a/gfs-kernel/src/gfs/log.h b/gfs-kernel/src/gfs/log.h
index e66b22b..159ec9f 100644
--- a/gfs-kernel/src/gfs/log.h
+++ b/gfs-kernel/src/gfs/log.h
@@ -50,7 +50,7 @@ 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_flush_glock(struct gfs_glock *gl, int flags);
 
 void gfs_log_shutdown(struct gfs_sbd *sdp);
 
diff --git a/gfs-kernel/src/gfs/ops_export.c b/gfs-kernel/src/gfs/ops_export.c
index e721c58..262af95 100644
--- a/gfs-kernel/src/gfs/ops_export.c
+++ b/gfs-kernel/src/gfs/ops_export.c
@@ -62,6 +62,9 @@ gfs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
 
 	atomic_inc(&get_v2sdp(sb)->sd_ops_export);
 
+	if (fh_type != fh_len)
+		return NULL;
+
 	memset(&parent, 0, sizeof(struct inode_cookie));
 
 	switch (fh_type) {
diff --git a/gfs-kernel/src/gfs/ops_file.c b/gfs-kernel/src/gfs/ops_file.c
index 3d748b6..03fcc73 100644
--- a/gfs-kernel/src/gfs/ops_file.c
+++ b/gfs-kernel/src/gfs/ops_file.c
@@ -604,7 +604,7 @@ do_write_direct_alloc(struct file *file, char *buf, size_t size, loff_t *offset,
 	 * 2. does gfs_log_flush_glock flush data ?
 	 */
 	if (file->f_flags & O_SYNC)
-		gfs_log_flush_glock(ip->i_gl);
+		gfs_log_flush_glock(ip->i_gl, 0);
 
 	gfs_inplace_release(ip);
 	gfs_quota_unlock_m(ip);
@@ -922,7 +922,7 @@ do_do_write_buf(struct file *file, char *buf, size_t size, loff_t *offset,
 	gfs_trans_end(sdp);
 
 	if (file->f_flags & O_SYNC || IS_SYNC(inode)) {
-		gfs_log_flush_glock(ip->i_gl);
+		gfs_log_flush_glock(ip->i_gl, 0);
 		error = filemap_fdatawrite(file->f_mapping);
 		if (error == 0)
 			error = filemap_fdatawait(file->f_mapping);
@@ -1626,7 +1626,7 @@ gfs_fsync(struct file *file, struct dentry *dentry, int datasync)
 		return error;
 
 	if (gfs_is_jdata(ip))
-		gfs_log_flush_glock(ip->i_gl);
+		gfs_log_flush_glock(ip->i_gl, 0);
 	else {
 		if ((!datasync) || (inode->i_state & I_DIRTY_DATASYNC)) {
 			struct writeback_control wbc = {
@@ -1636,7 +1636,7 @@ gfs_fsync(struct file *file, struct dentry *dentry, int datasync)
 			error = sync_inode(inode, &wbc);
 		}
 		if (gfs_is_stuffed(ip))
-			gfs_log_flush_glock(ip->i_gl);
+			gfs_log_flush_glock(ip->i_gl, 0);
 	}
 
 	gfs_glock_dq_uninit(&i_gh);
diff --git a/gfs-kernel/src/gfs/ops_fstype.c b/gfs-kernel/src/gfs/ops_fstype.c
index 1f5e8a5..34d9fa4 100644
--- a/gfs-kernel/src/gfs/ops_fstype.c
+++ b/gfs-kernel/src/gfs/ops_fstype.c
@@ -94,6 +94,7 @@ static struct gfs_sbd *init_sbd(struct super_block *sb)
 	INIT_LIST_HEAD(&sdp->sd_log_ail);
 	INIT_LIST_HEAD(&sdp->sd_log_incore);
 	init_rwsem(&sdp->sd_log_lock);
+	init_MUTEX(&sdp->sd_log_flush_lock);
 	INIT_LIST_HEAD(&sdp->sd_unlinked_list);
 	spin_lock_init(&sdp->sd_unlinked_lock);
 	INIT_LIST_HEAD(&sdp->sd_quota_list);
@@ -268,8 +269,10 @@ fail_mount:
 	gfs_glock_dq_uninit(mount_gh);
 
 fail:
-	while (sdp->sd_glockd_num--)
+	while (sdp->sd_glockd_num > 0) {
+		sdp->sd_glockd_num--;
 		kthread_stop(sdp->sd_glockd_process[sdp->sd_glockd_num]);
+	}
 
 	kthread_stop(sdp->sd_scand_process);
 
diff --git a/gfs-kernel/src/gfs/ops_super.c b/gfs-kernel/src/gfs/ops_super.c
index 6c513d2..1838e5d 100644
--- a/gfs-kernel/src/gfs/ops_super.c
+++ b/gfs-kernel/src/gfs/ops_super.c
@@ -56,7 +56,7 @@ gfs_write_inode(struct inode *inode, int sync)
 	atomic_inc(&ip->i_sbd->sd_ops_super);
 
 	if (ip && sync)
-		gfs_log_flush_glock(ip->i_gl);
+		gfs_log_flush_glock(ip->i_gl, 0);
 
 	return 0;
 }
@@ -124,8 +124,10 @@ gfs_put_super(struct super_block *sb)
 	kthread_stop(sdp->sd_recoverd_process);
 
 	/*  Kill off the glockd threads  */
-	while (sdp->sd_glockd_num--)
+	while (sdp->sd_glockd_num > 0) {
+		sdp->sd_glockd_num--;
 		kthread_stop(sdp->sd_glockd_process[sdp->sd_glockd_num]);
+	}
 
 	/*  Kill off the scand thread  */
 	kthread_stop(sdp->sd_scand_process);
diff --git a/gfs-kernel/src/gfs/quota.c b/gfs-kernel/src/gfs/quota.c
index 211b416..9e8089e 100644
--- a/gfs-kernel/src/gfs/quota.c
+++ b/gfs-kernel/src/gfs/quota.c
@@ -617,7 +617,7 @@ do_quota_sync(struct gfs_sbd *sdp, struct gfs_quota_data **qda,
 
 	kfree(ghs);
 
-	gfs_log_flush_glock(ip->i_gl);
+	gfs_log_flush_glock(ip->i_gl, 0);
 
 	return 0;
 
diff --git a/gfs-kernel/src/gfs/recovery.c b/gfs-kernel/src/gfs/recovery.c
index 9852bd2..401d043 100644
--- a/gfs-kernel/src/gfs/recovery.c
+++ b/gfs-kernel/src/gfs/recovery.c
@@ -24,6 +24,7 @@
 #include "glock.h"
 #include "glops.h"
 #include "lm.h"
+#include "log.h"
 #include "lops.h"
 #include "recovery.h"
 
@@ -252,6 +253,7 @@ gfs_find_jhead(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
 	seg1 = 0;
 	seg2 = jdesc->ji_nsegment - 1;
 
+	gfs_log_lock(sdp);
 	for (;;) {
 		seg_m = (seg1 + seg2) / 2;
 
@@ -280,6 +282,7 @@ gfs_find_jhead(struct gfs_sbd *sdp, struct gfs_jindex *jdesc,
 			seg2 = seg_m;
 	}
 
+	gfs_log_unlock(sdp);
 	return error;
 }
 
diff --git a/gfs-kernel/src/gfs/trans.c b/gfs-kernel/src/gfs/trans.c
index 494571f..e23017e 100644
--- a/gfs-kernel/src/gfs/trans.c
+++ b/gfs-kernel/src/gfs/trans.c
@@ -88,6 +88,9 @@ gfs_trans_begin_i(struct gfs_sbd *sdp,
 	unsigned int blocks;
 	int error;
 
+	if (test_bit(SDF_ROFS, &sdp->sd_flags))
+		return -EROFS;
+
 	tr = kmalloc(sizeof(struct gfs_trans), GFP_KERNEL);
 	if (!tr)
 		return -ENOMEM;
@@ -110,12 +113,6 @@ gfs_trans_begin_i(struct gfs_sbd *sdp,
 	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;


More information about the cluster-commits mailing list