Make sure btree_node_alloc() respects allocation policy when allocating
from the btree node reserve.

Signed-off-by: Kent Overstreet <[email protected]>
---
 fs/bcachefs/btree_update_interior.c | 41 ++++++++++++++++++++++++++---
 fs/bcachefs/extents.c               | 14 ++++++++++
 fs/bcachefs/extents.h               |  2 ++
 3 files changed, 54 insertions(+), 3 deletions(-)

diff --git a/fs/bcachefs/btree_update_interior.c 
b/fs/bcachefs/btree_update_interior.c
index 312ef203b27b..e4aa4fa749bc 100644
--- a/fs/bcachefs/btree_update_interior.c
+++ b/fs/bcachefs/btree_update_interior.c
@@ -14,6 +14,7 @@
 #include "btree_locking.h"
 #include "buckets.h"
 #include "clock.h"
+#include "disk_groups.h"
 #include "enumerated_ref.h"
 #include "error.h"
 #include "extents.h"
@@ -277,6 +278,36 @@ static void bch2_btree_node_free_never_used(struct 
btree_update *as,
        bch2_trans_node_drop(trans, b);
 }
 
+static bool can_use_btree_node(struct bch_fs *c,
+                              struct disk_reservation *res,
+                              unsigned target,
+                              struct bkey_s_c k)
+{
+       if (!bch2_bkey_devs_rw(c, k))
+               return false;
+
+       if (target && !bch2_bkey_in_target(c, k, target))
+               return false;
+
+       unsigned durability = bch2_bkey_durability(c, k);
+
+       if (durability >= res->nr_replicas)
+               return true;
+
+       struct bch_devs_mask devs = target_rw_devs(c, BCH_DATA_btree, target);
+
+       guard(rcu)();
+
+       unsigned durability_available = 0, i;
+       for_each_set_bit(i, devs.d, BCH_SB_MEMBERS_MAX) {
+               struct bch_dev *ca = bch2_dev_rcu_noerror(c, i);
+               if (ca)
+                       durability_available += ca->mi.durability;
+       }
+
+       return durability >= durability_available;
+}
+
 static struct btree *__bch2_btree_node_alloc(struct btree_trans *trans,
                                             struct disk_reservation *res,
                                             struct closure *cl,
@@ -303,10 +334,14 @@ static struct btree *__bch2_btree_node_alloc(struct 
btree_trans *trans,
        mutex_lock(&c->btree_reserve_cache_lock);
        if (c->btree_reserve_cache_nr > nr_reserve) {
                for (struct btree_alloc *a = c->btree_reserve_cache;
-                    a < c->btree_reserve_cache + c->btree_reserve_cache_nr;
-                    a++) {
-                       if (target && !bch2_bkey_in_target(c, 
bkey_i_to_s_c(&a->k), target))
+                    a < c->btree_reserve_cache + c->btree_reserve_cache_nr;) {
+                       /* check if it has sufficient durability */
+
+                       if (!can_use_btree_node(c, res, target, 
bkey_i_to_s_c(&a->k))) {
+                               bch2_open_buckets_put(c, &a->ob);
+                               *a = 
c->btree_reserve_cache[--c->btree_reserve_cache_nr];
                                continue;
+                       }
 
                        bkey_copy(&b->key, &a->k);
                        b->ob = a->ob;
diff --git a/fs/bcachefs/extents.c b/fs/bcachefs/extents.c
index b36ecfc0ab9d..8152ef1cbbcd 100644
--- a/fs/bcachefs/extents.c
+++ b/fs/bcachefs/extents.c
@@ -1006,6 +1006,20 @@ const struct bch_extent_ptr 
*bch2_bkey_has_device_c(struct bkey_s_c k, unsigned
        return NULL;
 }
 
+bool bch2_bkey_devs_rw(struct bch_fs *c, struct bkey_s_c k)
+{
+       struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+
+       guard(rcu)();
+       bkey_for_each_ptr(ptrs, ptr) {
+               CLASS(bch2_dev_tryget, ca)(c, ptr->dev);
+               if (!ca || ca->mi.state != BCH_MEMBER_STATE_rw)
+                       return false;
+       }
+
+       return true;
+}
+
 bool bch2_bkey_has_target(struct bch_fs *c, struct bkey_s_c k, unsigned target)
 {
        struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
diff --git a/fs/bcachefs/extents.h b/fs/bcachefs/extents.h
index f212f91c278d..35ee03cd5065 100644
--- a/fs/bcachefs/extents.h
+++ b/fs/bcachefs/extents.h
@@ -614,6 +614,8 @@ static inline struct bch_extent_ptr 
*bch2_bkey_has_device(struct bkey_s k, unsig
        return (void *) bch2_bkey_has_device_c(k.s_c, dev);
 }
 
+bool bch2_bkey_devs_rw(struct bch_fs *, struct bkey_s_c);
+
 bool bch2_bkey_has_target(struct bch_fs *, struct bkey_s_c, unsigned);
 bool bch2_bkey_in_target(struct bch_fs *, struct bkey_s_c, unsigned);
 
-- 
2.50.0


Reply via email to