aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs
diff options
context:
space:
mode:
authorLachlan McIlroy <lachlan@sgi.com>2007-10-11 21:13:35 -0400
committerTim Shimmin <tes@chook.melbourne.sgi.com>2007-10-16 00:22:28 -0400
commite893bffd4cf2f000f3058319eea5abeeb1755969 (patch)
treef72f91742c4e145df11168db118deb5ac2deb2b5 /fs/xfs
parentc2cba57e83dd7d2dda4ec425998b536669632c82 (diff)
[XFS] avoid race in sync_inodes() that can fail to write out all dirty data
In xfs_fs_sync_super() treat a sync the same as a filesystem freeze. This is needed to force the log to disk for inodes which are not marked dirty in the Linux inode (the inodes are marked dirty on completion of the log I/O) and so sync_inodes() will not flush them. In xfs_fs_write_inode() a synchronous flush will not get an EAGAIN from xfs_inode_flush() and if an asynchronous flush returns EAGAIN we should pass it on to the caller. If we get an error while flushing the inode then re-dirty it so we can try again later. SGI-PV: 971670 SGI-Modid: xfs-linux-melb:xfs-kern:29860a Signed-off-by: Lachlan McIlroy <lachlan@sgi.com> Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Tim Shimmin <tes@sgi.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r--fs/xfs/linux-2.6/xfs_super.c29
1 files changed, 20 insertions, 9 deletions
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c
index a1e3f3ea334b..02ec14eeb0ce 100644
--- a/fs/xfs/linux-2.6/xfs_super.c
+++ b/fs/xfs/linux-2.6/xfs_super.c
@@ -410,13 +410,12 @@ xfs_fs_write_inode(
410 flags |= FLUSH_SYNC; 410 flags |= FLUSH_SYNC;
411 } 411 }
412 error = xfs_inode_flush(XFS_I(inode), flags); 412 error = xfs_inode_flush(XFS_I(inode), flags);
413 if (error == EAGAIN) { 413 /*
414 if (sync) 414 * if we failed to write out the inode then mark
415 error = xfs_inode_flush(XFS_I(inode), 415 * it dirty again so we'll try again later.
416 flags | FLUSH_LOG); 416 */
417 else 417 if (error)
418 error = 0; 418 mark_inode_dirty_sync(inode);
419 }
420 419
421 return -error; 420 return -error;
422} 421}
@@ -622,7 +621,19 @@ xfs_fs_sync_super(
622 int error; 621 int error;
623 int flags; 622 int flags;
624 623
625 if (unlikely(sb->s_frozen == SB_FREEZE_WRITE)) { 624 /*
625 * Treat a sync operation like a freeze. This is to work
626 * around a race in sync_inodes() which works in two phases
627 * - an asynchronous flush, which can write out an inode
628 * without waiting for file size updates to complete, and a
629 * synchronous flush, which wont do anything because the
630 * async flush removed the inode's dirty flag. Also
631 * sync_inodes() will not see any files that just have
632 * outstanding transactions to be flushed because we don't
633 * dirty the Linux inode until after the transaction I/O
634 * completes.
635 */
636 if (wait || unlikely(sb->s_frozen == SB_FREEZE_WRITE)) {
626 /* 637 /*
627 * First stage of freeze - no more writers will make progress 638 * First stage of freeze - no more writers will make progress
628 * now we are here, so we flush delwri and delalloc buffers 639 * now we are here, so we flush delwri and delalloc buffers
@@ -633,7 +644,7 @@ xfs_fs_sync_super(
633 */ 644 */
634 flags = SYNC_DATA_QUIESCE; 645 flags = SYNC_DATA_QUIESCE;
635 } else 646 } else
636 flags = SYNC_FSDATA | (wait ? SYNC_WAIT : 0); 647 flags = SYNC_FSDATA;
637 648
638 error = xfs_sync(mp, flags); 649 error = xfs_sync(mp, flags);
639 sb->s_dirt = 0; 650 sb->s_dirt = 0;