On Thu, Jul 09, 2020 at 06:32:46PM +0200, David Sterba wrote:
> On Wed, Jul 08, 2020 at 02:24:46PM +0800, Qu Wenruo wrote:
> > -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.
> > + */
> > + ret = mutex_trylock(&root->qgroup_flushing_mutex);
> > + if (!ret) {
> > + mutex_lock(&root->qgroup_flushing_mutex);
> > + mutex_unlock(&root->qgroup_flushing_mutex);
>
> This is abuse of mutex, for status tracking "is somebody flushing" and
> for waiting until it's over.
>
> We have many root::status bits (the BTRFS_ROOT_* namespace) so that
> qgroups are flushing should another one. The bit atomically set when it
> starts and cleared when it ends.
>
> All waiting tasks should queue in a normal wait_queue_head.
Something like that (untested):
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 2cc1fcaa7cfa..5dbb6b7300b7 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1007,6 +1007,12 @@ enum {
BTRFS_ROOT_DEAD_TREE,
/* The root has a log tree. Used only for subvolume roots. */
BTRFS_ROOT_HAS_LOG_TREE,
+
+ /*
+ * Indicate that qgroup flushing is in progress to prevent multiple
+ * processes attempting that
+ */
+ BTRFS_ROOT_QGROUP_FLUSHING,
};
/*
@@ -1159,7 +1165,7 @@ struct btrfs_root {
spinlock_t qgroup_meta_rsv_lock;
u64 qgroup_meta_rsv_pertrans;
u64 qgroup_meta_rsv_prealloc;
- struct mutex qgroup_flushing_mutex;
+ wait_queue_head_t qgroup_flush_wait;
/* Number of active swapfiles */
atomic_t nr_swapfiles;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 8029127df537..e124e3376208 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1116,7 +1116,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);
- mutex_init(&root->qgroup_flushing_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]);
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 494ab2b1bbf2..95695aca7aa9 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3503,10 +3503,9 @@ static int try_flush_qgroup(struct btrfs_root *root)
* 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.
*/
- ret = mutex_trylock(&root->qgroup_flushing_mutex);
- if (!ret) {
- mutex_lock(&root->qgroup_flushing_mutex);
- mutex_unlock(&root->qgroup_flushing_mutex);
+ 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;
}
@@ -3523,7 +3522,7 @@ static int try_flush_qgroup(struct btrfs_root *root)
ret = btrfs_commit_transaction(trans);
unlock:
- mutex_unlock(&root->qgroup_flushing_mutex);
+ clear_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state);
return ret;
}