bcachefs: Don't recurse in check_discard_freespace_key
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 29 Nov 2024 00:30:23 +0000 (19:30 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sat, 21 Dec 2024 06:36:21 +0000 (01:36 -0500)
When calling check_discard_freeespace_key from the allocator, we can't
repair without recursing - run it asynchronously instead.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/alloc_background.c
fs/bcachefs/alloc_background.h
fs/bcachefs/alloc_foreground.c
fs/bcachefs/bcachefs.h

index e8c246e5803c2756336abb6878af67fd4c252521..b2d570453351f2b61e82a27b973396e864b9a654 100644 (file)
@@ -1338,7 +1338,40 @@ fsck_err:
        return ret;
 }
 
-int bch2_check_discard_freespace_key(struct btree_trans *trans, struct btree_iter *iter, u8 *gen)
+struct check_discard_freespace_key_async {
+       struct work_struct      work;
+       struct bch_fs           *c;
+       struct bbpos            pos;
+};
+
+static int bch2_recheck_discard_freespace_key(struct btree_trans *trans, struct bbpos pos)
+{
+       struct btree_iter iter;
+       struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, pos.btree, pos.pos, 0);
+       int ret = bkey_err(k);
+       if (ret)
+               return ret;
+
+       u8 gen;
+       ret = k.k->type != KEY_TYPE_set
+               ? bch2_check_discard_freespace_key(trans, &iter, &gen, false)
+               : 0;
+       bch2_trans_iter_exit(trans, &iter);
+       return ret;
+}
+
+static void check_discard_freespace_key_work(struct work_struct *work)
+{
+       struct check_discard_freespace_key_async *w =
+               container_of(work, struct check_discard_freespace_key_async, work);
+
+       bch2_trans_do(w->c, bch2_recheck_discard_freespace_key(trans, w->pos));
+       bch2_write_ref_put(w->c, BCH_WRITE_REF_check_discard_freespace_key);
+       kfree(w);
+}
+
+int bch2_check_discard_freespace_key(struct btree_trans *trans, struct btree_iter *iter, u8 *gen,
+                                    bool async_repair)
 {
        struct bch_fs *c = trans->c;
        enum bch_data_type state = iter->btree_id == BTREE_ID_need_discard
@@ -1351,7 +1384,8 @@ int bch2_check_discard_freespace_key(struct btree_trans *trans, struct btree_ite
        u64 genbits = iter->pos.offset & (~0ULL << 56);
 
        struct btree_iter alloc_iter;
-       struct bkey_s_c alloc_k = bch2_bkey_get_iter(trans, &alloc_iter, BTREE_ID_alloc, bucket, BTREE_ITER_cached);
+       struct bkey_s_c alloc_k = bch2_bkey_get_iter(trans, &alloc_iter,
+                                                    BTREE_ID_alloc, bucket, BTREE_ITER_cached);
        int ret = bkey_err(alloc_k);
        if (ret)
                return ret;
@@ -1392,17 +1426,39 @@ fsck_err:
        printbuf_exit(&buf);
        return ret;
 delete:
-       ret =   bch2_btree_bit_mod_iter(trans, iter, false) ?:
-               bch2_trans_commit(trans, NULL, NULL,
-                       BCH_TRANS_COMMIT_no_enospc) ?:
-               -BCH_ERR_transaction_restart_commit;
-       goto out;
+       if (!async_repair) {
+               ret =   bch2_btree_bit_mod_iter(trans, iter, false) ?:
+                       bch2_trans_commit(trans, NULL, NULL,
+                               BCH_TRANS_COMMIT_no_enospc) ?:
+                       -BCH_ERR_transaction_restart_commit;
+               goto out;
+       } else {
+               /*
+                * We can't repair here when called from the allocator path: the
+                * commit will recurse back into the allocator
+                */
+               struct check_discard_freespace_key_async *w =
+                       kzalloc(sizeof(*w), GFP_KERNEL);
+               if (!w)
+                       goto out;
+
+               if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_check_discard_freespace_key)) {
+                       kfree(w);
+                       goto out;
+               }
+
+               INIT_WORK(&w->work, check_discard_freespace_key_work);
+               w->c = c;
+               w->pos = BBPOS(iter->btree_id, iter->pos);
+               queue_work(c->write_ref_wq, &w->work);
+               goto out;
+       }
 }
 
 static int bch2_check_discard_freespace_key_fsck(struct btree_trans *trans, struct btree_iter *iter)
 {
        u8 gen;
-       int ret = bch2_check_discard_freespace_key(trans, iter, &gen);
+       int ret = bch2_check_discard_freespace_key(trans, iter, &gen, false);
        return ret < 0 ? ret : 0;
 }
 
index 8cacddd188f4b0d52f793ddf877e5622209fdb57..de25ba4ee94b534b45f198eddb0ccb8ee251bfb3 100644 (file)
@@ -310,7 +310,7 @@ int bch2_trigger_alloc(struct btree_trans *, enum btree_id, unsigned,
                       struct bkey_s_c, struct bkey_s,
                       enum btree_iter_update_trigger_flags);
 
-int bch2_check_discard_freespace_key(struct btree_trans *, struct btree_iter *, u8 *);
+int bch2_check_discard_freespace_key(struct btree_trans *, struct btree_iter *, u8 *, bool);
 int bch2_check_alloc_info(struct bch_fs *);
 int bch2_check_alloc_to_lru_refs(struct bch_fs *);
 void bch2_dev_do_discards(struct bch_dev *);
index 4d1ff7f1f302782a137c3984ced3e7a8835f4f0e..c40a76df76b85747498f6b525a92eb3b05c70588 100644 (file)
@@ -281,7 +281,7 @@ static struct open_bucket *try_alloc_bucket(struct btree_trans *trans, struct bc
        u64 b = freespace_iter->pos.offset & ~(~0ULL << 56);
        u8 gen;
 
-       int ret = bch2_check_discard_freespace_key(trans, freespace_iter, &gen);
+       int ret = bch2_check_discard_freespace_key(trans, freespace_iter, &gen, true);
        if (ret < 0)
                return ERR_PTR(ret);
        if (ret)
index d88129503bc558ce878b97f291399a7171969a1c..c16937e54734c748aa4e50fc7ccfadf3028f6429 100644 (file)
@@ -680,6 +680,7 @@ struct btree_trans_buf {
        x(dio_write)                                                    \
        x(discard)                                                      \
        x(discard_fast)                                                 \
+       x(check_discard_freespace_key)                                  \
        x(invalidate)                                                   \
        x(delete_dead_snapshots)                                        \
        x(gc_gens)                                                      \