btrfs: rework async transaction committing
authorJosef Bacik <josef@toxicpanda.com>
Fri, 5 Nov 2021 20:45:28 +0000 (16:45 -0400)
committerDavid Sterba <dsterba@suse.com>
Mon, 3 Jan 2022 14:09:46 +0000 (15:09 +0100)
Currently we do this awful thing where we get another ref on a trans
handle, async off that handle and commit the transaction from that work.
Because we do this we have to mess with current->journal_info and the
freeze counting stuff.

We already have an async thing to kick for the transaction commit, the
transaction kthread.  Replace this work struct with a flag on the
fs_info to tell the kthread to go ahead and commit even if it's before
our timeout.  Then we can drastically simplify the async transaction
commit path.

Note: this can be simplified and functionality based on the pending
operation COMMIT.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
[ add note ]
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/ioctl.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h

index f9e9f08..6474e30 100644 (file)
@@ -595,6 +595,9 @@ enum {
        /* Indicate whether there are any tree modification log users */
        BTRFS_FS_TREE_MOD_LOG_USERS,
 
+       /* Indicate that we want the transaction kthread to commit right now. */
+       BTRFS_FS_COMMIT_TRANS,
+
 #if BITS_PER_LONG == 32
        /* Indicate if we have error/warn message printed on 32bit systems */
        BTRFS_FS_32BIT_ERROR,
index de32387..9683b50 100644 (file)
@@ -1934,7 +1934,8 @@ static int transaction_kthread(void *arg)
                }
 
                delta = ktime_get_seconds() - cur->start_time;
-               if (cur->state < TRANS_STATE_COMMIT_START &&
+               if (!test_and_clear_bit(BTRFS_FS_COMMIT_TRANS, &fs_info->flags) &&
+                   cur->state < TRANS_STATE_COMMIT_START &&
                    delta < fs_info->commit_interval) {
                        spin_unlock(&fs_info->trans_lock);
                        delay -= msecs_to_jiffies((delta - 1) * 1000);
index 441d513..af77098 100644 (file)
@@ -3622,7 +3622,6 @@ static noinline long btrfs_ioctl_start_sync(struct btrfs_root *root,
 {
        struct btrfs_trans_handle *trans;
        u64 transid;
-       int ret;
 
        trans = btrfs_attach_transaction_barrier(root);
        if (IS_ERR(trans)) {
@@ -3634,11 +3633,7 @@ static noinline long btrfs_ioctl_start_sync(struct btrfs_root *root,
                goto out;
        }
        transid = trans->transid;
-       ret = btrfs_commit_transaction_async(trans);
-       if (ret) {
-               btrfs_end_transaction(trans);
-               return ret;
-       }
+       btrfs_commit_transaction_async(trans);
 out:
        if (argp)
                if (copy_to_user(argp, &transid, sizeof(transid)))
index 8b6a90f..3538980 100644 (file)
@@ -1880,50 +1880,14 @@ int btrfs_transaction_blocked(struct btrfs_fs_info *info)
        return ret;
 }
 
-/*
- * commit transactions asynchronously. once btrfs_commit_transaction_async
- * returns, any subsequent transaction will not be allowed to join.
- */
-struct btrfs_async_commit {
-       struct btrfs_trans_handle *newtrans;
-       struct work_struct work;
-};
-
-static void do_async_commit(struct work_struct *work)
-{
-       struct btrfs_async_commit *ac =
-               container_of(work, struct btrfs_async_commit, work);
-
-       /*
-        * We've got freeze protection passed with the transaction.
-        * Tell lockdep about it.
-        */
-       if (ac->newtrans->type & __TRANS_FREEZABLE)
-               __sb_writers_acquired(ac->newtrans->fs_info->sb, SB_FREEZE_FS);
-
-       current->journal_info = ac->newtrans;
-
-       btrfs_commit_transaction(ac->newtrans);
-       kfree(ac);
-}
-
-int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans)
+void btrfs_commit_transaction_async(struct btrfs_trans_handle *trans)
 {
        struct btrfs_fs_info *fs_info = trans->fs_info;
-       struct btrfs_async_commit *ac;
        struct btrfs_transaction *cur_trans;
 
-       ac = kmalloc(sizeof(*ac), GFP_NOFS);
-       if (!ac)
-               return -ENOMEM;
-
-       INIT_WORK(&ac->work, do_async_commit);
-       ac->newtrans = btrfs_join_transaction(trans->root);
-       if (IS_ERR(ac->newtrans)) {
-               int err = PTR_ERR(ac->newtrans);
-               kfree(ac);
-               return err;
-       }
+       /* Kick the transaction kthread. */
+       set_bit(BTRFS_FS_COMMIT_TRANS, &fs_info->flags);
+       wake_up_process(fs_info->transaction_kthread);
 
        /* take transaction reference */
        cur_trans = trans->transaction;
@@ -1932,28 +1896,15 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans)
        btrfs_end_transaction(trans);
 
        /*
-        * Tell lockdep we've released the freeze rwsem, since the
-        * async commit thread will be the one to unlock it.
-        */
-       if (ac->newtrans->type & __TRANS_FREEZABLE)
-               __sb_writers_release(fs_info->sb, SB_FREEZE_FS);
-
-       schedule_work(&ac->work);
-       /*
         * Wait for the current transaction commit to start and block
         * subsequent transaction joins
         */
        wait_event(fs_info->transaction_blocked_wait,
                   cur_trans->state >= TRANS_STATE_COMMIT_START ||
                   TRANS_ABORTED(cur_trans));
-       if (current->journal_info == trans)
-               current->journal_info = NULL;
-
        btrfs_put_transaction(cur_trans);
-       return 0;
 }
 
-
 static void cleanup_transaction(struct btrfs_trans_handle *trans, int err)
 {
        struct btrfs_fs_info *fs_info = trans->fs_info;
@@ -2219,6 +2170,13 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
        wait_event(cur_trans->writer_wait,
                   atomic_read(&cur_trans->num_writers) == 1);
 
+       /*
+        * We've started the commit, clear the flag in case we were triggered to
+        * do an async commit but somebody else started before the transaction
+        * kthread could do the work.
+        */
+       clear_bit(BTRFS_FS_COMMIT_TRANS, &fs_info->flags);
+
        if (TRANS_ABORTED(cur_trans)) {
                ret = cur_trans->aborted;
                goto scrub_continue;
index ba45065..e4b9b25 100644 (file)
@@ -217,7 +217,7 @@ void btrfs_add_dead_root(struct btrfs_root *root);
 int btrfs_defrag_root(struct btrfs_root *root);
 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);
+void btrfs_commit_transaction_async(struct btrfs_trans_handle *trans);
 int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans);
 bool btrfs_should_end_transaction(struct btrfs_trans_handle *trans);
 void btrfs_throttle(struct btrfs_fs_info *fs_info);