Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs
Pull btrfs fixes from Chris Mason: "Filipe is doing a careful pass through fsync problems, and these are the fixes so far. I'll have one more for rc6 that we're still testing. My big commit is fixing up some inode hash races that Al Viro found (thanks Al)" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs: Btrfs: use insert_inode_locked4 for inode creation Btrfs: fix fsync data loss after a ranged fsync Btrfs: kfree()ing ERR_PTRs Btrfs: fix crash while doing a ranged fsync Btrfs: fix corruption after write/fsync failure + fsync + log recovery Btrfs: fix autodefrag with compression
This commit is contained in:
Коммит
7ed641be75
|
@ -1966,7 +1966,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
|||
|
||||
btrfs_init_log_ctx(&ctx);
|
||||
|
||||
ret = btrfs_log_dentry_safe(trans, root, dentry, &ctx);
|
||||
ret = btrfs_log_dentry_safe(trans, root, dentry, start, end, &ctx);
|
||||
if (ret < 0) {
|
||||
/* Fallthrough and commit/free transaction. */
|
||||
ret = 1;
|
||||
|
|
191
fs/btrfs/inode.c
191
fs/btrfs/inode.c
|
@ -778,8 +778,12 @@ retry:
|
|||
ins.offset,
|
||||
BTRFS_ORDERED_COMPRESSED,
|
||||
async_extent->compress_type);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
btrfs_drop_extent_cache(inode, async_extent->start,
|
||||
async_extent->start +
|
||||
async_extent->ram_size - 1, 0);
|
||||
goto out_free_reserve;
|
||||
}
|
||||
|
||||
/*
|
||||
* clear dirty, set writeback and unlock the pages.
|
||||
|
@ -971,14 +975,14 @@ static noinline int cow_file_range(struct inode *inode,
|
|||
ret = btrfs_add_ordered_extent(inode, start, ins.objectid,
|
||||
ram_size, cur_alloc_size, 0);
|
||||
if (ret)
|
||||
goto out_reserve;
|
||||
goto out_drop_extent_cache;
|
||||
|
||||
if (root->root_key.objectid ==
|
||||
BTRFS_DATA_RELOC_TREE_OBJECTID) {
|
||||
ret = btrfs_reloc_clone_csums(inode, start,
|
||||
cur_alloc_size);
|
||||
if (ret)
|
||||
goto out_reserve;
|
||||
goto out_drop_extent_cache;
|
||||
}
|
||||
|
||||
if (disk_num_bytes < cur_alloc_size)
|
||||
|
@ -1006,6 +1010,8 @@ static noinline int cow_file_range(struct inode *inode,
|
|||
out:
|
||||
return ret;
|
||||
|
||||
out_drop_extent_cache:
|
||||
btrfs_drop_extent_cache(inode, start, start + ram_size - 1, 0);
|
||||
out_reserve:
|
||||
btrfs_free_reserved_extent(root, ins.objectid, ins.offset, 1);
|
||||
out_unlock:
|
||||
|
@ -4242,7 +4248,8 @@ out:
|
|||
btrfs_abort_transaction(trans, root, ret);
|
||||
}
|
||||
error:
|
||||
if (last_size != (u64)-1)
|
||||
if (last_size != (u64)-1 &&
|
||||
root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID)
|
||||
btrfs_ordered_update_i_size(inode, last_size, NULL);
|
||||
btrfs_free_path(path);
|
||||
return err;
|
||||
|
@ -5627,6 +5634,17 @@ int btrfs_set_inode_index(struct inode *dir, u64 *index)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int btrfs_insert_inode_locked(struct inode *inode)
|
||||
{
|
||||
struct btrfs_iget_args args;
|
||||
args.location = &BTRFS_I(inode)->location;
|
||||
args.root = BTRFS_I(inode)->root;
|
||||
|
||||
return insert_inode_locked4(inode,
|
||||
btrfs_inode_hash(inode->i_ino, BTRFS_I(inode)->root),
|
||||
btrfs_find_actor, &args);
|
||||
}
|
||||
|
||||
static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *dir,
|
||||
|
@ -5719,10 +5737,19 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||
sizes[1] = name_len + sizeof(*ref);
|
||||
}
|
||||
|
||||
location = &BTRFS_I(inode)->location;
|
||||
location->objectid = objectid;
|
||||
location->offset = 0;
|
||||
btrfs_set_key_type(location, BTRFS_INODE_ITEM_KEY);
|
||||
|
||||
ret = btrfs_insert_inode_locked(inode);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
path->leave_spinning = 1;
|
||||
ret = btrfs_insert_empty_items(trans, root, path, key, sizes, nitems);
|
||||
if (ret != 0)
|
||||
goto fail;
|
||||
goto fail_unlock;
|
||||
|
||||
inode_init_owner(inode, dir, mode);
|
||||
inode_set_bytes(inode, 0);
|
||||
|
@ -5745,11 +5772,6 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||
btrfs_mark_buffer_dirty(path->nodes[0]);
|
||||
btrfs_free_path(path);
|
||||
|
||||
location = &BTRFS_I(inode)->location;
|
||||
location->objectid = objectid;
|
||||
location->offset = 0;
|
||||
btrfs_set_key_type(location, BTRFS_INODE_ITEM_KEY);
|
||||
|
||||
btrfs_inherit_iflags(inode, dir);
|
||||
|
||||
if (S_ISREG(mode)) {
|
||||
|
@ -5760,7 +5782,6 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||
BTRFS_INODE_NODATASUM;
|
||||
}
|
||||
|
||||
btrfs_insert_inode_hash(inode);
|
||||
inode_tree_add(inode);
|
||||
|
||||
trace_btrfs_inode_new(inode);
|
||||
|
@ -5775,6 +5796,9 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||
btrfs_ino(inode), root->root_key.objectid, ret);
|
||||
|
||||
return inode;
|
||||
|
||||
fail_unlock:
|
||||
unlock_new_inode(inode);
|
||||
fail:
|
||||
if (dir && name)
|
||||
BTRFS_I(dir)->index_cnt--;
|
||||
|
@ -5909,28 +5933,28 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
|
||||
if (err) {
|
||||
drop_inode = 1;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the active LSM wants to access the inode during
|
||||
* d_instantiate it needs these. Smack checks to see
|
||||
* if the filesystem supports xattrs by looking at the
|
||||
* ops vector.
|
||||
*/
|
||||
|
||||
inode->i_op = &btrfs_special_inode_operations;
|
||||
err = btrfs_add_nondir(trans, dir, dentry, inode, 0, index);
|
||||
if (err)
|
||||
drop_inode = 1;
|
||||
else {
|
||||
init_special_inode(inode, inode->i_mode, rdev);
|
||||
|
||||
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
err = btrfs_add_nondir(trans, dir, dentry, inode, 0, index);
|
||||
if (err) {
|
||||
goto out_unlock_inode;
|
||||
} else {
|
||||
btrfs_update_inode(trans, root, inode);
|
||||
unlock_new_inode(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
btrfs_end_transaction(trans, root);
|
||||
btrfs_balance_delayed_items(root);
|
||||
|
@ -5940,6 +5964,12 @@ out_unlock:
|
|||
iput(inode);
|
||||
}
|
||||
return err;
|
||||
|
||||
out_unlock_inode:
|
||||
drop_inode = 1;
|
||||
unlock_new_inode(inode);
|
||||
goto out_unlock;
|
||||
|
||||
}
|
||||
|
||||
static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
||||
|
@ -5974,15 +6004,6 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
|||
goto out_unlock;
|
||||
}
|
||||
drop_inode_on_err = 1;
|
||||
|
||||
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
err = btrfs_update_inode(trans, root, inode);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
/*
|
||||
* If the active LSM wants to access the inode during
|
||||
* d_instantiate it needs these. Smack checks to see
|
||||
|
@ -5991,14 +6012,23 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
|||
*/
|
||||
inode->i_fop = &btrfs_file_operations;
|
||||
inode->i_op = &btrfs_file_inode_operations;
|
||||
inode->i_mapping->a_ops = &btrfs_aops;
|
||||
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
||||
|
||||
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
err = btrfs_update_inode(trans, root, inode);
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
err = btrfs_add_nondir(trans, dir, dentry, inode, 0, index);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
goto out_unlock_inode;
|
||||
|
||||
inode->i_mapping->a_ops = &btrfs_aops;
|
||||
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
||||
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
||||
unlock_new_inode(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
out_unlock:
|
||||
|
@ -6010,6 +6040,11 @@ out_unlock:
|
|||
btrfs_balance_delayed_items(root);
|
||||
btrfs_btree_balance_dirty(root);
|
||||
return err;
|
||||
|
||||
out_unlock_inode:
|
||||
unlock_new_inode(inode);
|
||||
goto out_unlock;
|
||||
|
||||
}
|
||||
|
||||
static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
|
@ -6117,25 +6152,30 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
}
|
||||
|
||||
drop_on_err = 1;
|
||||
/* these must be set before we unlock the inode */
|
||||
inode->i_op = &btrfs_dir_inode_operations;
|
||||
inode->i_fop = &btrfs_dir_file_operations;
|
||||
|
||||
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
|
||||
if (err)
|
||||
goto out_fail;
|
||||
|
||||
inode->i_op = &btrfs_dir_inode_operations;
|
||||
inode->i_fop = &btrfs_dir_file_operations;
|
||||
goto out_fail_inode;
|
||||
|
||||
btrfs_i_size_write(inode, 0);
|
||||
err = btrfs_update_inode(trans, root, inode);
|
||||
if (err)
|
||||
goto out_fail;
|
||||
goto out_fail_inode;
|
||||
|
||||
err = btrfs_add_link(trans, dir, inode, dentry->d_name.name,
|
||||
dentry->d_name.len, 0, index);
|
||||
if (err)
|
||||
goto out_fail;
|
||||
goto out_fail_inode;
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
/*
|
||||
* mkdir is special. We're unlocking after we call d_instantiate
|
||||
* to avoid a race with nfsd calling d_instantiate.
|
||||
*/
|
||||
unlock_new_inode(inode);
|
||||
drop_on_err = 0;
|
||||
|
||||
out_fail:
|
||||
|
@ -6145,6 +6185,10 @@ out_fail:
|
|||
btrfs_balance_delayed_items(root);
|
||||
btrfs_btree_balance_dirty(root);
|
||||
return err;
|
||||
|
||||
out_fail_inode:
|
||||
unlock_new_inode(inode);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
/* helper for btfs_get_extent. Given an existing extent in the tree,
|
||||
|
@ -8100,6 +8144,7 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
|
|||
|
||||
set_nlink(inode, 1);
|
||||
btrfs_i_size_write(inode, 0);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
err = btrfs_subvol_inherit_props(trans, new_root, parent_root);
|
||||
if (err)
|
||||
|
@ -8760,12 +8805,6 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
|
||||
if (err) {
|
||||
drop_inode = 1;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the active LSM wants to access the inode during
|
||||
* d_instantiate it needs these. Smack checks to see
|
||||
|
@ -8774,23 +8813,22 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
|||
*/
|
||||
inode->i_fop = &btrfs_file_operations;
|
||||
inode->i_op = &btrfs_file_inode_operations;
|
||||
|
||||
err = btrfs_add_nondir(trans, dir, dentry, inode, 0, index);
|
||||
if (err)
|
||||
drop_inode = 1;
|
||||
else {
|
||||
inode->i_mapping->a_ops = &btrfs_aops;
|
||||
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
||||
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
||||
}
|
||||
if (drop_inode)
|
||||
goto out_unlock;
|
||||
|
||||
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
err = btrfs_add_nondir(trans, dir, dentry, inode, 0, index);
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path) {
|
||||
err = -ENOMEM;
|
||||
drop_inode = 1;
|
||||
goto out_unlock;
|
||||
goto out_unlock_inode;
|
||||
}
|
||||
key.objectid = btrfs_ino(inode);
|
||||
key.offset = 0;
|
||||
|
@ -8799,9 +8837,8 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
|||
err = btrfs_insert_empty_item(trans, root, path, &key,
|
||||
datasize);
|
||||
if (err) {
|
||||
drop_inode = 1;
|
||||
btrfs_free_path(path);
|
||||
goto out_unlock;
|
||||
goto out_unlock_inode;
|
||||
}
|
||||
leaf = path->nodes[0];
|
||||
ei = btrfs_item_ptr(leaf, path->slots[0],
|
||||
|
@ -8825,12 +8862,15 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
|||
inode_set_bytes(inode, name_len);
|
||||
btrfs_i_size_write(inode, name_len);
|
||||
err = btrfs_update_inode(trans, root, inode);
|
||||
if (err)
|
||||
if (err) {
|
||||
drop_inode = 1;
|
||||
goto out_unlock_inode;
|
||||
}
|
||||
|
||||
unlock_new_inode(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
out_unlock:
|
||||
if (!err)
|
||||
d_instantiate(dentry, inode);
|
||||
btrfs_end_transaction(trans, root);
|
||||
if (drop_inode) {
|
||||
inode_dec_link_count(inode);
|
||||
|
@ -8838,6 +8878,11 @@ out_unlock:
|
|||
}
|
||||
btrfs_btree_balance_dirty(root);
|
||||
return err;
|
||||
|
||||
out_unlock_inode:
|
||||
drop_inode = 1;
|
||||
unlock_new_inode(inode);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
|
||||
|
@ -9021,14 +9066,6 @@ static int btrfs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = btrfs_init_inode_security(trans, inode, dir, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = btrfs_update_inode(trans, root, inode);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
inode->i_fop = &btrfs_file_operations;
|
||||
inode->i_op = &btrfs_file_inode_operations;
|
||||
|
||||
|
@ -9036,9 +9073,16 @@ static int btrfs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
||||
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
||||
|
||||
ret = btrfs_init_inode_security(trans, inode, dir, NULL);
|
||||
if (ret)
|
||||
goto out_inode;
|
||||
|
||||
ret = btrfs_update_inode(trans, root, inode);
|
||||
if (ret)
|
||||
goto out_inode;
|
||||
ret = btrfs_orphan_add(trans, inode);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto out_inode;
|
||||
|
||||
/*
|
||||
* We set number of links to 0 in btrfs_new_inode(), and here we set
|
||||
|
@ -9048,6 +9092,7 @@ static int btrfs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
* d_tmpfile() -> inode_dec_link_count() -> drop_nlink()
|
||||
*/
|
||||
set_nlink(inode, 1);
|
||||
unlock_new_inode(inode);
|
||||
d_tmpfile(dentry, inode);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
|
@ -9057,8 +9102,12 @@ out:
|
|||
iput(inode);
|
||||
btrfs_balance_delayed_items(root);
|
||||
btrfs_btree_balance_dirty(root);
|
||||
|
||||
return ret;
|
||||
|
||||
out_inode:
|
||||
unlock_new_inode(inode);
|
||||
goto out;
|
||||
|
||||
}
|
||||
|
||||
static const struct inode_operations btrfs_dir_inode_operations = {
|
||||
|
|
|
@ -1019,8 +1019,10 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em)
|
|||
return false;
|
||||
|
||||
next = defrag_lookup_extent(inode, em->start + em->len);
|
||||
if (!next || next->block_start >= EXTENT_MAP_LAST_BYTE ||
|
||||
(em->block_start + em->block_len == next->block_start))
|
||||
if (!next || next->block_start >= EXTENT_MAP_LAST_BYTE)
|
||||
ret = false;
|
||||
else if ((em->block_start + em->block_len == next->block_start) &&
|
||||
(em->block_len > 128 * 1024 && next->block_len > 128 * 1024))
|
||||
ret = false;
|
||||
|
||||
free_extent_map(next);
|
||||
|
@ -1055,7 +1057,6 @@ static int should_defrag_range(struct inode *inode, u64 start, int thresh,
|
|||
}
|
||||
|
||||
next_mergeable = defrag_check_next_extent(inode, em);
|
||||
|
||||
/*
|
||||
* we hit a real extent, if it is big or the next extent is not a
|
||||
* real extent, don't bother defragging it
|
||||
|
@ -1702,7 +1703,7 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
|
|||
~(BTRFS_SUBVOL_CREATE_ASYNC | BTRFS_SUBVOL_RDONLY |
|
||||
BTRFS_SUBVOL_QGROUP_INHERIT)) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
goto free_args;
|
||||
}
|
||||
|
||||
if (vol_args->flags & BTRFS_SUBVOL_CREATE_ASYNC)
|
||||
|
@ -1712,27 +1713,31 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
|
|||
if (vol_args->flags & BTRFS_SUBVOL_QGROUP_INHERIT) {
|
||||
if (vol_args->size > PAGE_CACHE_SIZE) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
goto free_args;
|
||||
}
|
||||
inherit = memdup_user(vol_args->qgroup_inherit, vol_args->size);
|
||||
if (IS_ERR(inherit)) {
|
||||
ret = PTR_ERR(inherit);
|
||||
goto out;
|
||||
goto free_args;
|
||||
}
|
||||
}
|
||||
|
||||
ret = btrfs_ioctl_snap_create_transid(file, vol_args->name,
|
||||
vol_args->fd, subvol, ptr,
|
||||
readonly, inherit);
|
||||
if (ret)
|
||||
goto free_inherit;
|
||||
|
||||
if (ret == 0 && ptr &&
|
||||
copy_to_user(arg +
|
||||
if (ptr && copy_to_user(arg +
|
||||
offsetof(struct btrfs_ioctl_vol_args_v2,
|
||||
transid), ptr, sizeof(*ptr)))
|
||||
transid),
|
||||
ptr, sizeof(*ptr)))
|
||||
ret = -EFAULT;
|
||||
out:
|
||||
kfree(vol_args);
|
||||
|
||||
free_inherit:
|
||||
kfree(inherit);
|
||||
free_args:
|
||||
kfree(vol_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2652,7 +2657,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
|
|||
vol_args = memdup_user(arg, sizeof(*vol_args));
|
||||
if (IS_ERR(vol_args)) {
|
||||
ret = PTR_ERR(vol_args);
|
||||
goto out;
|
||||
goto err_drop;
|
||||
}
|
||||
|
||||
vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
|
||||
|
@ -2670,6 +2675,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
|
|||
|
||||
out:
|
||||
kfree(vol_args);
|
||||
err_drop:
|
||||
mnt_drop_write_file(file);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -95,7 +95,9 @@
|
|||
|
||||
static int btrfs_log_inode(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct inode *inode,
|
||||
int inode_only);
|
||||
int inode_only,
|
||||
const loff_t start,
|
||||
const loff_t end);
|
||||
static int link_to_fixup_dir(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path, u64 objectid);
|
||||
|
@ -3859,7 +3861,9 @@ process:
|
|||
*/
|
||||
static int btrfs_log_inode(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct inode *inode,
|
||||
int inode_only)
|
||||
int inode_only,
|
||||
const loff_t start,
|
||||
const loff_t end)
|
||||
{
|
||||
struct btrfs_path *path;
|
||||
struct btrfs_path *dst_path;
|
||||
|
@ -3876,6 +3880,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
|
|||
int ins_nr;
|
||||
bool fast_search = false;
|
||||
u64 ino = btrfs_ino(inode);
|
||||
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
|
@ -4049,13 +4054,35 @@ log_extents:
|
|||
goto out_unlock;
|
||||
}
|
||||
} else if (inode_only == LOG_INODE_ALL) {
|
||||
struct extent_map_tree *tree = &BTRFS_I(inode)->extent_tree;
|
||||
struct extent_map *em, *n;
|
||||
|
||||
write_lock(&tree->lock);
|
||||
list_for_each_entry_safe(em, n, &tree->modified_extents, list)
|
||||
write_lock(&em_tree->lock);
|
||||
/*
|
||||
* We can't just remove every em if we're called for a ranged
|
||||
* fsync - that is, one that doesn't cover the whole possible
|
||||
* file range (0 to LLONG_MAX). This is because we can have
|
||||
* em's that fall outside the range we're logging and therefore
|
||||
* their ordered operations haven't completed yet
|
||||
* (btrfs_finish_ordered_io() not invoked yet). This means we
|
||||
* didn't get their respective file extent item in the fs/subvol
|
||||
* tree yet, and need to let the next fast fsync (one which
|
||||
* consults the list of modified extent maps) find the em so
|
||||
* that it logs a matching file extent item and waits for the
|
||||
* respective ordered operation to complete (if it's still
|
||||
* running).
|
||||
*
|
||||
* Removing every em outside the range we're logging would make
|
||||
* the next fast fsync not log their matching file extent items,
|
||||
* therefore making us lose data after a log replay.
|
||||
*/
|
||||
list_for_each_entry_safe(em, n, &em_tree->modified_extents,
|
||||
list) {
|
||||
const u64 mod_end = em->mod_start + em->mod_len - 1;
|
||||
|
||||
if (em->mod_start >= start && mod_end <= end)
|
||||
list_del_init(&em->list);
|
||||
write_unlock(&tree->lock);
|
||||
}
|
||||
write_unlock(&em_tree->lock);
|
||||
}
|
||||
|
||||
if (inode_only == LOG_INODE_ALL && S_ISDIR(inode->i_mode)) {
|
||||
|
@ -4065,8 +4092,19 @@ log_extents:
|
|||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
write_lock(&em_tree->lock);
|
||||
/*
|
||||
* If we're doing a ranged fsync and there are still modified extents
|
||||
* in the list, we must run on the next fsync call as it might cover
|
||||
* those extents (a full fsync or an fsync for other range).
|
||||
*/
|
||||
if (list_empty(&em_tree->modified_extents)) {
|
||||
BTRFS_I(inode)->logged_trans = trans->transid;
|
||||
BTRFS_I(inode)->last_log_commit = BTRFS_I(inode)->last_sub_trans;
|
||||
BTRFS_I(inode)->last_log_commit =
|
||||
BTRFS_I(inode)->last_sub_trans;
|
||||
}
|
||||
write_unlock(&em_tree->lock);
|
||||
out_unlock:
|
||||
if (unlikely(err))
|
||||
btrfs_put_logged_extents(&logged_list);
|
||||
|
@ -4161,7 +4199,10 @@ out:
|
|||
*/
|
||||
static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct inode *inode,
|
||||
struct dentry *parent, int exists_only,
|
||||
struct dentry *parent,
|
||||
const loff_t start,
|
||||
const loff_t end,
|
||||
int exists_only,
|
||||
struct btrfs_log_ctx *ctx)
|
||||
{
|
||||
int inode_only = exists_only ? LOG_INODE_EXISTS : LOG_INODE_ALL;
|
||||
|
@ -4207,7 +4248,7 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
|
|||
if (ret)
|
||||
goto end_no_trans;
|
||||
|
||||
ret = btrfs_log_inode(trans, root, inode, inode_only);
|
||||
ret = btrfs_log_inode(trans, root, inode, inode_only, start, end);
|
||||
if (ret)
|
||||
goto end_trans;
|
||||
|
||||
|
@ -4235,7 +4276,8 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
|
|||
|
||||
if (BTRFS_I(inode)->generation >
|
||||
root->fs_info->last_trans_committed) {
|
||||
ret = btrfs_log_inode(trans, root, inode, inode_only);
|
||||
ret = btrfs_log_inode(trans, root, inode, inode_only,
|
||||
0, LLONG_MAX);
|
||||
if (ret)
|
||||
goto end_trans;
|
||||
}
|
||||
|
@ -4269,13 +4311,15 @@ end_no_trans:
|
|||
*/
|
||||
int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct dentry *dentry,
|
||||
const loff_t start,
|
||||
const loff_t end,
|
||||
struct btrfs_log_ctx *ctx)
|
||||
{
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
int ret;
|
||||
|
||||
ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent,
|
||||
0, ctx);
|
||||
start, end, 0, ctx);
|
||||
dput(parent);
|
||||
|
||||
return ret;
|
||||
|
@ -4512,6 +4556,7 @@ int btrfs_log_new_name(struct btrfs_trans_handle *trans,
|
|||
root->fs_info->last_trans_committed))
|
||||
return 0;
|
||||
|
||||
return btrfs_log_inode_parent(trans, root, inode, parent, 1, NULL);
|
||||
return btrfs_log_inode_parent(trans, root, inode, parent, 0,
|
||||
LLONG_MAX, 1, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
|
|||
int btrfs_recover_log_trees(struct btrfs_root *tree_root);
|
||||
int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct dentry *dentry,
|
||||
const loff_t start,
|
||||
const loff_t end,
|
||||
struct btrfs_log_ctx *ctx);
|
||||
int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
|
|
Загрузка…
Ссылка в новой задаче