summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/bcachefs/alloc_foreground.c156
1 files changed, 60 insertions, 96 deletions
diff --git a/fs/bcachefs/alloc_foreground.c b/fs/bcachefs/alloc_foreground.c
index a6bce46c696a..5b9e64163d37 100644
--- a/fs/bcachefs/alloc_foreground.c
+++ b/fs/bcachefs/alloc_foreground.c
@@ -779,12 +779,6 @@ static int bucket_alloc_from_stripe(struct btree_trans *trans,
struct bch_fs *c = trans->c;
int ret = 0;
- if (req->nr_replicas < 2)
- return 0;
-
- if (ec_open_bucket(c, &req->ptrs))
- return 0;
-
struct ec_stripe_head *h =
bch2_ec_stripe_head_get(trans, req, 0, cl);
if (IS_ERR(h))
@@ -901,62 +895,6 @@ unlock:
return ret;
}
-static int __open_bucket_add_buckets(struct btree_trans *trans,
- struct alloc_request *req,
- struct closure *cl)
-{
- struct bch_fs *c = trans->c;
- struct open_bucket *ob;
- unsigned i;
- int ret;
-
- req->devs_may_alloc = target_rw_devs(c, req->wp->data_type, req->target);
-
- /* Don't allocate from devices we already have pointers to: */
- darray_for_each(*req->devs_have, i)
- __clear_bit(*i, req->devs_may_alloc.d);
-
- open_bucket_for_each(c, &req->ptrs, ob, i)
- __clear_bit(ob->dev, req->devs_may_alloc.d);
-
- ret = bucket_alloc_set_writepoint(c, req);
- if (ret)
- return ret;
-
- ret = bucket_alloc_set_partial(c, req);
- if (ret)
- return ret;
-
- return req->ec
- ? bucket_alloc_from_stripe(trans, req, cl)
- : bch2_bucket_alloc_set_trans(trans, req, &req->wp->stripe, cl);
-}
-
-static int open_bucket_add_buckets(struct btree_trans *trans,
- struct alloc_request *req,
- struct closure *cl)
-{
- int ret;
-
- if (req->ec && !ec_open_bucket(trans->c, &req->ptrs)) {
- ret = __open_bucket_add_buckets(trans, req, cl);
- if (bch2_err_matches(ret, BCH_ERR_transaction_restart) ||
- bch2_err_matches(ret, BCH_ERR_operation_blocked) ||
- bch2_err_matches(ret, BCH_ERR_freelist_empty) ||
- bch2_err_matches(ret, BCH_ERR_open_buckets_empty))
- return ret;
- if (req->nr_effective >= req->nr_replicas)
- return 0;
- }
-
- bool ec = false;
- swap(ec, req->ec);
- ret = __open_bucket_add_buckets(trans, req, cl);
- swap(ec, req->ec);
-
- return ret < 0 ? ret : 0;
-}
-
/**
* should_drop_bucket - check if this is open_bucket should go away
* @ob: open_bucket to predicate on
@@ -1236,7 +1174,7 @@ int bch2_alloc_sectors_start_trans(struct btree_trans *trans,
unsigned nr_replicas_required,
enum bch_watermark watermark,
enum bch_write_flags flags,
- struct closure *cl,
+ struct closure *_cl,
struct write_point **wp_ret)
{
struct bch_fs *c = trans->c;
@@ -1252,15 +1190,18 @@ int bch2_alloc_sectors_start_trans(struct btree_trans *trans,
if (!IS_ENABLED(CONFIG_BCACHEFS_ERASURE_CODING))
erasure_code = false;
+ if (nr_replicas < 2)
+ erasure_code = false;
+
req->nr_replicas = nr_replicas;
req->target = target;
- req->ec = erasure_code;
req->watermark = watermark;
req->flags = flags;
req->devs_have = devs_have;
BUG_ON(!nr_replicas || !nr_replicas_required);
retry:
+ req->ec = erasure_code;
req->ptrs.nr = 0;
req->nr_effective = 0;
req->have_cache = false;
@@ -1270,54 +1211,77 @@ retry:
req->data_type = req->wp->data_type;
+ /* metadata may not allocate on cache devices: */
+ if (req->data_type != BCH_DATA_user)
+ req->have_cache = true;
+
+ /* If we're going to fall back to the whole fs, try nonblocking first */
+ struct closure *cl = req->target && !(flags & BCH_WRITE_only_specified_devs)
+ ? _cl
+ : NULL;
+
ret = bch2_trans_relock(trans);
if (ret)
goto err;
- /* metadata may not allocate on cache devices: */
- if (req->data_type != BCH_DATA_user)
- req->have_cache = true;
+ while (1) {
+ req->devs_may_alloc = target_rw_devs(c, req->wp->data_type, req->target);
+
+ /* Don't allocate from devices we already have pointers to: */
+ darray_for_each(*req->devs_have, i)
+ __clear_bit(*i, req->devs_may_alloc.d);
+
+ open_bucket_for_each(c, &req->ptrs, ob, i)
+ __clear_bit(ob->dev, req->devs_may_alloc.d);
- if (target && !(flags & BCH_WRITE_only_specified_devs)) {
- ret = open_bucket_add_buckets(trans, req, NULL);
- if (!ret ||
- bch2_err_matches(ret, BCH_ERR_transaction_restart))
- goto alloc_done;
+ ret = bucket_alloc_set_writepoint(c, req) ?:
+ bucket_alloc_set_partial(c, req) ?:
+ (req->ec
+ ? bucket_alloc_from_stripe(trans, req, _cl)
+ : bch2_bucket_alloc_set_trans(trans, req, &req->wp->stripe, cl));
+
+ if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+ goto err;
/* Don't retry from all devices if we're out of open buckets: */
- if (bch2_err_matches(ret, BCH_ERR_open_buckets_empty)) {
- int ret2 = open_bucket_add_buckets(trans, req, cl);
- if (!ret2 ||
- bch2_err_matches(ret2, BCH_ERR_transaction_restart) ||
- bch2_err_matches(ret2, BCH_ERR_open_buckets_empty)) {
- ret = ret2;
- goto alloc_done;
- }
+ if (ret == -BCH_ERR_open_buckets_empty)
+ goto retry_blocking;
+
+ if (ret == -BCH_ERR_freelist_empty) {
+ if (req->target && !(flags & BCH_WRITE_only_specified_devs))
+ goto retry_all;
+ goto retry_blocking;
}
+ if (ret == -BCH_ERR_insufficient_devices && req->target)
+ goto retry_all;
+
+ if (req->nr_effective < req->nr_replicas && req->ec) {
+ req->ec = false;
+ continue;
+ }
+
+ if (ret == -BCH_ERR_insufficient_devices) {
+ if (req->nr_effective < nr_replicas_required)
+ goto err;
+ ret = 0;
+ }
+
+ BUG_ON(ret < 0);
+ break;
+retry_blocking:
+ if (cl == _cl)
+ goto err;
+ cl = _cl;
+ continue;
+retry_all:
/*
* Only try to allocate cache (durability = 0 devices) from the
* specified target:
*/
req->have_cache = true;
req->target = 0;
-
- ret = open_bucket_add_buckets(trans, req, cl);
- } else {
- ret = open_bucket_add_buckets(trans, req, cl);
}
-alloc_done:
- BUG_ON(!ret && req->nr_effective < req->nr_replicas);
-
- if (erasure_code && !ec_open_bucket(c, &req->ptrs))
- pr_debug("failed to get ec bucket: ret %u", ret);
-
- if (ret == -BCH_ERR_insufficient_devices &&
- req->nr_effective >= nr_replicas_required)
- ret = 0;
-
- if (ret)
- goto err;
if (req->nr_effective > req->nr_replicas)
deallocate_extra_replicas(c, req);