Skip to content

Commit

Permalink
ext4: Make non-journal fsync work properly
Browse files Browse the repository at this point in the history
Teach ext4_write_inode() and ext4_do_update_inode() about non-journal
mode:  If we're not using a journal, ext4_write_inode() now calls
ext4_do_update_inode() (after getting the iloc via ext4_get_inode_loc())
with a new "do_sync" parameter.  If that parameter is nonzero _and_ we're
not using a journal, ext4_do_update_inode() calls sync_dirty_buffer()
instead of ext4_handle_dirty_metadata().

This problem was found in power-fail testing, checking the amount of
loss of files and blocks after a power failure when using fsync() and
when not using fsync().  It turned out that using fsync() was actually
worse than not doing so, possibly because it increased the likelihood
that the inodes would remain unflushed and would therefore be lost at
the power failure.

Signed-off-by: Frank Mayhar <fmayhar@google.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
  • Loading branch information
Frank Mayhar authored and Theodore Ts'o committed Sep 10, 2009
1 parent fe188c0 commit 91ac6f4
Showing 1 changed file with 40 additions and 14 deletions.
54 changes: 40 additions & 14 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -4538,7 +4538,8 @@ static int ext4_inode_blocks_set(handle_t *handle,
*/
static int ext4_do_update_inode(handle_t *handle,
struct inode *inode,
struct ext4_iloc *iloc)
struct ext4_iloc *iloc,
int do_sync)
{
struct ext4_inode *raw_inode = ext4_raw_inode(iloc);
struct ext4_inode_info *ei = EXT4_I(inode);
Expand Down Expand Up @@ -4640,10 +4641,22 @@ static int ext4_do_update_inode(handle_t *handle,
raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize);
}

BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
rc = ext4_handle_dirty_metadata(handle, inode, bh);
if (!err)
err = rc;
/*
* If we're not using a journal and we were called from
* ext4_write_inode() to sync the inode (making do_sync true),
* we can just use sync_dirty_buffer() directly to do our dirty
* work. Testing s_journal here is a bit redundant but it's
* worth it to avoid potential future trouble.
*/
if (EXT4_SB(inode->i_sb)->s_journal == NULL && do_sync) {
BUFFER_TRACE(bh, "call sync_dirty_buffer");
sync_dirty_buffer(bh);
} else {
BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
rc = ext4_handle_dirty_metadata(handle, inode, bh);
if (!err)
err = rc;
}
ei->i_state &= ~EXT4_STATE_NEW;

out_brelse:
Expand Down Expand Up @@ -4689,19 +4702,32 @@ static int ext4_do_update_inode(handle_t *handle,
*/
int ext4_write_inode(struct inode *inode, int wait)
{
int err;

if (current->flags & PF_MEMALLOC)
return 0;

if (ext4_journal_current_handle()) {
jbd_debug(1, "called recursively, non-PF_MEMALLOC!\n");
dump_stack();
return -EIO;
}
if (EXT4_SB(inode->i_sb)->s_journal) {
if (ext4_journal_current_handle()) {
jbd_debug(1, "called recursively, non-PF_MEMALLOC!\n");
dump_stack();
return -EIO;
}

if (!wait)
return 0;
if (!wait)
return 0;

err = ext4_force_commit(inode->i_sb);
} else {
struct ext4_iloc iloc;

return ext4_force_commit(inode->i_sb);
err = ext4_get_inode_loc(inode, &iloc);
if (err)
return err;
err = ext4_do_update_inode(EXT4_NOJOURNAL_HANDLE,
inode, &iloc, wait);
}
return err;
}

/*
Expand Down Expand Up @@ -4995,7 +5021,7 @@ int ext4_mark_iloc_dirty(handle_t *handle,
get_bh(iloc->bh);

/* ext4_do_update_inode() does jbd2_journal_dirty_metadata */
err = ext4_do_update_inode(handle, inode, iloc);
err = ext4_do_update_inode(handle, inode, iloc, 0);
put_bh(iloc->bh);
return err;
}
Expand Down

0 comments on commit 91ac6f4

Please sign in to comment.