ext4: sync the directory inode in ext4_sync_parent()

ext4 has taken the stance that, in the absence of a journal,
when an fsync/fdatasync of an inode is done, the parent
directory should be sync'ed if this inode entry is new.
ext4_sync_parent(), which implements this, does indeed sync
the dirent pages for parent directories, but it does not
sync the directory *inode*.  This patch fixes this.

Also now return error status from ext4_sync_parent().

I tested this using a power fail test, which panics a
machine running a file server getting requests from a
client.  Without this patch, on about every other test run,
the server is missing many, many files that had been synced.
With this patch, on > 6 runs, I see zero files being lost.

Google-Bug-Id: 4179519
Signed-off-by: Curt Wohlgemuth <curtw@google.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
This commit is contained in:
Curt Wohlgemuth 2011-04-10 22:05:31 -04:00 коммит произвёл Theodore Ts'o
Родитель 0449641130
Коммит 0893ed458b
1 изменённых файлов: 14 добавлений и 3 удалений

Просмотреть файл

@ -125,9 +125,11 @@ extern int ext4_flush_completed_IO(struct inode *inode)
* the parent directory's parent as well, and so on recursively, if * the parent directory's parent as well, and so on recursively, if
* they are also freshly created. * they are also freshly created.
*/ */
static void ext4_sync_parent(struct inode *inode) static int ext4_sync_parent(struct inode *inode)
{ {
struct writeback_control wbc;
struct dentry *dentry = NULL; struct dentry *dentry = NULL;
int ret = 0;
while (inode && ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) { while (inode && ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) {
ext4_clear_inode_state(inode, EXT4_STATE_NEWENTRY); ext4_clear_inode_state(inode, EXT4_STATE_NEWENTRY);
@ -136,8 +138,17 @@ static void ext4_sync_parent(struct inode *inode)
if (!dentry || !dentry->d_parent || !dentry->d_parent->d_inode) if (!dentry || !dentry->d_parent || !dentry->d_parent->d_inode)
break; break;
inode = dentry->d_parent->d_inode; inode = dentry->d_parent->d_inode;
sync_mapping_buffers(inode->i_mapping); ret = sync_mapping_buffers(inode->i_mapping);
if (ret)
break;
memset(&wbc, 0, sizeof(wbc));
wbc.sync_mode = WB_SYNC_ALL;
wbc.nr_to_write = 0; /* only write out the inode */
ret = sync_inode(inode, &wbc);
if (ret)
break;
} }
return ret;
} }
/* /*
@ -176,7 +187,7 @@ int ext4_sync_file(struct file *file, int datasync)
if (!journal) { if (!journal) {
ret = generic_file_fsync(file, datasync); ret = generic_file_fsync(file, datasync);
if (!ret && !list_empty(&inode->i_dentry)) if (!ret && !list_empty(&inode->i_dentry))
ext4_sync_parent(inode); ret = ext4_sync_parent(inode);
goto out; goto out;
} }