1

bcachefs: bch2_gc_gens() no longer uses bucket array

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>
This commit is contained in:
Kent Overstreet 2021-12-24 04:51:10 -05:00 committed by Kent Overstreet
parent d73e0d2cd1
commit c45c866761
6 changed files with 90 additions and 47 deletions

View 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;

View 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;
for_each_bucket(g, buckets)
g->gc_gen = g->mark.gen;
up_read(&ca->bucket_lock);
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 (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;
}

View 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)
{

View 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;

View 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]);

View 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;