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:
Родитель
0449641130
Коммит
0893ed458b
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче