Commit ed80c569 authored by Kent Overstreet's avatar Kent Overstreet

bcachefs: Optimize bch2_dev_usage_read()

 - add bch2_dev_usage_read_fast(), which doesn't return by value -
   bch_dev_usage is big enough that we don't want the silent memcpy
 - tweak the allocation path to only call bch2_dev_usage_read() once per
   bucket allocated
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 2d485df3
...@@ -495,25 +495,25 @@ static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans, ...@@ -495,25 +495,25 @@ static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans,
struct bch_dev *ca, struct bch_dev *ca,
enum alloc_reserve reserve, enum alloc_reserve reserve,
bool may_alloc_partial, bool may_alloc_partial,
struct closure *cl) struct closure *cl,
struct bch_dev_usage *usage)
{ {
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
struct open_bucket *ob = NULL; struct open_bucket *ob = NULL;
struct bch_dev_usage usage;
u64 avail; u64 avail;
struct bucket_alloc_state s = { 0 }; struct bucket_alloc_state s = { 0 };
bool waiting = false; bool waiting = false;
again: again:
usage = bch2_dev_usage_read(ca); bch2_dev_usage_read_fast(ca, usage);
avail = dev_buckets_free(ca, usage, reserve); avail = dev_buckets_free(ca, *usage, reserve);
if (usage.d[BCH_DATA_need_discard].buckets > avail) if (usage->d[BCH_DATA_need_discard].buckets > avail)
bch2_do_discards(c); bch2_do_discards(c);
if (usage.d[BCH_DATA_need_gc_gens].buckets > avail) if (usage->d[BCH_DATA_need_gc_gens].buckets > avail)
bch2_do_gc_gens(c); bch2_do_gc_gens(c);
if (should_invalidate_buckets(ca, usage)) if (should_invalidate_buckets(ca, *usage))
bch2_do_invalidates(c); bch2_do_invalidates(c);
if (!avail) { if (!avail) {
...@@ -554,7 +554,7 @@ static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans, ...@@ -554,7 +554,7 @@ static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans,
bch2_alloc_reserves[reserve], bch2_alloc_reserves[reserve],
may_alloc_partial, may_alloc_partial,
ob->bucket, ob->bucket,
usage.d[BCH_DATA_free].buckets, usage->d[BCH_DATA_free].buckets,
avail, avail,
bch2_copygc_wait_amount(c), bch2_copygc_wait_amount(c),
c->copygc_wait - atomic64_read(&c->io_clock[WRITE].now), c->copygc_wait - atomic64_read(&c->io_clock[WRITE].now),
...@@ -566,7 +566,7 @@ static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans, ...@@ -566,7 +566,7 @@ static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans,
bch2_alloc_reserves[reserve], bch2_alloc_reserves[reserve],
may_alloc_partial, may_alloc_partial,
0, 0,
usage.d[BCH_DATA_free].buckets, usage->d[BCH_DATA_free].buckets,
avail, avail,
bch2_copygc_wait_amount(c), bch2_copygc_wait_amount(c),
c->copygc_wait - atomic64_read(&c->io_clock[WRITE].now), c->copygc_wait - atomic64_read(&c->io_clock[WRITE].now),
...@@ -582,11 +582,12 @@ struct open_bucket *bch2_bucket_alloc(struct bch_fs *c, struct bch_dev *ca, ...@@ -582,11 +582,12 @@ struct open_bucket *bch2_bucket_alloc(struct bch_fs *c, struct bch_dev *ca,
bool may_alloc_partial, bool may_alloc_partial,
struct closure *cl) struct closure *cl)
{ {
struct bch_dev_usage usage;
struct open_bucket *ob; struct open_bucket *ob;
bch2_trans_do(c, NULL, NULL, 0, bch2_trans_do(c, NULL, NULL, 0,
PTR_ERR_OR_ZERO(ob = bch2_bucket_alloc_trans(&trans, ca, reserve, PTR_ERR_OR_ZERO(ob = bch2_bucket_alloc_trans(&trans, ca, reserve,
may_alloc_partial, cl))); may_alloc_partial, cl, &usage)));
return ob; return ob;
} }
...@@ -613,8 +614,9 @@ struct dev_alloc_list bch2_dev_alloc_list(struct bch_fs *c, ...@@ -613,8 +614,9 @@ struct dev_alloc_list bch2_dev_alloc_list(struct bch_fs *c,
return ret; return ret;
} }
void bch2_dev_stripe_increment(struct bch_dev *ca, static inline void bch2_dev_stripe_increment_inlined(struct bch_dev *ca,
struct dev_stripe_state *stripe) struct dev_stripe_state *stripe,
struct bch_dev_usage *usage)
{ {
u64 *v = stripe->next_alloc + ca->dev_idx; u64 *v = stripe->next_alloc + ca->dev_idx;
u64 free_space = dev_buckets_available(ca, RESERVE_none); u64 free_space = dev_buckets_available(ca, RESERVE_none);
...@@ -633,6 +635,15 @@ void bch2_dev_stripe_increment(struct bch_dev *ca, ...@@ -633,6 +635,15 @@ void bch2_dev_stripe_increment(struct bch_dev *ca,
*v = *v < scale ? 0 : *v - scale; *v = *v < scale ? 0 : *v - scale;
} }
void bch2_dev_stripe_increment(struct bch_dev *ca,
struct dev_stripe_state *stripe)
{
struct bch_dev_usage usage;
bch2_dev_usage_read_fast(ca, &usage);
bch2_dev_stripe_increment_inlined(ca, stripe, &usage);
}
#define BUCKET_MAY_ALLOC_PARTIAL (1 << 0) #define BUCKET_MAY_ALLOC_PARTIAL (1 << 0)
#define BUCKET_ALLOC_USE_DURABILITY (1 << 1) #define BUCKET_ALLOC_USE_DURABILITY (1 << 1)
...@@ -677,6 +688,7 @@ static int bch2_bucket_alloc_set_trans(struct btree_trans *trans, ...@@ -677,6 +688,7 @@ static int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
BUG_ON(*nr_effective >= nr_replicas); BUG_ON(*nr_effective >= nr_replicas);
for (i = 0; i < devs_sorted.nr; i++) { for (i = 0; i < devs_sorted.nr; i++) {
struct bch_dev_usage usage;
struct open_bucket *ob; struct open_bucket *ob;
dev = devs_sorted.devs[i]; dev = devs_sorted.devs[i];
...@@ -696,9 +708,9 @@ static int bch2_bucket_alloc_set_trans(struct btree_trans *trans, ...@@ -696,9 +708,9 @@ static int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
} }
ob = bch2_bucket_alloc_trans(trans, ca, reserve, ob = bch2_bucket_alloc_trans(trans, ca, reserve,
flags & BUCKET_MAY_ALLOC_PARTIAL, cl); flags & BUCKET_MAY_ALLOC_PARTIAL, cl, &usage);
if (!IS_ERR(ob)) if (!IS_ERR(ob))
bch2_dev_stripe_increment(ca, stripe); bch2_dev_stripe_increment_inlined(ca, stripe, &usage);
percpu_ref_put(&ca->ref); percpu_ref_put(&ca->ref);
if (IS_ERR(ob)) { if (IS_ERR(ob)) {
......
...@@ -88,20 +88,17 @@ static inline struct bch_dev_usage *dev_usage_ptr(struct bch_dev *ca, ...@@ -88,20 +88,17 @@ static inline struct bch_dev_usage *dev_usage_ptr(struct bch_dev *ca,
: ca->usage[journal_seq & JOURNAL_BUF_MASK]); : ca->usage[journal_seq & JOURNAL_BUF_MASK]);
} }
struct bch_dev_usage bch2_dev_usage_read(struct bch_dev *ca) void bch2_dev_usage_read_fast(struct bch_dev *ca, struct bch_dev_usage *usage)
{ {
struct bch_fs *c = ca->fs; struct bch_fs *c = ca->fs;
struct bch_dev_usage ret;
unsigned seq, i, u64s = dev_usage_u64s(); unsigned seq, i, u64s = dev_usage_u64s();
do { do {
seq = read_seqcount_begin(&c->usage_lock); seq = read_seqcount_begin(&c->usage_lock);
memcpy(&ret, ca->usage_base, u64s * sizeof(u64)); memcpy(usage, ca->usage_base, u64s * sizeof(u64));
for (i = 0; i < ARRAY_SIZE(ca->usage); i++) for (i = 0; i < ARRAY_SIZE(ca->usage); i++)
acc_u64s_percpu((u64 *) &ret, (u64 __percpu *) ca->usage[i], u64s); acc_u64s_percpu((u64 *) usage, (u64 __percpu *) ca->usage[i], u64s);
} while (read_seqcount_retry(&c->usage_lock, seq)); } while (read_seqcount_retry(&c->usage_lock, seq));
return ret;
} }
static inline struct bch_fs_usage *fs_usage_ptr(struct bch_fs *c, static inline struct bch_fs_usage *fs_usage_ptr(struct bch_fs *c,
......
...@@ -120,7 +120,15 @@ static inline u8 ptr_stale(struct bch_dev *ca, ...@@ -120,7 +120,15 @@ static inline u8 ptr_stale(struct bch_dev *ca,
/* Device usage: */ /* Device usage: */
struct bch_dev_usage bch2_dev_usage_read(struct bch_dev *); void bch2_dev_usage_read_fast(struct bch_dev *, struct bch_dev_usage *);
static inline struct bch_dev_usage bch2_dev_usage_read(struct bch_dev *ca)
{
struct bch_dev_usage ret;
bch2_dev_usage_read_fast(ca, &ret);
return ret;
}
void bch2_dev_usage_init(struct bch_dev *); void bch2_dev_usage_init(struct bch_dev *);
static inline u64 bch2_dev_buckets_reserved(struct bch_dev *ca, enum alloc_reserve reserve) static inline u64 bch2_dev_buckets_reserved(struct bch_dev *ca, enum alloc_reserve reserve)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment