make btrfs/153 sucessful on 5.4.y
Subsystems (2)
cover make btrfs/153 sucessful on 5.4.y
Patch 1-2 and 5 helps to fix the conflicts smoothly.
Patch 3-4 and 6 fixes the regression as reported by the
test case btrfs/153 and comes from the below patch-set (btrfs: qgroup: Fix the long-existing regression of btrfs/153)
Patch 7 fixes lockdep Warning as in the commit log now reported by the test case btrfs/153 on 5.4.y
Filipe Manana (1):
btrfs: fix lockdep splat when enabling and disabling qgroups
Nikolay Borisov (2):
btrfs: make qgroup_free_reserved_data take btrfs_inode
btrfs: make btrfs_qgroup_reserve_data take btrfs_inode
Qu Wenruo (4):
btrfs: qgroup: allow to unreserve range without releasing other ranges
btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
btrfs: transaction: Cleanup unused TRANS_STATE_BLOCKED
btrfs: qgroup: remove ASYNC_COMMIT mechanism in favor of reserve
retry-after-EDQUOT
fs/btrfs/ctree.h | 13 +- fs/btrfs/delalloc-space.c | 2 +- fs/btrfs/disk-io.c | 4 +- fs/btrfs/file.c | 7 +- fs/btrfs/qgroup.c | 308 ++++++++++++++++++++++++++++---------- fs/btrfs/qgroup.h | 2 +- fs/btrfs/transaction.c | 16 +- fs/btrfs/transaction.h | 15 -- 8 files changed, 246 insertions(+), 121 deletions(-)
Signature · 1 line
2.31.1
PATCH 1/7 btrfs: make qgroup_free_reserved_data take btrfs_inode
From: Nikolay Borisov <[email protected]>
commit df2cfd131fd33dbef1ce33be8b332b1f3d645f35 upstream
It only uses btrfs_inode so can just as easily take it as an argument.
--- fs/btrfs/qgroup.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
fs/btrfs/qgroup.c +6 -5
@@ -3481,10 +3481,10 @@ int btrfs_qgroup_reserve_data(struct inode *inode, }/* Free ranges specified by @reserved, normally in error path */ -static int qgroup_free_reserved_data (struct inode *inode, +static int qgroup_free_reserved_data (struct btrfs_inode *inode,struct extent_changeset *reserved,u64 start,u64 len) { -struct btrfs_root *root =BTRFS_I (inode)->root; +struct btrfs_root *root = inode->root;struct ulist_node *unode;struct ulist_iterator uiter;struct extent_changeset changeset;
@@ -3520,8 +3520,8 @@ static int qgroup_free_reserved_data(struct inode *inode,* EXTENT_QGROUP_RESERVED, we won't double free. * So not need to rush. */ - ret =clear_record_extent_bits (&BTRFS_I (inode)->io_tree, - free_start, free_start + free_len -1 , + ret =clear_record_extent_bits (&inode->io_tree, free_start, + free_start + free_len -1 ,EXTENT_QGROUP_RESERVED , &changeset);if (ret <0 ) goto out;
@@ -3550,7 +3550,8 @@ static int __btrfs_qgroup_release_data(struct inode *inode,/* In release case, we shouldn't have @reserved */ WARN_ON (!free && reserved);if (free && reserved) -return qgroup_free_reserved_data (inode, reserved, start, len); +return qgroup_free_reserved_data (BTRFS_I (inode), reserved, + start, len);extent_changeset_init (&changeset); ret =clear_record_extent_bits (&BTRFS_I (inode)->io_tree, start, start + len -1 ,EXTENT_QGROUP_RESERVED , &changeset);
Signature · 1 line
2.31.1
PATCH 2/7 btrfs: make btrfs_qgroup_reserve_data take btrfs_inode
In reply to Anand JainFrom: Nikolay Borisov <[email protected]>
commit 7661a3e033ab782366e0e1f4b6aad0df3555fcbd upstream
There's only a single use of vfs_inode in a tracepoint so let's take btrfs_inode directly.
--- fs/btrfs/delalloc-space.c | 2 +- fs/btrfs/file.c | 7 ++++--- fs/btrfs/qgroup.c | 10 +++++----- fs/btrfs/qgroup.h | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-)
fs/btrfs/delalloc-space.c +1 -1
@@ -151,7 +151,7 @@ int btrfs_check_data_free_space(struct inode *inode,return ret;/* Use new btrfs_qgroup_reserve_data to reserve precious data space. */ - ret =btrfs_qgroup_reserve_data (inode, reserved, start, len); + ret =btrfs_qgroup_reserve_data (BTRFS_I (inode), reserved, start, len);if (ret <0 )btrfs_free_reserved_data_space_noquota (inode, start, len);else
fs/btrfs/file.c +4 -3
@@ -3149,7 +3149,7 @@ static int btrfs_zero_range(struct inode *inode, &cached_state);if (ret) goto out; - ret =btrfs_qgroup_reserve_data (inode, &data_reserved, + ret =btrfs_qgroup_reserve_data (BTRFS_I (inode), &data_reserved, alloc_start, bytes_to_reserve);if (ret) { unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart,
@@ -3322,8 +3322,9 @@ static long btrfs_fallocate(struct file *file, int mode,free_extent_map (em);break ; } - ret =btrfs_qgroup_reserve_data (inode, &data_reserved, - cur_offset, last_byte - cur_offset); + ret =btrfs_qgroup_reserve_data (BTRFS_I (inode), + &data_reserved, cur_offset, + last_byte - cur_offset);if (ret <0 ) { cur_offset = last_byte;free_extent_map (em);
fs/btrfs/qgroup.c +5 -5
@@ -3425,11 +3425,11 @@ btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info)* same @reserved, caller must ensure when error happens it's OK * to free *ALL* reserved space. */ -int btrfs_qgroup_reserve_data (struct inode *inode, +int btrfs_qgroup_reserve_data (struct btrfs_inode *inode,struct extent_changeset **reserved_ret,u64 start,u64 len) { -struct btrfs_root *root =BTRFS_I (inode)->root; +struct btrfs_root *root = inode->root;struct ulist_node *unode;struct ulist_iterator uiter;struct extent_changeset *reserved;
@@ -3452,12 +3452,12 @@ int btrfs_qgroup_reserve_data(struct inode *inode, reserved = *reserved_ret;/* Record already reserved space */ orig_reserved = reserved->bytes_changed; - ret =set_record_extent_bits (&BTRFS_I (inode)->io_tree, start, + ret =set_record_extent_bits (&inode->io_tree, start, start + len -1 ,EXTENT_QGROUP_RESERVED , reserved);/* Newly reserved space */ to_reserve = reserved->bytes_changed - orig_reserved; -trace_btrfs_qgroup_reserve_data (inode, start, len, +trace_btrfs_qgroup_reserve_data (&inode->vfs_inode, start, len, to_reserve,QGROUP_RESERVE );if (ret <0 ) goto cleanup;
@@ -3471,7 +3471,7 @@ int btrfs_qgroup_reserve_data(struct inode *inode,/* cleanup *ALL* already reserved ranges */ ULIST_ITER_INIT (&uiter);while ((unode =ulist_next (&reserved->range_changed, &uiter))) -clear_extent_bit (&BTRFS_I (inode)->io_tree, unode->val, +clear_extent_bit (&inode->io_tree, unode->val, unode->aux,EXTENT_QGROUP_RESERVED ,0 ,0 ,NULL );/* Also free data bytes of already reserved one */ btrfs_qgroup_free_refroot(root->fs_info, root->root_key.objectid,
fs/btrfs/qgroup.h +1 -1
@@ -344,7 +344,7 @@ int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid,#endif /* New io_tree based accurate qgroup reserve API */ -int btrfs_qgroup_reserve_data (struct inode *inode, +int btrfs_qgroup_reserve_data (struct btrfs_inode *inode,struct extent_changeset **reserved,u64 start,u64 len);int btrfs_qgroup_release_data (struct inode *inode,u64 start,u64 len); int btrfs_qgroup_free_data(struct inode *inode,
Signature · 1 line
2.31.1
PATCH 3/7 btrfs: qgroup: allow to unreserve range without releasing other ranges
In reply to Anand JainFrom: Qu Wenruo <[email protected]>
commit 263da812e87bac4098a4778efaa32c54275641db upstream
[PROBLEM]
Before this patch, when btrfs_qgroup_reserve_data() fails, we free all reserved space of the changeset.
For example:
ret = btrfs_qgroup_reserve_data(inode, changeset, 0, SZ_1M); ret = btrfs_qgroup_reserve_data(inode, changeset, SZ_1M, SZ_1M); ret = btrfs_qgroup_reserve_data(inode, changeset, SZ_2M, SZ_1M);
If the last btrfs_qgroup_reserve_data() failed, it will release the entire [0, 3M) range.
This behavior is kind of OK for now, as when we hit -EDQUOT, we normally go error handling and need to release all reserved ranges anyway.
But this also means the following call is not possible:
ret = btrfs_qgroup_reserve_data();
if (ret == -EDQUOT) {
/* Do something to free some qgroup space */
ret = btrfs_qgroup_reserve_data();
}As if the first btrfs_qgroup_reserve_data() fails, it will free all reserved qgroup space.
[CAUSE]
This is because we release all reserved ranges when btrfs_qgroup_reserve_data() fails.
[FIX]
This patch will implement a new function, qgroup_unreserve_range(), to iterate through the ulist nodes, to find any nodes in the failure range, and remove the EXTENT_QGROUP_RESERVED bits from the io_tree, and decrease the extent_changeset::bytes_changed, so that we can revert to previous state.
This allows later patches to retry btrfs_qgroup_reserve_data() if EDQUOT happens.
--- fs/btrfs/qgroup.c | 92 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 15 deletions(-)
fs/btrfs/qgroup.c +77 -15
@@ -3411,6 +3411,73 @@ btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info) } } +#define rbtree_iterate_from_safe (node, next, start) \ + for (node = start; node && ({ next = rb_next(node); 1;}); node = next) + +static int qgroup_unreserve_range (struct btrfs_inode *inode, +struct extent_changeset *reserved,u64 start, +u64 len) +{ +struct rb_node *node; +struct rb_node *next; +struct ulist_node *entry =NULL ; +int ret =0 ; + + node = reserved->range_changed.root.rb_node; +while (node) { + entry = rb_entry(node, struct ulist_node, rb_node); +if (entry->val < start) + node = node->rb_right; +else if (entry) + node = node->rb_left; +else +break ; + } + +/* Empty changeset */ +if (!entry) +return 0 ; + +if (entry->val > start &&rb_prev (&entry->rb_node)) + entry = rb_entry(rb_prev(&entry->rb_node), struct ulist_node, + rb_node); + + rbtree_iterate_from_safe(node, next, &entry->rb_node) { +u64 entry_start; +u64 entry_end; +u64 entry_len; +int clear_ret; + + entry = rb_entry(node, struct ulist_node, rb_node); + entry_start = entry->val; + entry_end = entry->aux; + entry_len = entry_end - entry_start +1 ; + +if (entry_start >= start + len) +break ; +if (entry_start + entry_len <= start) +continue ; +/* +* Now the entry is in [start, start + len), revert the +* EXTENT_QGROUP_RESERVED bit. +*/ + clear_ret =clear_extent_bits (&inode->io_tree, entry_start, + entry_end,EXTENT_QGROUP_RESERVED ); +if (!ret && clear_ret <0 ) + ret = clear_ret; + +ulist_del (&reserved->range_changed, entry->val, entry->aux); +if (likely (reserved->bytes_changed >= entry_len)) { + reserved->bytes_changed -= entry_len; + }else { +WARN_ON (1 ); + reserved->bytes_changed =0 ; + } + } + +return ret; +} +/* * Reserve qgroup space for range [start, start + len). *
@@ -3421,18 +3488,14 @@ btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info)* Return <0 for error (including -EQUOT) * * NOTE: this function may sleep for memory allocation. -* if btrfs_qgroup_reserve_data() is called multiple times with -* same @reserved, caller must ensure when error happens it's OK -* to free *ALL* reserved space. */ int btrfs_qgroup_reserve_data (struct btrfs_inode *inode,struct extent_changeset **reserved_ret,u64 start,u64 len) {struct btrfs_root *root = inode->root; -struct ulist_node *unode; -struct ulist_iterator uiter;struct extent_changeset *reserved; +bool new_reserved = false;u64 orig_reserved;u64 to_reserve;int ret;
@@ -3445,6 +3508,7 @@ int btrfs_qgroup_reserve_data(struct btrfs_inode *inode,if (WARN_ON (!reserved_ret))return -EINVAL ;if (!*reserved_ret) { + new_reserved = true; *reserved_ret =extent_changeset_alloc ();if (!*reserved_ret)return -ENOMEM ;
@@ -3460,7 +3524,7 @@ int btrfs_qgroup_reserve_data(struct btrfs_inode *inode,trace_btrfs_qgroup_reserve_data (&inode->vfs_inode, start, len, to_reserve,QGROUP_RESERVE );if (ret <0 ) - goto cleanup; + goto out; ret =qgroup_reserve (root, to_reserve, true,BTRFS_QGROUP_RSV_DATA );if (ret <0 ) goto cleanup;
@@ -3468,15 +3532,13 @@ int btrfs_qgroup_reserve_data(struct btrfs_inode *inode,return ret; cleanup: -/* cleanup *ALL* already reserved ranges */ -ULIST_ITER_INIT (&uiter); -while ((unode =ulist_next (&reserved->range_changed, &uiter))) -clear_extent_bit (&inode->io_tree, unode->val, - unode->aux,EXTENT_QGROUP_RESERVED ,0 ,0 ,NULL ); -/* Also free data bytes of already reserved one */ -btrfs_qgroup_free_refroot (root->fs_info, root->root_key.objectid, - orig_reserved,BTRFS_QGROUP_RSV_DATA ); -extent_changeset_release (reserved); +qgroup_unreserve_range (inode, reserved, start, len); +out: +if (new_reserved) { +extent_changeset_release (reserved); +kfree (reserved); + *reserved_ret =NULL ; + }return ret; }
Signature · 1 line
2.31.1
PATCH 4/7 btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
In reply to Anand JainFrom: Qu Wenruo <[email protected]>
commit c53e9653605dbf708f5be02902de51831be4b009 upstream
[PROBLEM]
There are known problem related to how btrfs handles qgroup reserved space. One of the most obvious case is the the test case btrfs/153, which do fallocate, then write into the preallocated range.
btrfs/153 1s ... - output mismatch (see xfstests-dev/results//btrfs/153.out.bad)
--- tests/btrfs/153.out 2019-10-22 15:18:14.068965341 +0800
+++ xfstests-dev/results//btrfs/153.out.bad 2020-07-01 20:24:40.730000089 +0800
@@ -1,2 +1,5 @@
QA output created by 153
+pwrite: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
Silence is golden
...
(Run 'diff -u xfstests-dev/tests/btrfs/153.out xfstests-dev/results//btrfs/153.out.bad' to see the entire diff)
[CAUSE]
Since commit c6887cd11149 ("Btrfs: don't do nocow check unless we have to"), we always reserve space no matter if it's COW or not.
Such behavior change is mostly for performance, and reverting it is not a good idea anyway.
For preallcoated extent, we reserve qgroup data space for it already, and since we also reserve data space for qgroup at buffered write time, it needs twice the space for us to write into preallocated space.
This leads to the -EDQUOT in buffered write routine.
And we can't follow the same solution, unlike data/meta space check, qgroup reserved space is shared between data/metadata.
The EDQUOT can happen at the metadata reservation, so doing NODATACOW check after qgroup reservation failure is not a solution.
[FIX]
To solve the problem, we don't return -EDQUOT directly, but every time we got a -EDQUOT, we try to flush qgroup space:
- Flush all inodes of the root
NODATACOW writes will free the qgroup reserved at run_dealloc_range(). However we don't have the infrastructure to only flush NODATACOW inodes, here we flush all inodes anyway.
- Wait for ordered extents
This would convert the preallocated metadata space into per-trans metadata, which can be freed in later transaction commit.
- Commit transaction
This will free all per-trans metadata space.
Also we don't want to trigger flush multiple times, so here we introduce a per-root wait list and a new root status, to ensure only one thread starts the flushing.
--- fs/btrfs/ctree.h | 3 ++ fs/btrfs/disk-io.c | 1 + fs/btrfs/qgroup.c | 100 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 96 insertions(+), 8 deletions(-)
fs/btrfs/ctree.h +3 -0
@@ -945,6 +945,8 @@ enum {BTRFS_ROOT_DEAD_TREE ,/* The root has a log tree. Used only for subvolume roots. */ BTRFS_ROOT_HAS_LOG_TREE , +/* Qgroup flushing is in progress */ + BTRFS_ROOT_QGROUP_FLUSHING, };/*
@@ -1097,6 +1099,7 @@ struct btrfs_root {spinlock_t qgroup_meta_rsv_lock;u64 qgroup_meta_rsv_pertrans;u64 qgroup_meta_rsv_prealloc; +wait_queue_head_t qgroup_flush_wait;/* Number of active swapfiles */ atomic_t nr_swapfiles;
fs/btrfs/disk-io.c +1 -0
@@ -1154,6 +1154,7 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,mutex_init (&root->log_mutex);mutex_init (&root->ordered_extent_mutex);mutex_init (&root->delalloc_mutex); +init_waitqueue_head (&root->qgroup_flush_wait);init_waitqueue_head (&root->log_writer_wait);init_waitqueue_head (&root->log_commit_wait[0 ]);init_waitqueue_head (&root->log_commit_wait[1 ]);
fs/btrfs/qgroup.c +92 -8
128 lines not on this page · raw message
Signature · 1 line
2.31.1
Re: [PATCH 4/7] btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
Quoted 3 lines · Anand Jain
From: Qu Wenruo <[email protected]>
commit
c53e9653605dbf708f5be02902de51831be4b009upstream
This lacks certain upstream fixes for it:
f9baa501b4fd6962257853d46ddffbc21f27e344 btrfs: fix deadlock when cloning inline extents and using qgroups
4d14c5cde5c268a2bc26addecf09489cb953ef64 btrfs: don't flush from btrfs_delayed_inode_reserve_metadata
6f23277a49e68f8a9355385c846939ad0b1261e7 btrfs: qgroup: don't commit transaction when we already hold the handle
All these fixes are to ensure we don't try to flush in context where we shouldn't.
Without them, it can hit various deadlock.
Thanks,
Qu
Quoted 238 lines
[PROBLEM]
There are known problem related to how btrfs handles qgroup reserved space. One of the most obvious case is the the test case btrfs/153, which do fallocate, then write into the preallocated range.btrfs/153 1s ... - output mismatch (see xfstests-dev/results//btrfs/153.out.bad)
--- tests/btrfs/153.out 2019-10-22 15:18:14.068965341 +0800
+++ xfstests-dev/results//btrfs/153.out.bad 2020-07-01 20:24:40.730000089 +0800
@@ -1,2 +1,5 @@
QA output created by 153
+pwrite: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
Silence is golden
...
(Run 'diff -u xfstests-dev/tests/btrfs/153.out xfstests-dev/results//btrfs/153.out.bad' to see the entire diff)[CAUSE]
Since commitc6887cd11149("Btrfs: don't do nocow check unless we have to"), we always reserve space no matter if it's COW or not.Such behavior change is mostly for performance, and reverting it is not a good idea anyway.
For preallcoated extent, we reserve qgroup data space for it already, and since we also reserve data space for qgroup at buffered write time, it needs twice the space for us to write into preallocated space.
This leads to the
-EDQUOTin buffered write routine.And we can't follow the same solution, unlike data/meta space check, qgroup reserved space is shared between data/metadata.
The EDQUOT can happen at the metadata reservation, so doing NODATACOW check after qgroup reservation failure is not a solution.[FIX]
To solve the problem, we don't return-EDQUOTdirectly, but every time we got a-EDQUOT, we try to flush qgroup space:- Flush all inodes of the root
NODATACOW writes will free the qgroup reserved atrun_dealloc_range(). However we don't have the infrastructure to only flush NODATACOW inodes, here we flush all inodes anyway.- Wait for ordered extents
This would convert the preallocated metadata space into per-trans metadata, which can be freed in later transaction commit.- Commit transaction
This will free all per-trans metadata space.Also we don't want to trigger flush multiple times, so here we introduce a per-root wait list and a new root status, to ensure only one thread starts the flushing.
Fixes c6887cd11149 ("Btrfs: don't do nocow check unless we have to")Reviewed-by Josef Bacik <[email protected]>Signed-off-by Qu Wenruo <[email protected]>Reviewed-by David Sterba <[email protected]>Signed-off-by David Sterba <[email protected]>Signed-off-by Anand Jain <[email protected]>--- fs/btrfs/ctree.h | 3 ++ fs/btrfs/disk-io.c | 1 + fs/btrfs/qgroup.c | 100 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 96 insertions(+), 8 deletions(-)fs/btrfs/ctree.h +3
-0@@ -945,6 +945,8 @@ enum {BTRFS_ROOT_DEAD_TREE ,/* The root has a log tree. Used only for subvolume roots. */ BTRFS_ROOT_HAS_LOG_TREE , +/* Qgroup flushing is in progress */ + BTRFS_ROOT_QGROUP_FLUSHING, };/* @@ -1097,6 +1099,7 @@ struct btrfs_root {spinlock_t qgroup_meta_rsv_lock;u64 qgroup_meta_rsv_pertrans;u64 qgroup_meta_rsv_prealloc; +wait_queue_head_t qgroup_flush_wait;/* Number of active swapfiles */ atomic_t nr_swapfiles;fs/btrfs/disk-io.c +1
-0@@ -1154,6 +1154,7 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,mutex_init (&root->log_mutex);mutex_init (&root->ordered_extent_mutex);mutex_init (&root->delalloc_mutex); +init_waitqueue_head (&root->qgroup_flush_wait);init_waitqueue_head (&root->log_writer_wait);init_waitqueue_head (&root->log_commit_wait[0 ]);init_waitqueue_head (&root->log_commit_wait[1 ]);fs/btrfs/qgroup.c +92
-8@@ -3479,17 +3479,58 @@ static int qgroup_unreserve_range(struct btrfs_inode *inode, }/* -* Reserve qgroup space for range [start, start + len). +* Try to free some space for qgroup. * -* This function will either reserve space from related qgroups or doing -* nothing if the range is already reserved. +* For qgroup, there are only 3 ways to free qgroup space: +* - Flush nodatacow write +* Any nodatacow write will free its reserved data space at run_delalloc_range(). +* In theory, we should only flush nodatacow inodes, but it's not yet +* possible, so we need to flush the whole root. * -* Return 0 for successful reserve -* Return <0 for error (including -EQUOT) +* - Wait for ordered extents +* When ordered extents are finished, their reserved metadata is finally +* converted to per_trans status, which can be freed by later commit +* transaction. * -* NOTE: this function may sleep for memory allocation. +* - Commit transaction +* This would free the meta_per_trans space. +* In theory this shouldn't provide much space, but any more qgroup space +* is needed. */ -int btrfs_qgroup_reserve_data (struct btrfs_inode *inode, +static int try_flush_qgroup (struct btrfs_root *root) +{ +struct btrfs_trans_handle *trans; +int ret; + +/* +* We don't want to run flush again and again, so if there is a running +* one, we won't try to start a new flush, but exit directly. +*/ +if (test_and_set_bit (BTRFS_ROOT_QGROUP_FLUSHING , &root->state)) { +wait_event (root->qgroup_flush_wait, + !test_bit (BTRFS_ROOT_QGROUP_FLUSHING , &root->state)); +return 0 ; + } + + ret =btrfs_start_delalloc_snapshot (root); +if (ret <0 ) + goto out; +btrfs_wait_ordered_extents (root,U64_MAX ,0 , (u64)-1 ); + + trans =btrfs_join_transaction (root); +if (IS_ERR (trans)) { + ret =PTR_ERR (trans); + goto out; + } + + ret =btrfs_commit_transaction (trans); +out: +clear_bit (BTRFS_ROOT_QGROUP_FLUSHING , &root->state); +wake_up (&root->qgroup_flush_wait); +return ret; +} + +static int qgroup_reserve_data (struct btrfs_inode *inode,struct extent_changeset **reserved_ret,u64 start,u64 len) {@@ -3542,6 +3583,34 @@ int btrfs_qgroup_reserve_data(struct btrfs_inode *inode,return ret; } +/* +* Reserve qgroup space for range [start, start + len). +* +* This function will either reserve space from related qgroups or do nothing +* if the range is already reserved. +* +* Return 0 for successful reservation +* Return <0 for error (including -EQUOT) +* +* NOTE: This function may sleep for memory allocation, dirty page flushing and +* commit transaction. So caller should not hold any dirty page locked. +*/ +int btrfs_qgroup_reserve_data (struct btrfs_inode *inode, +struct extent_changeset **reserved_ret,u64 start, +u64 len) +{ +int ret; + + ret =qgroup_reserve_data (inode, reserved_ret, start, len); +if (ret <=0 && ret != -EDQUOT ) +return ret; + + ret =try_flush_qgroup (inode->root); +if (ret <0 ) +return ret; +return qgroup_reserve_data (inode, reserved_ret, start, len); +} +/* Free ranges specified by @reserved, normally in error path */ static int qgroup_free_reserved_data (struct btrfs_inode *inode,struct extent_changeset *reserved,u64 start,u64 len)@@ -3712,7 +3781,7 @@ static int sub_root_meta_rsv(struct btrfs_root *root, int num_bytes,return num_bytes; } -int __btrfs_qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, +static int qgroup_reserve_meta (struct btrfs_root *root,int num_bytes,enum btrfs_qgroup_rsv_type type,bool enforce) {struct btrfs_fs_info *fs_info = root->fs_info;@@ -3739,6 +3808,21 @@ int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,return ret; } +int __btrfs_qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, +enum btrfs_qgroup_rsv_type type,bool enforce) +{ +int ret; + + ret =qgroup_reserve_meta (root, num_bytes, type, enforce); +if (ret <=0 && ret != -EDQUOT ) +return ret; + + ret =try_flush_qgroup (root); +if (ret <0 ) +return ret; +return qgroup_reserve_meta (root, num_bytes, type, enforce); +} +void btrfs_qgroup_free_meta_all_pertrans (struct btrfs_root *root) {struct btrfs_fs_info *fs_info = root->fs_info;
Re: [PATCH 4/7] btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
Quoted 15 lines · Qu Wenruo
Quoted 3 lines · Anand Jain
From: Qu Wenruo <[email protected]>
commit
c53e9653605dbf708f5be02902de51831be4b009upstreamThis lacks certain upstream fixes for it:
f9baa501b4fd6962257853d46ddffbc21f27e344btrfs: fix deadlock when cloning inline extents and using qgroups
4d14c5cde5c268a2bc26addecf09489cb953ef64btrfs: don't flush frombtrfs_delayed_inode_reserve_metadata
Quoted 8 lines
6f23277a49e68f8a9355385c846939ad0b1261e7btrfs: qgroup: don't commit transaction when we already hold the handleAll these fixes are to ensure we don't try to flush in context where we shouldn't.
Without them, it can hit various deadlock.
Qu,
Thanks for taking a look. I will send it in v2.
-Anand
Quoted 252 lines
Thanks,
QuQuoted 250 lines
[PROBLEM]
There are known problem related to how btrfs handles qgroup reserved space. One of the most obvious case is the the test case btrfs/153, which do fallocate, then write into the preallocated range.btrfs/153 1s ... - output mismatch (see xfstests-dev/results//btrfs/153.out.bad)
--- tests/btrfs/153.out 2019-10-22 15:18:14.068965341 +0800
+++ xfstests-dev/results//btrfs/153.out.bad 2020-07-01 20:24:40.730000089 +0800
@@ -1,2 +1,5 @@
QA output created by 153
+pwrite: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
Silence is golden
...
(Run 'diff -u xfstests-dev/tests/btrfs/153.out xfstests-dev/results//btrfs/153.out.bad' to see the entire diff)[CAUSE]
Since commitc6887cd11149("Btrfs: don't do nocow check unless we have to"), we always reserve space no matter if it's COW or not.Such behavior change is mostly for performance, and reverting it is not a good idea anyway.
For preallcoated extent, we reserve qgroup data space for it already, and since we also reserve data space for qgroup at buffered write time, it needs twice the space for us to write into preallocated space.
This leads to the
-EDQUOTin buffered write routine.And we can't follow the same solution, unlike data/meta space check, qgroup reserved space is shared between data/metadata.
The EDQUOT can happen at the metadata reservation, so doing NODATACOW check after qgroup reservation failure is not a solution.[FIX]
To solve the problem, we don't return-EDQUOTdirectly, but every time we got a-EDQUOT, we try to flush qgroup space:- Flush all inodes of the root
NODATACOW writes will free the qgroup reserved atrun_dealloc_range(). However we don't have the infrastructure to only flush NODATACOW inodes, here we flush all inodes anyway.- Wait for ordered extents
This would convert the preallocated metadata space into per-trans metadata, which can be freed in later transaction commit.- Commit transaction
This will free all per-trans metadata space.Also we don't want to trigger flush multiple times, so here we introduce a per-root wait list and a new root status, to ensure only one thread starts the flushing.
Fixes c6887cd11149 ("Btrfs: don't do nocow check unless we have to")Reviewed-by Josef Bacik <[email protected]>Signed-off-by Qu Wenruo <[email protected]>Reviewed-by David Sterba <[email protected]>Signed-off-by David Sterba <[email protected]>Signed-off-by Anand Jain <[email protected]>---
fs/btrfs/ctree.h | 3 ++
fs/btrfs/disk-io.c| 1 +
fs/btrfs/qgroup.c | 100 +++++++++++++++++++++++++++++++++++++++++----3 files changed, 96 insertions(+), 8 deletions(-)fs/btrfs/ctree.h +3
-0@@ -945,6 +945,8 @@ enum { BTRFS_ROOT_DEAD_TREE, /* The root has a log tree. Used only for subvolume roots. */ BTRFS_ROOT_HAS_LOG_TREE, + /* Qgroup flushing is in progress */ + BTRFS_ROOT_QGROUP_FLUSHING, }; /*@@ -1097,6 +1099,7 @@ struct btrfs_root { spinlock_t qgroup_meta_rsv_lock; u64 qgroup_meta_rsv_pertrans; u64 qgroup_meta_rsv_prealloc; + wait_queue_head_t qgroup_flush_wait; /* Number of active swapfiles */ atomic_t nr_swapfiles;fs/btrfs/disk-io.c +1
-0@@ -1154,6 +1154,7 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info, mutex_init(&root->log_mutex); mutex_init(&root->ordered_extent_mutex); mutex_init(&root->delalloc_mutex); + init_waitqueue_head(&root->qgroup_flush_wait); init_waitqueue_head(&root->log_writer_wait); init_waitqueue_head(&root->log_commit_wait[0]);
init_waitqueue_head(&root->log_commit_wait[1]);fs/btrfs/qgroup.c +4
-3@@ -3479,17 +3479,58 @@ static int qgroup_unreserve_range(struct btrfs_inode *inode, } /* -* Reserve qgroup space for range [start, start + len). +* Try to free some space for qgroup. * -* This function will either reserve space from related qgroups or doing -* nothing if the range is already reserved. +* For qgroup, there are only 3 ways to free qgroup space: +* - Flush nodatacow write +* Any nodatacow write will free its reserved data space at
run_delalloc_range().
+ * In theory, we should only flush nodatacow inodes, but it's not yet
+ * possible, so we need to flush the whole root.
*
- * Return 0 for successful reserve
- * Return <0 for error (including-EQUOT)
+ * - Wait for ordered extents
+ * When ordered extents are finished, their reserved metadata is finally
+ * converted toper_transstatus, which can be freed by later commit
+ * transaction.
*
- * NOTE: this function may sleep for memory allocation.
+ * - Commit transaction
+ * This would free themeta_per_transspace.
+ * In theory this shouldn't provide much space, but any more qgroup space
+ * is needed.
*/fs/btrfs/qgroup.c +7
-1-int btrfs_qgroup_reserve_data(struct btrfs_inode *inode, +static int try_flush_qgroup (struct btrfs_root *root) +{ + struct btrfs_trans_handle *trans; + int ret; + + /* +* We don't want to run flush again and again, so if there is a running
+ * one, we won't try to start a new flush, but exit directly. + */
+ if (test_and_set_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state)) { + wait_event(root->qgroup_flush_wait,
+ !test_bit(BTRFS_ROOT_QGROUP_FLUSHING,&root->state)); + return 0;
+ }fs/btrfs/qgroup.c +20
-0+ + ret = btrfs_start_delalloc_snapshot(root); + if (ret < 0) + goto out; + btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); + + trans = btrfs_join_transaction(root); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + + ret = btrfs_commit_transaction(trans); +out: + clear_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state); + wake_up(&root->qgroup_flush_wait); + return ret; +} + +static int qgroup_reserve_data(struct btrfs_inode *inode,struct
extent_changeset**reserved_ret, u64 start, u64 len)
{fs/btrfs/qgroup.c +4
-0@@ -3542,6 +3583,34 @@ int btrfs_qgroup_reserve_data(structbtrfs_inode *inode, return ret; } +/* +* Reserve qgroup space for range [start, start + len). +* +* This function will either reserve space from related qgroups or do nothing
+ * if the range is already reserved.
+ *
+ * Return 0 for successful reservation
+ * Return <0 for error (including-EQUOT)
+ *
+ * NOTE: This function may sleep for memory allocation, dirty page flushing and
+ * commit transaction. So caller should not hold any dirty page locked.
+ */fs/btrfs/qgroup.c +16
-0+int btrfs_qgroup_reserve_data (struct btrfs_inode *inode, + struct extent_changeset **reserved_ret, u64 start, + u64 len) +{ + int ret; + + ret = qgroup_reserve_data(inode, reserved_ret, start, len); + if (ret <= 0 && ret != -EDQUOT) + return ret; + + ret = try_flush_qgroup(inode->root); + if (ret < 0) + return ret; + return qgroup_reserve_data(inode, reserved_ret, start, len); +} +/* Free ranges specified by @reserved, normally in error path */ static int
qgroup_free_reserved_data(struct btrfs_inode *inode, struct extent_changeset *reserved, u64 start, u64 len)fs/btrfs/qgroup.c +1
-1@@ -3712,7 +3781,7 @@ static int sub_root_meta_rsv(struct btrfs_root *root, int num_bytes, return num_bytes; } -int __btrfs_qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, +static int qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, enum btrfs_qgroup_rsv_type type, bool enforce) {struct
btrfs_fs_info*fs_info=root->fs_info;fs/btrfs/qgroup.c +15
-0@@ -3739,6 +3808,21 @@ int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, return ret; } +int __btrfs_qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, + enum btrfs_qgroup_rsv_type type, bool enforce) +{ + int ret; + + ret = qgroup_reserve_meta(root, num_bytes, type, enforce); + if (ret <= 0 && ret != -EDQUOT) + return ret; + + ret = try_flush_qgroup(root); + if (ret < 0) + return ret; + return qgroup_reserve_meta(root, num_bytes, type, enforce); +} + void btrfs_qgroup_free_meta_all_pertrans(struct btrfs_root *root) {struct
btrfs_fs_info*fs_info=root->fs_info;
Re: [PATCH 4/7] btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
Quoted 24 lines · Anand Jain
Quoted 21 lines · Qu Wenruo
Quoted 3 lines · Anand Jain
From: Qu Wenruo <[email protected]>
commit
c53e9653605dbf708f5be02902de51831be4b009upstreamThis lacks certain upstream fixes for it:
f9baa501b4fd6962257853d46ddffbc21f27e344btrfs: fix deadlock when cloning inline extents and using qgroups
4d14c5cde5c268a2bc26addecf09489cb953ef64btrfs: don't flush frombtrfs_delayed_inode_reserve_metadata
6f23277a49e68f8a9355385c846939ad0b1261e7btrfs: qgroup: don't commit transaction when we already hold the handleAll these fixes are to ensure we don't try to flush in context where we shouldn't.
Quoted 6 lines
Quoted 2 lines
Without them, it can hit various deadlock.
Qu,
Thanks for taking a look. I will send it in v2.
I guess you only need to add the missing fixes?
Thanks,
Qu
Quoted 258 lines
-Anand
Quoted 253 lines
Thanks,
QuQuoted 251 lines
[PROBLEM]
There are known problem related to how btrfs handles qgroup reserved space. One of the most obvious case is the the test case btrfs/153, which do fallocate, then write into the preallocated range.btrfs/153 1s ... - output mismatch (see xfstests-dev/results//btrfs/153.out.bad)
--- tests/btrfs/153.out 2019-10-22 15:18:14.068965341 +0800
+++ xfstests-dev/results//btrfs/153.out.bad 2020-07-01 20:24:40.730000089 +0800
@@ -1,2 +1,5 @@
QA output created by 153
+pwrite: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
Silence is golden
...
(Run 'diff -u xfstests-dev/tests/btrfs/153.out xfstests-dev/results//btrfs/153.out.bad' to see the entire diff)[CAUSE]
Since commitc6887cd11149("Btrfs: don't do nocow check unless we have to"), we always reserve space no matter if it's COW or not.Such behavior change is mostly for performance, and reverting it is not a good idea anyway.
For preallcoated extent, we reserve qgroup data space for it already, and since we also reserve data space for qgroup at buffered write time, it needs twice the space for us to write into preallocated space.
This leads to the
-EDQUOTin buffered write routine.And we can't follow the same solution, unlike data/meta space check, qgroup reserved space is shared between data/metadata.
The EDQUOT can happen at the metadata reservation, so doing NODATACOW check after qgroup reservation failure is not a solution.[FIX]
To solve the problem, we don't return-EDQUOTdirectly, but every time we got a-EDQUOT, we try to flush qgroup space:- Flush all inodes of the root
NODATACOW writes will free the qgroup reserved atrun_dealloc_range(). However we don't have the infrastructure to only flush NODATACOW inodes, here we flush all inodes anyway.- Wait for ordered extents
This would convert the preallocated metadata space into per-trans metadata, which can be freed in later transaction commit.- Commit transaction
This will free all per-trans metadata space.Also we don't want to trigger flush multiple times, so here we introduce a per-root wait list and a new root status, to ensure only one thread starts the flushing.
Fixes c6887cd11149 ("Btrfs: don't do nocow check unless we have to")Reviewed-by Josef Bacik <[email protected]>Signed-off-by Qu Wenruo <[email protected]>Reviewed-by David Sterba <[email protected]>Signed-off-by David Sterba <[email protected]>Signed-off-by Anand Jain <[email protected]>---
fs/btrfs/ctree.h | 3 ++
fs/btrfs/disk-io.c| 1 +
fs/btrfs/qgroup.c | 100 +++++++++++++++++++++++++++++++++++++++++----3 files changed, 96 insertions(+), 8 deletions(-)fs/btrfs/ctree.h +3
-0@@ -945,6 +945,8 @@ enum { BTRFS_ROOT_DEAD_TREE, /* The root has a log tree. Used only for subvolume roots. */ BTRFS_ROOT_HAS_LOG_TREE, + /* Qgroup flushing is in progress */ + BTRFS_ROOT_QGROUP_FLUSHING, }; /*@@ -1097,6 +1099,7 @@ struct btrfs_root { spinlock_t qgroup_meta_rsv_lock; u64 qgroup_meta_rsv_pertrans; u64 qgroup_meta_rsv_prealloc; + wait_queue_head_t qgroup_flush_wait; /* Number of active swapfiles */ atomic_t nr_swapfiles;fs/btrfs/disk-io.c +1
-0@@ -1154,6 +1154,7 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info, mutex_init(&root->log_mutex); mutex_init(&root->ordered_extent_mutex); mutex_init(&root->delalloc_mutex); + init_waitqueue_head(&root->qgroup_flush_wait); init_waitqueue_head(&root->log_writer_wait); init_waitqueue_head(&root->log_commit_wait[0]);
init_waitqueue_head(&root->log_commit_wait[1]);fs/btrfs/qgroup.c +1
-2@@ -3479,17 +3479,58 @@ static int qgroup_unreserve_range(struct btrfs_inode *inode, } /* -* Reserve qgroup space for range [start, start + len). +* Try to free some space for qgroup. * -* This function will either reserve space from related qgroups or doing
- * nothing if the range is already reserved.
+ * For qgroup, there are only 3 ways to free qgroup space:
+ * - Flush nodatacow write
+ * Any nodatacow write will free its reserved data space atrun_delalloc_range().
+ * In theory, we should only flush nodatacow inodes, but it's not yet
+ * possible, so we need to flush the whole root.
*
- * Return 0 for successful reserve
- * Return <0 for error (including-EQUOT)
+ * - Wait for ordered extents
+ * When ordered extents are finished, their reserved metadata is finally
+ * converted toper_transstatus, which can be freed by later commit
+ * transaction.
*
- * NOTE: this function may sleep for memory allocation.
+ * - Commit transaction
+ * This would free themeta_per_transspace.
+ * In theory this shouldn't provide much space, but any more qgroup space
+ * is needed.
*/fs/btrfs/qgroup.c +7
-1-int btrfs_qgroup_reserve_data(struct btrfs_inode *inode, +static int try_flush_qgroup (struct btrfs_root *root) +{ + struct btrfs_trans_handle *trans; + int ret; + + /* +* We don't want to run flush again and again, so if there is a running
+ * one, we won't try to start a new flush, but exit directly. + */
+ if (test_and_set_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state)) { + wait_event(root->qgroup_flush_wait,
+ !test_bit(BTRFS_ROOT_QGROUP_FLUSHING,&root->state)); + return 0;
+ }fs/btrfs/qgroup.c +20
-0+ + ret = btrfs_start_delalloc_snapshot(root); + if (ret < 0) + goto out; + btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); + + trans = btrfs_join_transaction(root); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + + ret = btrfs_commit_transaction(trans); +out: + clear_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state); + wake_up(&root->qgroup_flush_wait); + return ret; +} + +static int qgroup_reserve_data(struct btrfs_inode *inode,struct
extent_changeset**reserved_ret, u64 start, u64 len)
{fs/btrfs/qgroup.c +4
-0@@ -3542,6 +3583,34 @@ int btrfs_qgroup_reserve_data(structbtrfs_inode *inode, return ret; } +/* +* Reserve qgroup space for range [start, start + len). +* +* This function will either reserve space from related qgroups or do nothing
+ * if the range is already reserved.
+ *
+ * Return 0 for successful reservation
+ * Return <0 for error (including-EQUOT)
+ *
+ * NOTE: This function may sleep for memory allocation, dirty page flushing and
+ * commit transaction. So caller should not hold any dirty page locked.
+ */fs/btrfs/qgroup.c +16
-0+int btrfs_qgroup_reserve_data (struct btrfs_inode *inode, + struct extent_changeset **reserved_ret, u64 start, + u64 len) +{ + int ret; + + ret = qgroup_reserve_data(inode, reserved_ret, start, len); + if (ret <= 0 && ret != -EDQUOT) + return ret; + + ret = try_flush_qgroup(inode->root); + if (ret < 0) + return ret; + return qgroup_reserve_data(inode, reserved_ret, start, len); +} +/* Free ranges specified by @reserved, normally in error path */ static int
qgroup_free_reserved_data(struct btrfs_inode *inode, struct extent_changeset *reserved, u64 start, u64 len)fs/btrfs/qgroup.c +1
-1@@ -3712,7 +3781,7 @@ static int sub_root_meta_rsv(struct btrfs_root *root, int num_bytes, return num_bytes; } -int __btrfs_qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, +static int qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, enum btrfs_qgroup_rsv_type type, bool enforce) {struct
btrfs_fs_info*fs_info=root->fs_info;fs/btrfs/qgroup.c +15
-0@@ -3739,6 +3808,21 @@ int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, return ret; } +int __btrfs_qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, + enum btrfs_qgroup_rsv_type type, bool enforce) +{ + int ret; + + ret = qgroup_reserve_meta(root, num_bytes, type, enforce); + if (ret <= 0 && ret != -EDQUOT) + return ret; + + ret = try_flush_qgroup(root); + if (ret < 0) + return ret; + return qgroup_reserve_meta(root, num_bytes, type, enforce); +} + void btrfs_qgroup_free_meta_all_pertrans(struct btrfs_root *root) {struct
btrfs_fs_info*fs_info=root->fs_info;
Re: [PATCH 4/7] btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
Quoted 27 lines · Qu Wenruo
Quoted 24 lines · Anand Jain
Quoted 21 lines · Qu Wenruo
Quoted 3 lines · Anand Jain
From: Qu Wenruo <[email protected]>
commit
c53e9653605dbf708f5be02902de51831be4b009upstreamThis lacks certain upstream fixes for it:
f9baa501b4fd6962257853d46ddffbc21f27e344btrfs: fix deadlock when cloning inline extents and using qgroups
4d14c5cde5c268a2bc26addecf09489cb953ef64btrfs: don't flush frombtrfs_delayed_inode_reserve_metadata
6f23277a49e68f8a9355385c846939ad0b1261e7btrfs: qgroup: don't commit transaction when we already hold the handleAll these fixes are to ensure we don't try to flush in context where we shouldn't.
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
Without them, it can hit various deadlock.
Qu,
Thanks for taking a look. I will send it in v2.
I guess you only need to add the missing fixes?
Yeah, maybe it's better to send it as a new set.
Thx.
Anand
Quoted 265 lines
Thanks,
QuQuoted 262 lines
-Anand
Quoted 257 lines
Thanks,
QuQuoted 255 lines
[PROBLEM]
There are known problem related to how btrfs handles qgroup reserved space. One of the most obvious case is the the test case btrfs/153, which do fallocate, then write into the preallocated range.btrfs/153 1s ... - output mismatch (see xfstests-dev/results//btrfs/153.out.bad)
--- tests/btrfs/153.out 2019-10-22 15:18:14.068965341 +0800
+++ xfstests-dev/results//btrfs/153.out.bad 2020-07-01 20:24:40.730000089 +0800
@@ -1,2 +1,5 @@
QA output created by 153
+pwrite: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
+/mnt/scratch/testfile2: Disk quota exceeded
Silence is golden
...
(Run 'diff -u xfstests-dev/tests/btrfs/153.out xfstests-dev/results//btrfs/153.out.bad' to see the entire diff)[CAUSE]
Since commitc6887cd11149("Btrfs: don't do nocow check unless we have to"), we always reserve space no matter if it's COW or not.Such behavior change is mostly for performance, and reverting it is not a good idea anyway.
For preallcoated extent, we reserve qgroup data space for it already, and since we also reserve data space for qgroup at buffered write time, it needs twice the space for us to write into preallocated space.
This leads to the
-EDQUOTin buffered write routine.And we can't follow the same solution, unlike data/meta space check, qgroup reserved space is shared between data/metadata.
The EDQUOT can happen at the metadata reservation, so doing NODATACOW check after qgroup reservation failure is not a solution.[FIX]
To solve the problem, we don't return-EDQUOTdirectly, but every time we got a-EDQUOT, we try to flush qgroup space:- Flush all inodes of the root
NODATACOW writes will free the qgroup reserved atrun_dealloc_range(). However we don't have the infrastructure to only flush NODATACOW inodes, here we flush all inodes anyway.- Wait for ordered extents
This would convert the preallocated metadata space into per-trans metadata, which can be freed in later transaction commit.- Commit transaction
This will free all per-trans metadata space.Also we don't want to trigger flush multiple times, so here we introduce a per-root wait list and a new root status, to ensure only one thread starts the flushing.
Fixes c6887cd11149 ("Btrfs: don't do nocow check unless we have to")Reviewed-by Josef Bacik <[email protected]>Signed-off-by Qu Wenruo <[email protected]>Reviewed-by David Sterba <[email protected]>Signed-off-by David Sterba <[email protected]>Signed-off-by Anand Jain <[email protected]>---
fs/btrfs/ctree.h | 3 ++
fs/btrfs/disk-io.c| 1 +
fs/btrfs/qgroup.c | 100
+++++++++++++++++++++++++++++++++++++++++----3 files changed, 96 insertions(+), 8 deletions(-)fs/btrfs/ctree.h +3
-0@@ -945,6 +945,8 @@ enum { BTRFS_ROOT_DEAD_TREE, /* The root has a log tree. Used only for subvolume roots. */ BTRFS_ROOT_HAS_LOG_TREE, + /* Qgroup flushing is in progress */ + BTRFS_ROOT_QGROUP_FLUSHING, }; /*@@ -1097,6 +1099,7 @@ struct btrfs_root { spinlock_t qgroup_meta_rsv_lock; u64 qgroup_meta_rsv_pertrans; u64 qgroup_meta_rsv_prealloc; + wait_queue_head_t qgroup_flush_wait; /* Number of active swapfiles */ atomic_t nr_swapfiles;fs/btrfs/disk-io.c +1
-0@@ -1154,6 +1154,7 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info, mutex_init(&root->log_mutex); mutex_init(&root->ordered_extent_mutex); mutex_init(&root->delalloc_mutex); + init_waitqueue_head(&root->qgroup_flush_wait); init_waitqueue_head(&root->log_writer_wait); init_waitqueue_head(&root->log_commit_wait[0]);
init_waitqueue_head(&root->log_commit_wait[1]);fs/btrfs/qgroup.c +1
-2@@ -3479,17 +3479,58 @@ static int qgroup_unreserve_range(struct btrfs_inode *inode, } /* -* Reserve qgroup space for range [start, start + len). +* Try to free some space for qgroup. * -* This function will either reserve space from related qgroups or doing
- * nothing if the range is already reserved.
+ * For qgroup, there are only 3 ways to free qgroup space:
+ * - Flush nodatacow write
+ * Any nodatacow write will free its reserved data space atrun_delalloc_range().
+ * In theory, we should only flush nodatacow inodes, but it's not yet
+ * possible, so we need to flush the whole root.
*
- * Return 0 for successful reserve
- * Return <0 for error (including-EQUOT)
+ * - Wait for ordered extents
+ * When ordered extents are finished, their reserved metadata is finally
+ * converted toper_transstatus, which can be freed by later commit
+ * transaction.
*
- * NOTE: this function may sleep for memory allocation.
+ * - Commit transaction
+ * This would free themeta_per_transspace.
+ * In theory this shouldn't provide much space, but any more qgroup space
+ * is needed.
*/fs/btrfs/qgroup.c +7
-1-int btrfs_qgroup_reserve_data(struct btrfs_inode *inode, +static int try_flush_qgroup (struct btrfs_root *root) +{ + struct btrfs_trans_handle *trans; + int ret; + + /* +* We don't want to run flush again and again, so if there is a running
+ * one, we won't try to start a new flush, but exit directly. + */
+ if (test_and_set_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state)) { + wait_event(root->qgroup_flush_wait,
+ !test_bit(BTRFS_ROOT_QGROUP_FLUSHING,&root->state)); + return 0;
+ }fs/btrfs/qgroup.c +20
-0+ + ret = btrfs_start_delalloc_snapshot(root); + if (ret < 0) + goto out; + btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); + + trans = btrfs_join_transaction(root); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + + ret = btrfs_commit_transaction(trans); +out: + clear_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state); + wake_up(&root->qgroup_flush_wait); + return ret; +} + +static int qgroup_reserve_data(struct btrfs_inode *inode,struct
extent_changeset**reserved_ret, u64 start, u64 len)
{fs/btrfs/qgroup.c +4
-0@@ -3542,6 +3583,34 @@ int btrfs_qgroup_reserve_data(structbtrfs_inode *inode, return ret; } +/* +* Reserve qgroup space for range [start, start + len). +* +* This function will either reserve space from related qgroups or do nothing
+ * if the range is already reserved.
+ *
+ * Return 0 for successful reservation
+ * Return <0 for error (including-EQUOT)
+ *
+ * NOTE: This function may sleep for memory allocation, dirty page flushing and
+ * commit transaction. So caller should not hold any dirty page locked.
+ */fs/btrfs/qgroup.c +16
-0+int btrfs_qgroup_reserve_data (struct btrfs_inode *inode, + struct extent_changeset **reserved_ret, u64 start, + u64 len) +{ + int ret; + + ret = qgroup_reserve_data(inode, reserved_ret, start, len); + if (ret <= 0 && ret != -EDQUOT) + return ret; + + ret = try_flush_qgroup(inode->root); + if (ret < 0) + return ret; + return qgroup_reserve_data(inode, reserved_ret, start, len); +} +/* Free ranges specified by @reserved, normally in error path */ static int
qgroup_free_reserved_data(struct btrfs_inode *inode, struct extent_changeset *reserved, u64 start, u64 len)fs/btrfs/qgroup.c +1
-1@@ -3712,7 +3781,7 @@ static int sub_root_meta_rsv(struct btrfs_root *root, int num_bytes, return num_bytes; } -int __btrfs_qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, +static int qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, enum btrfs_qgroup_rsv_type type, bool enforce){
structbtrfs_fs_info*fs_info=root->fs_info;fs/btrfs/qgroup.c +15
-0@@ -3739,6 +3808,21 @@ int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, return ret; } +int __btrfs_qgroup_reserve_meta (struct btrfs_root *root,int num_bytes, + enum btrfs_qgroup_rsv_type type, bool enforce) +{ + int ret; + + ret = qgroup_reserve_meta(root, num_bytes, type, enforce); + if (ret <= 0 && ret != -EDQUOT) + return ret; + + ret = try_flush_qgroup(root); + if (ret < 0) + return ret; + return qgroup_reserve_meta(root, num_bytes, type, enforce); +} + void btrfs_qgroup_free_meta_all_pertrans(struct btrfs_root *root){
structbtrfs_fs_info*fs_info=root->fs_info;
Re: [PATCH 4/7] btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
Quoted 33 lines · Anand Jain
Quoted 30 lines · Qu Wenruo
Quoted 27 lines · Anand Jain
Quoted 23 lines · Qu Wenruo
Quoted 3 lines · Anand Jain
From: Qu Wenruo <[email protected]>
commit
c53e9653605dbf708f5be02902de51831be4b009upstreamThis lacks certain upstream fixes for it:
f9baa501b4fd6962257853d46ddffbc21f27e344btrfs: fix deadlock when cloning inline extents and using qgroups
4d14c5cde5c268a2bc26addecf09489cb953ef64btrfs: don't flush frombtrfs_delayed_inode_reserve_metadata
6f23277a49e68f8a9355385c846939ad0b1261e7btrfs: qgroup: don't commit transaction when we already hold the handleAll these fixes are to ensure we don't try to flush in context where we shouldn't.
Without them, it can hit various deadlock.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
Qu,
Thanks for taking a look. I will send it in v2.
I guess you only need to add the missing fixes?
Yeah, maybe it's better to send it as a new set.
So should I drop the existing patches and wait for a whole new series, or will you send these as an additional set?
And at least one of the above commits needs to go to the 5.10.y tree, I did not check them all...
thanks,
greg k-h
Re: [PATCH 4/7] btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
Quoted 36 lines · Greg KH
Quoted 35 lines · Anand Jain
Quoted 32 lines · Qu Wenruo
Quoted 29 lines · Anand Jain
Quoted 15 lines · Qu Wenruo
Quoted 3 lines · Anand Jain
From: Qu Wenruo <[email protected]>
commit
c53e9653605dbf708f5be02902de51831be4b009upstreamThis lacks certain upstream fixes for it:
f9baa501b4fd6962257853d46ddffbc21f27e344btrfs: fix deadlock when cloning inline extents and using qgroups
4d14c5cde5c268a2bc26addecf09489cb953ef64btrfs: don't flush frombtrfs_delayed_inode_reserve_metadataQuoted 8 lines
6f23277a49e68f8a9355385c846939ad0b1261e7btrfs: qgroup: don't commit transaction when we already hold the handleAll these fixes are to ensure we don't try to flush in context where we shouldn't.
Without them, it can hit various deadlock.
Qu,
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Thanks for taking a look. I will send it in v2.
I guess you only need to add the missing fixes?
Yeah, maybe it's better to send it as a new set.
So should I drop the existing patches and wait for a whole new series, or will you send these as an additional set?
Greg, I am sending it as an additional set.
Quoted 2 lines
And at least one of the above commits needs to go to the 5.10.y tree, I did not check them all...
I need to look into it.
Thanks, Anand
Quoted 4 lines
thanks,
greg k-h
Re: [PATCH 4/7] btrfs: qgroup: try to flush qgroup space when we get -EDQUOT
Quoted 48 lines · Anand Jain
Quoted 36 lines · Greg KH
Quoted 35 lines · Anand Jain
Quoted 32 lines · Qu Wenruo
Quoted 29 lines · Anand Jain
Quoted 15 lines · Qu Wenruo
Quoted 3 lines · Anand Jain
From: Qu Wenruo <[email protected]>
commit
c53e9653605dbf708f5be02902de51831be4b009upstreamThis lacks certain upstream fixes for it:
f9baa501b4fd6962257853d46ddffbc21f27e344btrfs: fix deadlock when cloning inline extents and using qgroups
4d14c5cde5c268a2bc26addecf09489cb953ef64btrfs: don't flush frombtrfs_delayed_inode_reserve_metadataQuoted 8 lines
6f23277a49e68f8a9355385c846939ad0b1261e7btrfs: qgroup: don't commit transaction when we already hold the handleAll these fixes are to ensure we don't try to flush in context where we shouldn't.
Without them, it can hit various deadlock.
Qu,
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Thanks for taking a look. I will send it in v2.
I guess you only need to add the missing fixes?
Yeah, maybe it's better to send it as a new set.
So should I drop the existing patches and wait for a whole new series, or will you send these as an additional set?
Quoted 8 lines
Greg, I am sending it as an additional set.
Quoted 2 lines
And at least one of the above commits needs to go to the 5.10.y tree, I did not check them all...
I need to look into it.
We don't need 1/7 in 5.10.y it was a preparatory patch in 5.4.y
[PATCH 1/7] btrfs: make qgroup_free_reserved_data take btrfs_inode
The rest of the patches (in patchset 1 and 2) are already in the stable-5.10.y.
Thx, Anand
Quoted 7 lines
Thanks, Anand
Quoted 4 lines
thanks,
greg k-h
PATCH 5/7 btrfs: transaction: Cleanup unused TRANS_STATE_BLOCKED
In reply to Anand JainFrom: Qu Wenruo <[email protected]>
commit 3296bf562443a8ca35aaad959a76a49e9b412760 upstream
The state was introduced in commit 4a9d8bdee368 ("Btrfs: make the state of the transaction more readable"), then in commit 302167c50b32 ("btrfs: don't end the transaction for delayed refs in throttle") the state is completely removed.
So we can just clean up the state since it's only compared but never set.
--- fs/btrfs/disk-io.c | 2 +- fs/btrfs/transaction.c | 15 +++------------ fs/btrfs/transaction.h | 1 - 3 files changed, 4 insertions(+), 14 deletions(-)
fs/btrfs/disk-io.c +1 -1
@@ -1748,7 +1748,7 @@ static int transaction_kthread(void *arg) } now =ktime_get_seconds (); -if (cur->state <TRANS_STATE_BLOCKED && +if (cur->state <TRANS_STATE_COMMIT_START && !test_bit (BTRFS_FS_NEED_ASYNC_COMMIT , &fs_info->flags) && (now < cur->start_time || now - cur->start_time < fs_info->commit_interval)) {
fs/btrfs/transaction.c +3 -12
@@ -27,7 +27,6 @@static const unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX ] = { [TRANS_STATE_RUNNING ] =0U , - [TRANS_STATE_BLOCKED ] = __TRANS_START, [TRANS_STATE_COMMIT_START ] = (__TRANS_START | __TRANS_ATTACH), [TRANS_STATE_COMMIT_DOING] = (__TRANS_START | __TRANS_ATTACH |
@@ -388,7 +387,7 @@ int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,static inline int is_transaction_blocked (struct btrfs_transaction *trans) { -return (trans->state >=TRANS_STATE_BLOCKED && +return (trans->state >=TRANS_STATE_COMMIT_START && trans->state <TRANS_STATE_UNBLOCKED && !TRANS_ABORTED (trans)); }
@@ -580,7 +579,7 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,INIT_LIST_HEAD (&h->new_bgs);smp_mb (); -if (cur_trans->state >=TRANS_STATE_BLOCKED && +if (cur_trans->state >=TRANS_STATE_COMMIT_START &&may_wait_transaction (fs_info, type)) { current->journal_info = h;btrfs_commit_transaction (h);
@@ -797,7 +796,7 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans)struct btrfs_transaction *cur_trans = trans->transaction;smp_mb (); -if (cur_trans->state >=TRANS_STATE_BLOCKED || +if (cur_trans->state >=TRANS_STATE_COMMIT_START || cur_trans->delayed_refs.flushing)return 1 ;
@@ -830,7 +829,6 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, {struct btrfs_fs_info *info = trans->fs_info;struct btrfs_transaction *cur_trans = trans->transaction; -int lock = (trans->type !=TRANS_JOIN_NOLOCK );int err =0 ;if (refcount_read (&trans->use_count) >1 ) {
@@ -846,13 +844,6 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,btrfs_trans_release_chunk_metadata (trans); -if (lock &&READ_ONCE (cur_trans->state) ==TRANS_STATE_BLOCKED ) { -if (throttle) -return btrfs_commit_transaction (trans); -else -wake_up_process (info->transaction_kthread); - } -if (trans->type & __TRANS_FREEZABLE)sb_end_intwrite (info->sb);
fs/btrfs/transaction.h +0 -1
@@ -13,7 +13,6 @@enum btrfs_trans_state {TRANS_STATE_RUNNING , -TRANS_STATE_BLOCKED ,TRANS_STATE_COMMIT_START ,TRANS_STATE_COMMIT_DOING ,TRANS_STATE_UNBLOCKED ,
Signature · 1 line
2.31.1
PATCH 6/7 btrfs: qgroup: remove ASYNC_COMMIT mechanism in favor of reserve retry-after-EDQUOT
In reply to Anand JainFrom: Qu Wenruo <[email protected]>
commit adca4d945c8dca28a85df45c5b117e6dac2e77f1 upstream
commit a514d63882c3 ("btrfs: qgroup: Commit transaction in advance to reduce early EDQUOT") tries to reduce the early EDQUOT problems by checking the qgroup free against threshold and tries to wake up commit kthread to free some space.
The problem of that mechanism is, it can only free qgroup per-trans metadata space, can't do anything to data, nor prealloc qgroup space.
Now since we have the ability to flush qgroup space, and implemented retry-after-EDQUOT behavior, such mechanism can be completely replaced.
So this patch will cleanup such mechanism in favor of retry-after-EDQUOT.
--- fs/btrfs/ctree.h | 5 ----- fs/btrfs/disk-io.c | 1 - fs/btrfs/qgroup.c | 43 ++---------------------------------------- fs/btrfs/transaction.c | 1 - fs/btrfs/transaction.h | 14 -------------- 5 files changed, 2 insertions(+), 62 deletions(-)
fs/btrfs/ctree.h +0 -5
@@ -504,11 +504,6 @@ enum {* (device replace, resize, device add/delete, balance) */ BTRFS_FS_EXCL_OP, -/* -* To info transaction_kthread we need an immediate commit so it -* doesn't need to wait for commit_interval -*/ - BTRFS_FS_NEED_ASYNC_COMMIT,/* * Indicate that balance has been set up from the ioctl and is in the * main phase. The fs_info::balance_ctl is initialized.
fs/btrfs/disk-io.c +0 -1
@@ -1749,7 +1749,6 @@ static int transaction_kthread(void *arg) now =ktime_get_seconds ();if (cur->state <TRANS_STATE_COMMIT_START && - !test_bit (BTRFS_FS_NEED_ASYNC_COMMIT , &fs_info->flags) && (now < cur->start_time || now - cur->start_time < fs_info->commit_interval)) {spin_unlock (&fs_info->trans_lock);
fs/btrfs/qgroup.c +2 -41
68 lines not on this page · raw message
fs/btrfs/transaction.c +0 -1
@@ -2295,7 +2295,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)*/ cur_trans->state =TRANS_STATE_COMPLETED ;wake_up (&cur_trans->commit_wait); -clear_bit (BTRFS_FS_NEED_ASYNC_COMMIT , &fs_info->flags);spin_lock (&fs_info->trans_lock);list_del_init (&cur_trans->list);
fs/btrfs/transaction.h +0 -14
@@ -207,20 +207,6 @@ int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root);int btrfs_commit_transaction (struct btrfs_trans_handle *trans);int btrfs_commit_transaction_async (struct btrfs_trans_handle *trans,int wait_for_unblock); - -/* -* Try to commit transaction asynchronously, so this is safe to call -* even holding a spinlock. -* -* It's done by informing transaction_kthread to commit transaction without -* waiting for commit interval. -*/ -static inline void btrfs_commit_transaction_locksafe ( -struct btrfs_fs_info *fs_info) -{ -set_bit (BTRFS_FS_NEED_ASYNC_COMMIT , &fs_info->flags); -wake_up_process (fs_info->transaction_kthread); -}int btrfs_end_transaction_throttle (struct btrfs_trans_handle *trans);int btrfs_should_end_transaction (struct btrfs_trans_handle *trans);void btrfs_throttle (struct btrfs_fs_info *fs_info);
Signature · 1 line
2.31.1
PATCH 7/7 btrfs: fix lockdep splat when enabling and disabling qgroups
In reply to Anand JainFrom: Filipe Manana <[email protected]>
commit a855fbe69229078cd8aecd8974fb996a5ca651e6 upstream
When running test case btrfs/017 from fstests, lockdep reported the following splat:
[ 1297.067385] ======================================================
[ 1297.067708] WARNING: possible circular locking dependency detected
[ 1297.068022] 5.10.0-rc4-btrfs-next-73 #1 Not tainted
[ 1297.068322] ------------------------------------------------------
[ 1297.068629] btrfs/189080 is trying to acquire lock:
[ 1297.068929] ffff9f2725731690 (sb_internal#2){.+.+}-{0:0}, at: btrfs_quota_enable+0xaf/0xa70 [btrfs]
[ 1297.069274]
but task is already holding lock:
[ 1297.069868] ffff9f2702b61a08 (&fs_info->qgroup_ioctl_lock){+.+.}-{3:3}, at: btrfs_quota_enable+0x3b/0xa70 [btrfs]
[ 1297.070219]
which lock already depends on the new lock. [ 1297.071131]
the existing dependency chain (in reverse order) is:
[ 1297.071721]
-> #1 (&fs_info->qgroup_ioctl_lock){+.+.}-{3:3}:
[ 1297.072375] lock_acquire+0xd8/0x490
[ 1297.072710] __mutex_lock+0xa3/0xb30
[ 1297.073061] btrfs_qgroup_inherit+0x59/0x6a0 [btrfs]
[ 1297.073421] create_subvol+0x194/0x990 [btrfs]
[ 1297.073780] btrfs_mksubvol+0x3fb/0x4a0 [btrfs]
[ 1297.074133] __btrfs_ioctl_snap_create+0x119/0x1a0 [btrfs]
[ 1297.074498] btrfs_ioctl_snap_create+0x58/0x80 [btrfs]
[ 1297.074872] btrfs_ioctl+0x1a90/0x36f0 [btrfs]
[ 1297.075245] __x64_sys_ioctl+0x83/0xb0
[ 1297.075617] do_syscall_64+0x33/0x80
[ 1297.075993] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 1297.076380]
-> #0 (sb_internal#2){.+.+}-{0:0}:
[ 1297.077166] check_prev_add+0x91/0xc60
[ 1297.077572] __lock_acquire+0x1740/0x3110
[ 1297.077984] lock_acquire+0xd8/0x490
[ 1297.078411] start_transaction+0x3c5/0x760 [btrfs]
[ 1297.078853] btrfs_quota_enable+0xaf/0xa70 [btrfs]
[ 1297.079323] btrfs_ioctl+0x2c60/0x36f0 [btrfs]
[ 1297.079789] __x64_sys_ioctl+0x83/0xb0
[ 1297.080232] do_syscall_64+0x33/0x80
[ 1297.080680] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 1297.081139]
other info that might help us debug this:[ 1297.082536] Possible unsafe locking scenario:
[ 1297.083510] CPU0 CPU1
[ 1297.084005] ---- ----
[ 1297.084500] lock(&fs_info->qgroup_ioctl_lock);
[ 1297.084994] lock(sb_internal#2);
[ 1297.085485] lock(&fs_info->qgroup_ioctl_lock);
[ 1297.085974] lock(sb_internal#2);
[ 1297.086454]
*** DEADLOCK ***
[ 1297.087880] 3 locks held by btrfs/189080:
[ 1297.088324] #0: ffff9f2725731470 (sb_writers#14){.+.+}-{0:0}, at: btrfs_ioctl+0xa73/0x36f0 [btrfs]
[ 1297.088799] #1: ffff9f2702b60cc0 (&fs_info->subvol_sem){++++}-{3:3}, at: btrfs_ioctl+0x1f4d/0x36f0 [btrfs]
[ 1297.089284] #2: ffff9f2702b61a08 (&fs_info->qgroup_ioctl_lock){+.+.}-{3:3}, at: btrfs_quota_enable+0x3b/0xa70 [btrfs]
[ 1297.089771]
stack backtrace:
[ 1297.090662] CPU: 5 PID: 189080 Comm: btrfs Not tainted 5.10.0-rc4-btrfs-next-73 #1
[ 1297.091132] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
[ 1297.092123] Call Trace:
[ 1297.092629] dump_stack+0x8d/0xb5
[ 1297.093115] check_noncircular+0xff/0x110
[ 1297.093596] check_prev_add+0x91/0xc60
[ 1297.094076] ? kvm_clock_read+0x14/0x30
[ 1297.094553] ? kvm_sched_clock_read+0x5/0x10
[ 1297.095029] __lock_acquire+0x1740/0x3110
[ 1297.095510] lock_acquire+0xd8/0x490
[ 1297.095993] ? btrfs_quota_enable+0xaf/0xa70 [btrfs]
[ 1297.096476] start_transaction+0x3c5/0x760 [btrfs]
[ 1297.096962] ? btrfs_quota_enable+0xaf/0xa70 [btrfs]
[ 1297.097451] btrfs_quota_enable+0xaf/0xa70 [btrfs]
[ 1297.097941] ? btrfs_ioctl+0x1f4d/0x36f0 [btrfs]
[ 1297.098429] btrfs_ioctl+0x2c60/0x36f0 [btrfs]
[ 1297.098904] ? do_user_addr_fault+0x20c/0x430
[ 1297.099382] ? kvm_clock_read+0x14/0x30
[ 1297.099854] ? kvm_sched_clock_read+0x5/0x10
[ 1297.100328] ? sched_clock+0x5/0x10
[ 1297.100801] ? sched_clock_cpu+0x12/0x180
[ 1297.101272] ? __x64_sys_ioctl+0x83/0xb0
[ 1297.101739] __x64_sys_ioctl+0x83/0xb0
[ 1297.102207] do_syscall_64+0x33/0x80
[ 1297.102673] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 1297.103148] RIP: 0033:0x7f773ff65d87This is because during the quota enable ioctl we lock first the mutex qgroup_ioctl_lock and then start a transaction, and starting a transaction acquires a fs freeze semaphore (at the VFS level). However, every other code path, except for the quota disable ioctl path, we do the opposite:
we start a transaction and then lock the mutex.
So fix this by making the quota enable and disable paths to start the transaction without having the mutex locked, and then, after starting the transaction, lock the mutex and check if some other task already enabled or disabled the quotas, bailing with success if that was the case.
Conflicts:
fs/btrfs/qgroup.c
--- fs/btrfs/ctree.h | 5 ++++- fs/btrfs/qgroup.c | 56 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 9 deletions(-)
fs/btrfs/ctree.h +4 -1
@@ -827,7 +827,10 @@ struct btrfs_fs_info {*/ struct ulist *qgroup_ulist; -/* protect user change for quota operations */ +/* +* Protect user change for quota operations. If a transaction is needed, +* it must be started before locking this lock. +*/ struct mutex qgroup_ioctl_lock;/* list of dirty qgroups to be written at next commit */
fs/btrfs/qgroup.c +48 -8
130 lines not on this page · raw message
Signature · 1 line
2.31.1
Quoted 6 lines · Anand Jain
Patch 1-2 and 5 helps to fix the conflicts smoothly.
Patch 3-4 and 6 fixes the regression as reported by the
test case btrfs/153 and comes from the below patch-set (btrfs: qgroup: Fix the long-existing regression of btrfs/153)
Patch 7 fixes lockdep Warning as in the commit log now reported by the test case btrfs/153 on 5.4.y
All queued up, thanks!
greg k-h