bcachefs: Switch gc bucket array to a genradix
authorKent Overstreet <kent.overstreet@linux.dev>
Sat, 24 Aug 2024 15:38:21 +0000 (11:38 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 9 Sep 2024 13:41:49 +0000 (09:41 -0400)
A user with a 30 tb device is overflowing the INT_MAX limit on vmalloc
allocations...

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

index a4773b0..ad4df0a 100644 (file)
@@ -542,7 +542,7 @@ struct bch_dev {
         * gc_gens_lock, for device resize - holding any is sufficient for
         * access: Or rcu_read_lock(), but only for dev_ptr_stale():
         */
-       struct bucket_array __rcu *buckets_gc;
+       GENRADIX(struct bucket) buckets_gc;
        struct bucket_gens __rcu *bucket_gens;
        u8                      *oldest_gen;
        unsigned long           *buckets_nouse;
index eb3002c..120ffd6 100644 (file)
@@ -753,10 +753,8 @@ static void bch2_gc_free(struct bch_fs *c)
        genradix_free(&c->reflink_gc_table);
        genradix_free(&c->gc_stripes);
 
-       for_each_member_device(c, ca) {
-               kvfree(rcu_dereference_protected(ca->buckets_gc, 1));
-               ca->buckets_gc = NULL;
-       }
+       for_each_member_device(c, ca)
+               genradix_free(&ca->buckets_gc);
 }
 
 static int bch2_gc_start(struct bch_fs *c)
@@ -910,20 +908,12 @@ static int bch2_gc_alloc_start(struct bch_fs *c)
        int ret = 0;
 
        for_each_member_device(c, ca) {
-               struct bucket_array *buckets = kvmalloc(sizeof(struct bucket_array) +
-                               ca->mi.nbuckets * sizeof(struct bucket),
-                               GFP_KERNEL|__GFP_ZERO);
-               if (!buckets) {
+               ret = genradix_prealloc(&ca->buckets_gc, ca->mi.nbuckets, GFP_KERNEL);
+               if (ret) {
                        bch2_dev_put(ca);
                        ret = -BCH_ERR_ENOMEM_gc_alloc_start;
                        break;
                }
-
-               buckets->first_bucket   = ca->mi.first_bucket;
-               buckets->nbuckets       = ca->mi.nbuckets;
-               buckets->nbuckets_minus_first =
-                       buckets->nbuckets - buckets->first_bucket;
-               rcu_assign_pointer(ca->buckets_gc, buckets);
        }
 
        bch_err_fn(c, ret);
index edbdffd..e2cb7b2 100644 (file)
@@ -80,22 +80,9 @@ static inline void bucket_lock(struct bucket *b)
                         TASK_UNINTERRUPTIBLE);
 }
 
-static inline struct bucket_array *gc_bucket_array(struct bch_dev *ca)
-{
-       return rcu_dereference_check(ca->buckets_gc,
-                                    !ca->fs ||
-                                    percpu_rwsem_is_held(&ca->fs->mark_lock) ||
-                                    lockdep_is_held(&ca->fs->state_lock) ||
-                                    lockdep_is_held(&ca->bucket_lock));
-}
-
 static inline struct bucket *gc_bucket(struct bch_dev *ca, size_t b)
 {
-       struct bucket_array *buckets = gc_bucket_array(ca);
-
-       if (b - buckets->first_bucket >= buckets->nbuckets_minus_first)
-               return NULL;
-       return buckets->b + b;
+       return genradix_ptr(&ca->buckets_gc, b);
 }
 
 static inline struct bucket_gens *bucket_gens(struct bch_dev *ca)
index a19460a..28bd09a 100644 (file)
@@ -19,14 +19,6 @@ struct bucket {
        u32                     stripe_sectors;
 } __aligned(sizeof(long));
 
-struct bucket_array {
-       struct rcu_head         rcu;
-       u16                     first_bucket;
-       size_t                  nbuckets;
-       size_t                  nbuckets_minus_first;
-       struct bucket           b[] __counted_by(nbuckets);
-};
-
 struct bucket_gens {
        struct rcu_head         rcu;
        u16                     first_bucket;