ext4: restart ext4_ext_remove_space() after transaction restart
If i_data_sem was internally dropped due to transaction restart, it is necessary to restart path look-up because extents tree was possibly modified by ext4_get_block(). https://bugzilla.kernel.org/show_bug.cgi?id=15827 Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Acked-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
786ec7915e
commit
0617b83fa2
1 changed files with 9 additions and 7 deletions
|
@ -107,11 +107,8 @@ static int ext4_ext_truncate_extend_restart(handle_t *handle,
|
||||||
if (err <= 0)
|
if (err <= 0)
|
||||||
return err;
|
return err;
|
||||||
err = ext4_truncate_restart_trans(handle, inode, needed);
|
err = ext4_truncate_restart_trans(handle, inode, needed);
|
||||||
/*
|
if (err == 0)
|
||||||
* We have dropped i_data_sem so someone might have cached again
|
err = -EAGAIN;
|
||||||
* an extent we are going to truncate.
|
|
||||||
*/
|
|
||||||
ext4_ext_invalidate_cache(inode);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -2359,7 +2356,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
|
||||||
int depth = ext_depth(inode);
|
int depth = ext_depth(inode);
|
||||||
struct ext4_ext_path *path;
|
struct ext4_ext_path *path;
|
||||||
handle_t *handle;
|
handle_t *handle;
|
||||||
int i = 0, err = 0;
|
int i, err;
|
||||||
|
|
||||||
ext_debug("truncate since %u\n", start);
|
ext_debug("truncate since %u\n", start);
|
||||||
|
|
||||||
|
@ -2368,23 +2365,26 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
|
||||||
if (IS_ERR(handle))
|
if (IS_ERR(handle))
|
||||||
return PTR_ERR(handle);
|
return PTR_ERR(handle);
|
||||||
|
|
||||||
|
again:
|
||||||
ext4_ext_invalidate_cache(inode);
|
ext4_ext_invalidate_cache(inode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We start scanning from right side, freeing all the blocks
|
* We start scanning from right side, freeing all the blocks
|
||||||
* after i_size and walking into the tree depth-wise.
|
* after i_size and walking into the tree depth-wise.
|
||||||
*/
|
*/
|
||||||
|
depth = ext_depth(inode);
|
||||||
path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), GFP_NOFS);
|
path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), GFP_NOFS);
|
||||||
if (path == NULL) {
|
if (path == NULL) {
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
path[0].p_depth = depth;
|
||||||
path[0].p_hdr = ext_inode_hdr(inode);
|
path[0].p_hdr = ext_inode_hdr(inode);
|
||||||
if (ext4_ext_check(inode, path[0].p_hdr, depth)) {
|
if (ext4_ext_check(inode, path[0].p_hdr, depth)) {
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
path[0].p_depth = depth;
|
i = err = 0;
|
||||||
|
|
||||||
while (i >= 0 && err == 0) {
|
while (i >= 0 && err == 0) {
|
||||||
if (i == depth) {
|
if (i == depth) {
|
||||||
|
@ -2478,6 +2478,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
|
||||||
out:
|
out:
|
||||||
ext4_ext_drop_refs(path);
|
ext4_ext_drop_refs(path);
|
||||||
kfree(path);
|
kfree(path);
|
||||||
|
if (err == -EAGAIN)
|
||||||
|
goto again;
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
Loading…
Add table
Reference in a new issue