btrfs: add and use helper to assert an inode range is clean
We have four different scenarios where we don't expect to find ordered extents after locking a file range: 1) During plain fallocate; 2) During hole punching; 3) During zero range; 4) During reflinks (both cloning and deduplication). This is because in all these cases we follow the pattern: 1) Lock the inode's VFS lock in exclusive mode; 2) Lock the inode's i_mmap_lock in exclusive node, to serialize with mmap writes; 3) Flush delalloc in a file range and wait for all ordered extents to complete - both done through btrfs_wait_ordered_range(); 4) Lock the file range in the inode's io_tree. So add a helper that asserts that we don't have ordered extents for a given range. Make the four scenarios listed above use this helper after locking the respective file range. Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
committed by
David Sterba
parent
55961c8abf
commit
63c34cb4c6
@@ -3375,6 +3375,7 @@ void btrfs_inode_unlock(struct inode *inode, unsigned int ilock_flags);
|
|||||||
void btrfs_update_inode_bytes(struct btrfs_inode *inode,
|
void btrfs_update_inode_bytes(struct btrfs_inode *inode,
|
||||||
const u64 add_bytes,
|
const u64 add_bytes,
|
||||||
const u64 del_bytes);
|
const u64 del_bytes);
|
||||||
|
void btrfs_assert_inode_range_clean(struct btrfs_inode *inode, u64 start, u64 end);
|
||||||
|
|
||||||
/* ioctl.c */
|
/* ioctl.c */
|
||||||
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||||
|
|||||||
@@ -2608,6 +2608,8 @@ static void btrfs_punch_hole_lock_range(struct inode *inode,
|
|||||||
unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart,
|
unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart,
|
||||||
lockend, cached_state);
|
lockend, cached_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
btrfs_assert_inode_range_clean(BTRFS_I(inode), lockstart, lockend);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans,
|
static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans,
|
||||||
@@ -3479,6 +3481,8 @@ static long btrfs_fallocate(struct file *file, int mode,
|
|||||||
lock_extent_bits(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
|
lock_extent_bits(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
|
||||||
&cached_state);
|
&cached_state);
|
||||||
|
|
||||||
|
btrfs_assert_inode_range_clean(BTRFS_I(inode), alloc_start, locked_end);
|
||||||
|
|
||||||
/* First, check if we exceed the qgroup limit */
|
/* First, check if we exceed the qgroup limit */
|
||||||
INIT_LIST_HEAD(&reserve_list);
|
INIT_LIST_HEAD(&reserve_list);
|
||||||
while (cur_offset < alloc_end) {
|
while (cur_offset < alloc_end) {
|
||||||
|
|||||||
@@ -11261,6 +11261,41 @@ void btrfs_update_inode_bytes(struct btrfs_inode *inode,
|
|||||||
spin_unlock(&inode->lock);
|
spin_unlock(&inode->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that there are no ordered extents for a given file range.
|
||||||
|
*
|
||||||
|
* @inode: The target inode.
|
||||||
|
* @start: Start offset of the file range, should be sector size aligned.
|
||||||
|
* @end: End offset (inclusive) of the file range, its value +1 should be
|
||||||
|
* sector size aligned.
|
||||||
|
*
|
||||||
|
* This should typically be used for cases where we locked an inode's VFS lock in
|
||||||
|
* exclusive mode, we have also locked the inode's i_mmap_lock in exclusive mode,
|
||||||
|
* we have flushed all delalloc in the range, we have waited for all ordered
|
||||||
|
* extents in the range to complete and finally we have locked the file range in
|
||||||
|
* the inode's io_tree.
|
||||||
|
*/
|
||||||
|
void btrfs_assert_inode_range_clean(struct btrfs_inode *inode, u64 start, u64 end)
|
||||||
|
{
|
||||||
|
struct btrfs_root *root = inode->root;
|
||||||
|
struct btrfs_ordered_extent *ordered;
|
||||||
|
|
||||||
|
if (!IS_ENABLED(CONFIG_BTRFS_ASSERT))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ordered = btrfs_lookup_first_ordered_range(inode, start, end + 1 - start);
|
||||||
|
if (ordered) {
|
||||||
|
btrfs_err(root->fs_info,
|
||||||
|
"found unexpected ordered extent in file range [%llu, %llu] for inode %llu root %llu (ordered range [%llu, %llu])",
|
||||||
|
start, end, btrfs_ino(inode), root->root_key.objectid,
|
||||||
|
ordered->file_offset,
|
||||||
|
ordered->file_offset + ordered->num_bytes - 1);
|
||||||
|
btrfs_put_ordered_extent(ordered);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(ordered == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct inode_operations btrfs_dir_inode_operations = {
|
static const struct inode_operations btrfs_dir_inode_operations = {
|
||||||
.getattr = btrfs_getattr,
|
.getattr = btrfs_getattr,
|
||||||
.lookup = btrfs_lookup,
|
.lookup = btrfs_lookup,
|
||||||
|
|||||||
@@ -614,14 +614,23 @@ static void btrfs_double_extent_unlock(struct inode *inode1, u64 loff1,
|
|||||||
static void btrfs_double_extent_lock(struct inode *inode1, u64 loff1,
|
static void btrfs_double_extent_lock(struct inode *inode1, u64 loff1,
|
||||||
struct inode *inode2, u64 loff2, u64 len)
|
struct inode *inode2, u64 loff2, u64 len)
|
||||||
{
|
{
|
||||||
|
u64 range1_end = loff1 + len - 1;
|
||||||
|
u64 range2_end = loff2 + len - 1;
|
||||||
|
|
||||||
if (inode1 < inode2) {
|
if (inode1 < inode2) {
|
||||||
swap(inode1, inode2);
|
swap(inode1, inode2);
|
||||||
swap(loff1, loff2);
|
swap(loff1, loff2);
|
||||||
|
swap(range1_end, range2_end);
|
||||||
} else if (inode1 == inode2 && loff2 < loff1) {
|
} else if (inode1 == inode2 && loff2 < loff1) {
|
||||||
swap(loff1, loff2);
|
swap(loff1, loff2);
|
||||||
|
swap(range1_end, range2_end);
|
||||||
}
|
}
|
||||||
lock_extent(&BTRFS_I(inode1)->io_tree, loff1, loff1 + len - 1);
|
|
||||||
lock_extent(&BTRFS_I(inode2)->io_tree, loff2, loff2 + len - 1);
|
lock_extent(&BTRFS_I(inode1)->io_tree, loff1, range1_end);
|
||||||
|
lock_extent(&BTRFS_I(inode2)->io_tree, loff2, range2_end);
|
||||||
|
|
||||||
|
btrfs_assert_inode_range_clean(BTRFS_I(inode1), loff1, range1_end);
|
||||||
|
btrfs_assert_inode_range_clean(BTRFS_I(inode2), loff2, range2_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void btrfs_double_mmap_lock(struct inode *inode1, struct inode *inode2)
|
static void btrfs_double_mmap_lock(struct inode *inode1, struct inode *inode2)
|
||||||
|
|||||||
Reference in New Issue
Block a user