]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bcachefs: Bail out early on alloc_nowait data updates
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 19 Jan 2025 18:55:33 +0000 (13:55 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sat, 15 Mar 2025 01:02:11 +0000 (21:02 -0400)
If a data update doesn't want to block on allocations (promotes, self
healing on read error) - check if the allocation would fail before
kicking off the data update and calling into the write path.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/alloc_foreground.c
fs/bcachefs/alloc_foreground.h
fs/bcachefs/data_update.c
fs/bcachefs/errcode.h

index 1a539e7bedc8ca58132c538f6cd909e09a0c6b25..1759c15a77451bc522d9bc3587cacfac144c9b7e 100644 (file)
@@ -179,23 +179,6 @@ static void open_bucket_free_unused(struct bch_fs *c, struct open_bucket *ob)
        closure_wake_up(&c->freelist_wait);
 }
 
-static inline unsigned open_buckets_reserved(enum bch_watermark watermark)
-{
-       switch (watermark) {
-       case BCH_WATERMARK_interior_updates:
-               return 0;
-       case BCH_WATERMARK_reclaim:
-               return OPEN_BUCKETS_COUNT / 6;
-       case BCH_WATERMARK_btree:
-       case BCH_WATERMARK_btree_copygc:
-               return OPEN_BUCKETS_COUNT / 4;
-       case BCH_WATERMARK_copygc:
-               return OPEN_BUCKETS_COUNT / 3;
-       default:
-               return OPEN_BUCKETS_COUNT / 2;
-       }
-}
-
 static inline bool may_alloc_bucket(struct bch_fs *c,
                                    struct bpos bucket,
                                    struct bucket_alloc_state *s)
@@ -239,7 +222,7 @@ static struct open_bucket *__try_alloc_bucket(struct bch_fs *c, struct bch_dev *
 
        spin_lock(&c->freelist_lock);
 
-       if (unlikely(c->open_buckets_nr_free <= open_buckets_reserved(watermark))) {
+       if (unlikely(c->open_buckets_nr_free <= bch2_open_buckets_reserved(watermark))) {
                if (cl)
                        closure_wait(&c->open_buckets_wait, cl);
 
index f25481a0d1a06806b0c998b49221ccc1d61e8886..baf5dc163c8ab8ceca554ba4ae602f67da20a809 100644 (file)
@@ -33,6 +33,23 @@ static inline struct bch_dev *ob_dev(struct bch_fs *c, struct open_bucket *ob)
        return bch2_dev_have_ref(c, ob->dev);
 }
 
+static inline unsigned bch2_open_buckets_reserved(enum bch_watermark watermark)
+{
+       switch (watermark) {
+       case BCH_WATERMARK_interior_updates:
+               return 0;
+       case BCH_WATERMARK_reclaim:
+               return OPEN_BUCKETS_COUNT / 6;
+       case BCH_WATERMARK_btree:
+       case BCH_WATERMARK_btree_copygc:
+               return OPEN_BUCKETS_COUNT / 4;
+       case BCH_WATERMARK_copygc:
+               return OPEN_BUCKETS_COUNT / 3;
+       default:
+               return OPEN_BUCKETS_COUNT / 2;
+       }
+}
+
 struct open_bucket *bch2_bucket_alloc(struct bch_fs *, struct bch_dev *,
                                      enum bch_watermark, enum bch_data_type,
                                      struct closure *);
index 3e8ad94dca590041eec0abf24266277db1a31817..ec63dd494c801adebd2bbe5b53939a082b8a8af2 100644 (file)
@@ -639,6 +639,40 @@ int bch2_extent_drop_ptrs(struct btree_trans *trans,
                bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
 }
 
+static bool can_allocate_without_blocking(struct bch_fs *c,
+                                         struct data_update *m)
+{
+       if (unlikely(c->open_buckets_nr_free <= bch2_open_buckets_reserved(m->op.watermark)))
+               return false;
+
+       unsigned target = m->op.flags & BCH_WRITE_only_specified_devs
+               ? m->op.target
+               : 0;
+       struct bch_devs_mask devs = target_rw_devs(c, BCH_DATA_user, target);
+
+       darray_for_each(m->op.devs_have, i)
+               __clear_bit(*i, devs.d);
+
+       rcu_read_lock();
+       unsigned nr_replicas = 0, i;
+       for_each_set_bit(i, devs.d, BCH_SB_MEMBERS_MAX) {
+               struct bch_dev *ca = bch2_dev_rcu(c, i);
+
+               struct bch_dev_usage usage;
+               bch2_dev_usage_read_fast(ca, &usage);
+
+               if (!dev_buckets_free(ca, usage, m->op.watermark))
+                       continue;
+
+               nr_replicas += ca->mi.durability;
+               if (nr_replicas >= m->op.nr_replicas)
+                       break;
+       }
+       rcu_read_unlock();
+
+       return nr_replicas >= m->op.nr_replicas;
+}
+
 int bch2_data_update_init(struct btree_trans *trans,
                          struct btree_iter *iter,
                          struct moving_context *ctxt,
@@ -759,6 +793,12 @@ int bch2_data_update_init(struct btree_trans *trans,
                goto out_bkey_buf_exit;
        }
 
+       if ((m->op.flags & BCH_WRITE_alloc_nowait) &&
+           !can_allocate_without_blocking(c, m)) {
+               ret = -BCH_ERR_data_update_done_would_block;
+               goto out_bkey_buf_exit;
+       }
+
        if (reserve_sectors) {
                ret = bch2_disk_reservation_add(c, &m->op.res, reserve_sectors,
                                m->data_opts.extra_replicas
index 82f950ea1c26f75fe87bf1b76572971a731bb9cf..1e8f65f95d60659ed6fddeec5179ea9fa2074138 100644 (file)
        x(EINVAL,                       not_in_recovery)                        \
        x(EINVAL,                       cannot_rewind_recovery)                 \
        x(0,                            data_update_done)                       \
+       x(BCH_ERR_data_update_done,     data_update_done_would_block)           \
        x(BCH_ERR_data_update_done,     data_update_done_unwritten)             \
        x(BCH_ERR_data_update_done,     data_update_done_no_writes_needed)      \
        x(BCH_ERR_data_update_done,     data_update_done_no_snapshot)           \