Commit 2c6a7bff authored by Kent Overstreet's avatar Kent Overstreet
Browse files

bcachefs: Switch gc bucket array to a genradix



A user with a 30 tb device is overflowing the INT_MAX limit on vmalloc
allocations...

Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent a803fa55
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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;
+4 −14
Original line number Diff line number Diff line
@@ -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);
+1 −14
Original line number Diff line number Diff line
@@ -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)
+0 −8
Original line number Diff line number Diff line
@@ -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;