bcachefs: bch2_gc_gens() no longer uses bucket array
authorKent Overstreet <kent.overstreet@gmail.com>
Fri, 24 Dec 2021 09:51:10 +0000 (04:51 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:23 +0000 (17:09 -0400)
Like the previous patches, this converts bch2_gc_gens() to use the alloc
btree directly, and private arrays of generation numbers for its own
recalculation of oldest_gen.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
fs/bcachefs/bcachefs.h
fs/bcachefs/btree_gc.c
fs/bcachefs/buckets.h
fs/bcachefs/buckets_types.h
fs/bcachefs/super.c
fs/bcachefs/sysfs.c

index 91514365d72b9bf17eb9e63c4fe5f4294b7c3d6c..1985af8311dc1094b0442e3abe7680db5872181c 100644 (file)
@@ -452,6 +452,7 @@ struct bch_dev {
         */
        struct bucket_array __rcu *buckets[2];
        struct bucket_gens __rcu *bucket_gens;
+       u8                      *oldest_gen;
        unsigned long           *buckets_nouse;
        struct rw_semaphore     bucket_lock;
 
@@ -806,6 +807,7 @@ struct bch_fs {
         * it's not while a gc is in progress.
         */
        struct rw_semaphore     gc_lock;
+       struct mutex            gc_gens_lock;
 
        /* IO PATH */
        struct semaphore        io_in_flight;
index d4b2d2657340007343e381da446029c2b878c11e..9c33341c7947484caef127a060d7de5e33e06104 100644 (file)
@@ -1787,9 +1787,8 @@ static bool gc_btree_gens_key(struct bch_fs *c, struct bkey_s_c k)
        percpu_down_read(&c->mark_lock);
        bkey_for_each_ptr(ptrs, ptr) {
                struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev);
-               struct bucket *g = PTR_BUCKET(ca, ptr);
 
-               if (gen_after(g->mark.gen, ptr->gen) > 16) {
+               if (ptr_stale(ca, ptr) > 16) {
                        percpu_up_read(&c->mark_lock);
                        return true;
                }
@@ -1797,10 +1796,10 @@ static bool gc_btree_gens_key(struct bch_fs *c, struct bkey_s_c k)
 
        bkey_for_each_ptr(ptrs, ptr) {
                struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev);
-               struct bucket *g = PTR_BUCKET(ca, ptr);
+               u8 *gen = &ca->oldest_gen[PTR_BUCKET_NR(ca, ptr)];
 
-               if (gen_after(g->gc_gen, ptr->gen))
-                       g->gc_gen = ptr->gen;
+               if (gen_after(*gen, ptr->gen))
+                       *gen = ptr->gen;
        }
        percpu_up_read(&c->mark_lock);
 
@@ -1811,23 +1810,22 @@ static bool gc_btree_gens_key(struct bch_fs *c, struct bkey_s_c k)
  * For recalculating oldest gen, we only need to walk keys in leaf nodes; btree
  * node pointers currently never have cached pointers that can become stale:
  */
-static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id btree_id)
+static int bch2_gc_btree_gens(struct btree_trans *trans, enum btree_id btree_id)
 {
-       struct btree_trans trans;
+       struct bch_fs *c = trans->c;
        struct btree_iter iter;
        struct bkey_s_c k;
        struct bkey_buf sk;
        int ret = 0, commit_err = 0;
 
        bch2_bkey_buf_init(&sk);
-       bch2_trans_init(&trans, c, 0, 0);
 
-       bch2_trans_iter_init(&trans, &iter, btree_id, POS_MIN,
+       bch2_trans_iter_init(trans, &iter, btree_id, POS_MIN,
                             BTREE_ITER_PREFETCH|
                             BTREE_ITER_NOT_EXTENTS|
                             BTREE_ITER_ALL_SNAPSHOTS);
 
-       while ((bch2_trans_begin(&trans),
+       while ((bch2_trans_begin(trans),
                k = bch2_btree_iter_peek(&iter)).k) {
                ret = bkey_err(k);
 
@@ -1843,10 +1841,10 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id btree_id)
                        bch2_extent_normalize(c, bkey_i_to_s(sk.k));
 
                        commit_err =
-                               bch2_trans_update(&trans, &iter, sk.k, 0) ?:
-                               bch2_trans_commit(&trans, NULL, NULL,
-                                                      BTREE_INSERT_NOWAIT|
-                                                      BTREE_INSERT_NOFAIL);
+                               bch2_trans_update(trans, &iter, sk.k, 0) ?:
+                               bch2_trans_commit(trans, NULL, NULL,
+                                                 BTREE_INSERT_NOWAIT|
+                                                 BTREE_INSERT_NOFAIL);
                        if (commit_err == -EINTR) {
                                commit_err = 0;
                                continue;
@@ -1855,20 +1853,42 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id btree_id)
 
                bch2_btree_iter_advance(&iter);
        }
-       bch2_trans_iter_exit(&trans, &iter);
+       bch2_trans_iter_exit(trans, &iter);
 
-       bch2_trans_exit(&trans);
        bch2_bkey_buf_exit(&sk, c);
 
        return ret;
 }
 
+static int bch2_alloc_write_oldest_gen(struct btree_trans *trans, struct btree_iter *iter)
+{
+       struct bch_dev *ca = bch_dev_bkey_exists(trans->c, iter->pos.inode);
+       struct bkey_s_c k;
+       struct bkey_alloc_unpacked u;
+       int ret;
+
+       k = bch2_btree_iter_peek_slot(iter);
+       ret = bkey_err(k);
+       if (ret)
+               return ret;
+
+       u = bch2_alloc_unpack(k);
+
+       if (u.oldest_gen == ca->oldest_gen[iter->pos.offset])
+               return 0;
+
+       u.oldest_gen = ca->oldest_gen[iter->pos.offset];
+
+       return bch2_alloc_write(trans, iter, &u, BTREE_TRIGGER_NORUN);
+}
+
 int bch2_gc_gens(struct bch_fs *c)
 {
+       struct btree_trans trans;
+       struct btree_iter iter;
+       struct bkey_s_c k;
        struct bch_dev *ca;
-       struct bucket_array *buckets;
-       struct bucket *g;
-       u64 start_time = local_clock();
+       u64 b, start_time = local_clock();
        unsigned i;
        int ret;
 
@@ -1877,36 +1897,53 @@ int bch2_gc_gens(struct bch_fs *c)
         * introduces a deadlock in the RO path - we currently take the state
         * lock at the start of going RO, thus the gc thread may get stuck:
         */
+       if (!mutex_trylock(&c->gc_gens_lock))
+               return 0;
+
        down_read(&c->gc_lock);
+       bch2_trans_init(&trans, c, 0, 0);
 
        for_each_member_device(ca, c, i) {
-               down_read(&ca->bucket_lock);
-               buckets = bucket_array(ca);
+               struct bucket_gens *gens;
+
+               BUG_ON(ca->oldest_gen);
+
+               ca->oldest_gen = kvmalloc(ca->mi.nbuckets, GFP_KERNEL);
+               if (!ca->oldest_gen) {
+                       percpu_ref_put(&ca->ref);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               gens = bucket_gens(ca);
 
-               for_each_bucket(g, buckets)
-                       g->gc_gen = g->mark.gen;
-               up_read(&ca->bucket_lock);
+               for (b = gens->first_bucket;
+                    b < gens->nbuckets; b++)
+                       ca->oldest_gen[b] = gens->b[b];
        }
 
        for (i = 0; i < BTREE_ID_NR; i++)
                if ((1 << i) & BTREE_ID_HAS_PTRS) {
                        c->gc_gens_btree = i;
                        c->gc_gens_pos = POS_MIN;
-                       ret = bch2_gc_btree_gens(c, i);
+                       ret = bch2_gc_btree_gens(&trans, i);
                        if (ret) {
                                bch_err(c, "error recalculating oldest_gen: %i", ret);
                                goto err;
                        }
                }
 
-       for_each_member_device(ca, c, i) {
-               down_read(&ca->bucket_lock);
-               buckets = bucket_array(ca);
-
-               for_each_bucket(g, buckets)
-                       g->oldest_gen = g->gc_gen;
-               up_read(&ca->bucket_lock);
+       for_each_btree_key(&trans, iter, BTREE_ID_alloc, POS_MIN,
+                          BTREE_ITER_PREFETCH, k, ret) {
+               ret = __bch2_trans_do(&trans, NULL, NULL,
+                                     BTREE_INSERT_NOFAIL,
+                               bch2_alloc_write_oldest_gen(&trans, &iter));
+               if (ret) {
+                       bch_err(c, "error writing oldest_gen: %i", ret);
+                       break;
+               }
        }
+       bch2_trans_iter_exit(&trans, &iter);
 
        c->gc_gens_btree        = 0;
        c->gc_gens_pos          = POS_MIN;
@@ -1915,7 +1952,14 @@ int bch2_gc_gens(struct bch_fs *c)
 
        bch2_time_stats_update(&c->times[BCH_TIME_btree_gc], start_time);
 err:
+       for_each_member_device(ca, c, i) {
+               kvfree(ca->oldest_gen);
+               ca->oldest_gen = NULL;
+       }
+
+       bch2_trans_exit(&trans);
        up_read(&c->gc_lock);
+       mutex_unlock(&c->gc_gens_lock);
        return ret;
 }
 
index 483c8b24293f913642a18e23b5f157eb4f459872..8a3cea6f94df939d90b1298bf3f99406ff3ed619 100644 (file)
@@ -97,12 +97,6 @@ static inline size_t PTR_BUCKET_NR(const struct bch_dev *ca,
        return sector_to_bucket(ca, ptr->offset);
 }
 
-static inline struct bucket *PTR_BUCKET(struct bch_dev *ca,
-                                       const struct bch_extent_ptr *ptr)
-{
-       return bucket(ca, PTR_BUCKET_NR(ca, ptr));
-}
-
 static inline struct bucket *PTR_GC_BUCKET(struct bch_dev *ca,
                                           const struct bch_extent_ptr *ptr)
 {
index 24139831226d49f7912854a5fcf90f1550423231..2c73dc60b838f08f42da26ded8c6b8a0358a4b20 100644 (file)
@@ -30,7 +30,6 @@ struct bucket {
 
        u64                             io_time[2];
        u8                              oldest_gen;
-       u8                              gc_gen;
        unsigned                        gen_valid:1;
        u8                              stripe_redundancy;
        u32                             stripe;
index a90fa0ae550bd9707453c9893efd737947fff0ad..d35547fbefdb3ab93f0a9261b77824fcdcde13e2 100644 (file)
@@ -682,6 +682,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
        INIT_WORK(&c->read_only_work, bch2_fs_read_only_work);
 
        init_rwsem(&c->gc_lock);
+       mutex_init(&c->gc_gens_lock);
 
        for (i = 0; i < BCH_TIME_STAT_NR; i++)
                bch2_time_stats_init(&c->times[i]);
index ed9a095063e8f684098f5a12d5e1541236a00bba..b727845dd64b73d4ea51fe8842c9f03ca899ad23 100644 (file)
@@ -499,6 +499,17 @@ STORE(bch2_fs)
 
        /* Debugging: */
 
+       if (!test_bit(BCH_FS_RW, &c->flags))
+               return -EROFS;
+
+       if (attr == &sysfs_prune_cache) {
+               struct shrink_control sc;
+
+               sc.gfp_mask = GFP_KERNEL;
+               sc.nr_to_scan = strtoul_or_return(buf);
+               c->btree_cache.shrink.scan_objects(&c->btree_cache.shrink, &sc);
+       }
+
        if (attr == &sysfs_trigger_gc) {
                /*
                 * Full gc is currently incompatible with btree key cache:
@@ -512,14 +523,6 @@ STORE(bch2_fs)
 #endif
        }
 
-       if (attr == &sysfs_prune_cache) {
-               struct shrink_control sc;
-
-               sc.gfp_mask = GFP_KERNEL;
-               sc.nr_to_scan = strtoul_or_return(buf);
-               c->btree_cache.shrink.scan_objects(&c->btree_cache.shrink, &sc);
-       }
-
 #ifdef CONFIG_BCACHEFS_TESTS
        if (attr == &sysfs_perf_test) {
                char *tmp = kstrdup(buf, GFP_KERNEL), *p = tmp;