summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/bcachefs/alloc_foreground.c284
-rw-r--r--fs/bcachefs/alloc_foreground.h7
-rw-r--r--fs/bcachefs/btree_cache.c5
-rw-r--r--fs/bcachefs/btree_journal_iter.c17
-rw-r--r--fs/bcachefs/btree_node_scan.c4
-rw-r--r--fs/bcachefs/data_update.c13
-rw-r--r--fs/bcachefs/ec.c38
-rw-r--r--fs/bcachefs/ec.h2
-rw-r--r--fs/bcachefs/errcode.h5
-rw-r--r--fs/bcachefs/journal.c14
-rw-r--r--fs/bcachefs/opts.h11
-rw-r--r--fs/bcachefs/trace.h5
12 files changed, 170 insertions, 235 deletions
diff --git a/fs/bcachefs/alloc_foreground.c b/fs/bcachefs/alloc_foreground.c
index 23a9fbb36f49..bb211d051d7d 100644
--- a/fs/bcachefs/alloc_foreground.c
+++ b/fs/bcachefs/alloc_foreground.c
@@ -206,8 +206,7 @@ static inline bool may_alloc_bucket(struct bch_fs *c,
static struct open_bucket *__try_alloc_bucket(struct bch_fs *c,
struct alloc_request *req,
- u64 bucket, u8 gen,
- struct closure *cl)
+ u64 bucket, u8 gen)
{
struct bch_dev *ca = req->ca;
@@ -222,12 +221,18 @@ static struct open_bucket *__try_alloc_bucket(struct bch_fs *c,
spin_lock(&c->freelist_lock);
if (unlikely(c->open_buckets_nr_free <= bch2_open_buckets_reserved(req->watermark))) {
- if (cl)
- closure_wait(&c->open_buckets_wait, cl);
-
track_event_change(&c->times[BCH_TIME_blocked_allocate_open_bucket], true);
+
+ int ret;
+ if (req->cl && !(req->flags & BCH_WRITE_alloc_nowait)) {
+ closure_wait(&c->open_buckets_wait, req->cl);
+ ret = bch_err_throw(c, open_bucket_alloc_blocked);
+ } else {
+ ret = bch_err_throw(c, open_buckets_empty);
+ }
+
spin_unlock(&c->freelist_lock);
- return ERR_PTR(bch_err_throw(c, open_buckets_empty));
+ return ERR_PTR(ret);
}
/* Recheck under lock: */
@@ -259,8 +264,7 @@ static struct open_bucket *__try_alloc_bucket(struct bch_fs *c,
static struct open_bucket *try_alloc_bucket(struct btree_trans *trans,
struct alloc_request *req,
- struct btree_iter *freespace_iter,
- struct closure *cl)
+ struct btree_iter *freespace_iter)
{
struct bch_fs *c = trans->c;
u64 b = freespace_iter->pos.offset & ~(~0ULL << 56);
@@ -275,7 +279,7 @@ static struct open_bucket *try_alloc_bucket(struct btree_trans *trans,
if (ret)
return NULL;
- return __try_alloc_bucket(c, req, b, gen, cl);
+ return __try_alloc_bucket(c, req, b, gen);
}
/*
@@ -283,8 +287,7 @@ static struct open_bucket *try_alloc_bucket(struct btree_trans *trans,
*/
static noinline struct open_bucket *
bch2_bucket_alloc_early(struct btree_trans *trans,
- struct alloc_request *req,
- struct closure *cl)
+ struct alloc_request *req)
{
struct bch_fs *c = trans->c;
struct bch_dev *ca = req->ca;
@@ -348,7 +351,7 @@ again:
req->counters.buckets_seen++;
ob = may_alloc_bucket(c, req, k.k->p)
- ? __try_alloc_bucket(c, req, k.k->p.offset, a->gen, cl)
+ ? __try_alloc_bucket(c, req, k.k->p.offset, a->gen)
: NULL;
next:
bch2_set_btree_iter_dontneed(trans, &citer);
@@ -374,8 +377,7 @@ next:
}
static struct open_bucket *bch2_bucket_alloc_freelist(struct btree_trans *trans,
- struct alloc_request *req,
- struct closure *cl)
+ struct alloc_request *req)
{
struct bch_dev *ca = req->ca;
struct btree_iter iter;
@@ -417,7 +419,7 @@ again:
goto next;
}
- ob = try_alloc_bucket(trans, req, &iter, cl);
+ ob = try_alloc_bucket(trans, req, &iter);
if (ob) {
if (!IS_ERR(ob))
*dev_alloc_cursor = iter.pos.offset;
@@ -450,7 +452,6 @@ fail:
static noinline void trace_bucket_alloc2(struct bch_fs *c,
struct alloc_request *req,
- struct closure *cl,
struct open_bucket *ob)
{
struct printbuf buf = PRINTBUF;
@@ -460,7 +461,8 @@ static noinline void trace_bucket_alloc2(struct bch_fs *c,
prt_printf(&buf, "dev\t%s (%u)\n", req->ca->name, req->ca->dev_idx);
prt_printf(&buf, "watermark\t%s\n", bch2_watermarks[req->watermark]);
prt_printf(&buf, "data type\t%s\n", __bch2_data_types[req->data_type]);
- prt_printf(&buf, "blocking\t%u\n", cl != NULL);
+ prt_printf(&buf, "blocking\t%u\n", !req->will_retry_target_devices &&
+ !req->will_retry_all_devices);
prt_printf(&buf, "free\t%llu\n", req->usage.buckets[BCH_DATA_free]);
prt_printf(&buf, "avail\t%llu\n", dev_buckets_free(req->ca, req->usage, req->watermark));
prt_printf(&buf, "copygc_wait\t%llu/%lli\n",
@@ -488,28 +490,23 @@ static noinline void trace_bucket_alloc2(struct bch_fs *c,
* bch2_bucket_alloc_trans - allocate a single bucket from a specific device
* @trans: transaction object
* @req: state for the entire allocation
- * @cl: if not NULL, closure to be used to wait if buckets not available
- * @nowait: if true, do not wait for buckets to become available
*
* Returns: an open_bucket on success, or an ERR_PTR() on failure.
*/
static struct open_bucket *bch2_bucket_alloc_trans(struct btree_trans *trans,
- struct alloc_request *req,
- struct closure *cl,
- bool nowait)
+ struct alloc_request *req)
{
struct bch_fs *c = trans->c;
struct bch_dev *ca = req->ca;
struct open_bucket *ob = NULL;
bool freespace = READ_ONCE(ca->mi.freespace_initialized);
- u64 avail;
- bool waiting = nowait;
+ bool waiting = false;
req->btree_bitmap = req->data_type == BCH_DATA_btree;
memset(&req->counters, 0, sizeof(req->counters));
again:
bch2_dev_usage_read_fast(ca, &req->usage);
- avail = dev_buckets_free(ca, req->usage, req->watermark);
+ u64 avail = dev_buckets_free(ca, req->usage, req->watermark);
if (req->usage.buckets[BCH_DATA_need_discard] > avail)
bch2_dev_do_discards(ca);
@@ -525,8 +522,11 @@ again:
c->recovery.pass_done < BCH_RECOVERY_PASS_check_allocations)
goto alloc;
- if (cl && !waiting) {
- closure_wait(&c->freelist_wait, cl);
+ if (req->cl &&
+ !req->will_retry_target_devices &&
+ !req->will_retry_all_devices &&
+ !(req->flags & BCH_WRITE_alloc_nowait)) {
+ closure_wait(&c->freelist_wait, req->cl);
waiting = true;
goto again;
}
@@ -541,8 +541,8 @@ again:
closure_wake_up(&c->freelist_wait);
alloc:
ob = likely(freespace)
- ? bch2_bucket_alloc_freelist(trans, req, cl)
- : bch2_bucket_alloc_early(trans, req, cl);
+ ? bch2_bucket_alloc_freelist(trans, req)
+ : bch2_bucket_alloc_early(trans, req);
if (req->counters.need_journal_commit * 2 > avail)
bch2_journal_flush_async(&c->journal, NULL);
@@ -571,7 +571,7 @@ err:
if (!IS_ERR(ob)
? trace_bucket_alloc_enabled()
: trace_bucket_alloc_fail_enabled())
- trace_bucket_alloc2(c, req, cl, ob);
+ trace_bucket_alloc2(c, req, ob);
return ob;
}
@@ -583,13 +583,14 @@ struct open_bucket *bch2_bucket_alloc(struct bch_fs *c, struct bch_dev *ca,
{
struct open_bucket *ob;
struct alloc_request req = {
+ .cl = cl,
.watermark = watermark,
.data_type = data_type,
.ca = ca,
};
bch2_trans_do(c,
- PTR_ERR_OR_ZERO(ob = bch2_bucket_alloc_trans(trans, &req, cl, false)));
+ PTR_ERR_OR_ZERO(ob = bch2_bucket_alloc_trans(trans, &req)));
return ob;
}
@@ -703,18 +704,26 @@ static int add_new_bucket(struct bch_fs *c,
return 0;
}
-inline int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
- struct alloc_request *req,
- struct dev_stripe_state *stripe,
- struct closure *cl)
+int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
+ struct alloc_request *req,
+ struct dev_stripe_state *stripe)
{
struct bch_fs *c = trans->c;
+ struct closure *cl = NULL;
int ret = 0;
BUG_ON(req->nr_effective >= req->nr_replicas);
+ /*
+ * Try nonblocking first, so that if one device is full we'll try from
+ * other devices:
+ */
+retry_blocking:
bch2_dev_alloc_list(c, stripe, &req->devs_may_alloc, &req->devs_sorted);
+ if (req->devs_sorted.nr == 1)
+ req->will_retry_target_devices = false;
+
darray_for_each(req->devs_sorted, i) {
req->ca = bch2_dev_tryget_noerror(c, *i);
if (!req->ca)
@@ -725,8 +734,7 @@ inline int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
continue;
}
- struct open_bucket *ob = bch2_bucket_alloc_trans(trans, req, cl,
- req->flags & BCH_WRITE_alloc_nowait);
+ struct open_bucket *ob = bch2_bucket_alloc_trans(trans, req);
if (!IS_ERR(ob))
bch2_dev_stripe_increment_inlined(req->ca, stripe, &req->usage);
bch2_dev_put(req->ca);
@@ -745,6 +753,14 @@ inline int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
if (ret == 1)
return 0;
+
+ if (ret &&
+ !bch2_err_matches(ret, BCH_ERR_transaction_restart) &&
+ req->will_retry_target_devices) {
+ req->will_retry_target_devices = false;
+ goto retry_blocking;
+ }
+
if (ret)
return ret;
return bch_err_throw(c, insufficient_devices);
@@ -759,20 +775,13 @@ inline int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
*/
static int bucket_alloc_from_stripe(struct btree_trans *trans,
- struct alloc_request *req,
- struct closure *cl)
+ struct alloc_request *req)
{
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);
+ bch2_ec_stripe_head_get(trans, req, 0);
if (IS_ERR(h))
return PTR_ERR(h);
if (!h)
@@ -887,79 +896,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;
- struct closure *cl = NULL;
- 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;
-
- if (req->ec) {
- ret = bucket_alloc_from_stripe(trans, req, _cl);
- } else {
-retry_blocking:
- /*
- * Try nonblocking first, so that if one device is full we'll try from
- * other devices:
- */
- ret = bch2_bucket_alloc_set_trans(trans, req, &req->wp->stripe, cl);
- if (ret &&
- !bch2_err_matches(ret, BCH_ERR_transaction_restart) &&
- !bch2_err_matches(ret, BCH_ERR_insufficient_devices) &&
- !cl && _cl) {
- cl = _cl;
- goto retry_blocking;
- }
- }
-
- return ret;
-}
-
-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
@@ -1255,72 +1191,96 @@ 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->cl = cl;
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->ptrs.nr = 0;
- req->nr_effective = 0;
- req->have_cache = false;
- write_points_nr = c->write_points_nr;
+ req->ec = erasure_code;
+ req->will_retry_target_devices = true;
+ req->will_retry_all_devices = true;
+ req->ptrs.nr = 0;
+ req->nr_effective = 0;
+ req->have_cache = false;
+ write_points_nr = c->write_points_nr;
*wp_ret = req->wp = writepoint_find(trans, write_point.v);
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;
+
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);
- 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;
-
- /* 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;
- }
- }
+ /* 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);
- /*
- * Only try to allocate cache (durability = 0 devices) from the
- * specified target:
- */
- req->have_cache = true;
- req->target = 0;
+ open_bucket_for_each(c, &req->ptrs, ob, i)
+ __clear_bit(ob->dev, req->devs_may_alloc.d);
- 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);
+ ret = bucket_alloc_set_writepoint(c, req) ?:
+ bucket_alloc_set_partial(c, req) ?:
+ (req->ec
+ ? bucket_alloc_from_stripe(trans, req)
+ : bch2_bucket_alloc_set_trans(trans, req, &req->wp->stripe));
- if (erasure_code && !ec_open_bucket(c, &req->ptrs))
- pr_debug("failed to get ec bucket: ret %u", ret);
+ if (bch2_err_matches(ret, BCH_ERR_transaction_restart) ||
+ bch2_err_matches(ret, BCH_ERR_operation_blocked))
+ goto err;
+
+ if (ret == -BCH_ERR_freelist_empty ||
+ ret == -BCH_ERR_insufficient_devices) {
+ if (req->will_retry_all_devices) {
+ BUG_ON(!req->will_retry_all_devices);
+ req->will_retry_all_devices = false;
+ /*
+ * Only try to allocate cache (durability = 0 devices) from the
+ * specified target:
+ */
+ if (req->target &&
+ (!(flags & BCH_WRITE_only_specified_devs) ||
+ (ret == -BCH_ERR_insufficient_devices))) {
+ req->have_cache = true;
+ req->target = 0;
+ }
+ continue;
+ }
- if (ret == -BCH_ERR_insufficient_devices &&
- req->nr_effective >= nr_replicas_required)
- ret = 0;
+ if (ret == -BCH_ERR_insufficient_devices &&
+ req->nr_effective >= nr_replicas_required)
+ ret = 0;
+ else
+ goto err;
+ }
- if (ret)
- goto err;
+ if (req->nr_effective < req->nr_replicas && req->ec) {
+ if (!ec_open_bucket(c, &req->ptrs))
+ panic("ret %i %s\n", ret, bch2_err_str(ret));
+ req->ec = false;
+ req->will_retry_target_devices = true;
+ req->will_retry_all_devices = true;
+ continue;
+ }
+
+ BUG_ON(req->nr_effective < req->nr_replicas);
+ BUG_ON(ret < 0);
+ break;
+ }
if (req->nr_effective > req->nr_replicas)
deallocate_extra_replicas(c, req);
diff --git a/fs/bcachefs/alloc_foreground.h b/fs/bcachefs/alloc_foreground.h
index 1b3fc8460096..90eb8604a0a2 100644
--- a/fs/bcachefs/alloc_foreground.h
+++ b/fs/bcachefs/alloc_foreground.h
@@ -26,9 +26,12 @@ struct dev_alloc_list {
};
struct alloc_request {
+ struct closure *cl;
unsigned nr_replicas;
unsigned target;
- bool ec;
+ bool ec:1;
+ bool will_retry_target_devices:1;
+ bool will_retry_all_devices:1;
enum bch_watermark watermark;
enum bch_write_flags flags;
enum bch_data_type data_type;
@@ -224,7 +227,7 @@ static inline bool bch2_bucket_is_open_safe(struct bch_fs *c, unsigned dev, u64
enum bch_write_flags;
int bch2_bucket_alloc_set_trans(struct btree_trans *, struct alloc_request *,
- struct dev_stripe_state *, struct closure *);
+ struct dev_stripe_state *);
int bch2_alloc_sectors_start_trans(struct btree_trans *,
unsigned, unsigned,
diff --git a/fs/bcachefs/btree_cache.c b/fs/bcachefs/btree_cache.c
index 702c8f7081d7..49505653fe12 100644
--- a/fs/bcachefs/btree_cache.c
+++ b/fs/bcachefs/btree_cache.c
@@ -187,10 +187,7 @@ static struct btree *__btree_node_mem_alloc(struct bch_fs *c, gfp_t gfp)
struct btree *__bch2_btree_node_mem_alloc(struct bch_fs *c)
{
- struct btree_cache *bc = &c->btree_cache;
- struct btree *b;
-
- b = __btree_node_mem_alloc(c, GFP_KERNEL);
+ struct btree *b = __btree_node_mem_alloc(c, GFP_KERNEL);
if (!b)
return NULL;
diff --git a/fs/bcachefs/btree_journal_iter.c b/fs/bcachefs/btree_journal_iter.c
index 341d31b3a1f1..ea839560a136 100644
--- a/fs/bcachefs/btree_journal_iter.c
+++ b/fs/bcachefs/btree_journal_iter.c
@@ -717,18 +717,6 @@ static void __journal_keys_sort(struct journal_keys *keys)
keys->nr = dst - keys->data;
}
-static bool should_rewind_entry(struct bch_fs *c, struct jset_entry *entry)
-{
- if (entry->level)
- return false;
- if (btree_id_is_alloc(entry->btree_id))
- return false;
- if (c->opts.journal_rewind_no_extents &&
- entry->btree_id == BTREE_ID_extents)
- return false;
- return true;
-}
-
int bch2_journal_keys_sort(struct bch_fs *c)
{
struct genradix_iter iter;
@@ -747,8 +735,9 @@ int bch2_journal_keys_sort(struct bch_fs *c)
cond_resched();
vstruct_for_each(&i->j, entry) {
- bool rewind = le64_to_cpu(i->j.seq) >= rewind_seq &&
- should_rewind_entry(c, entry);
+ bool rewind = !entry->level &&
+ !btree_id_is_alloc(entry->btree_id) &&
+ le64_to_cpu(i->j.seq) >= rewind_seq;
if (entry->type != (rewind
? BCH_JSET_ENTRY_overwrite
diff --git a/fs/bcachefs/btree_node_scan.c b/fs/bcachefs/btree_node_scan.c
index cc7af8fe689e..365808b4b7c0 100644
--- a/fs/bcachefs/btree_node_scan.c
+++ b/fs/bcachefs/btree_node_scan.c
@@ -196,12 +196,14 @@ static void try_read_btree_node(struct find_btree_nodes *f, struct bch_dev *ca,
CLASS(printbuf, buf)();
if (!bch2_btree_node_read_done(c, ca, b, NULL, &buf)) {
+ /* read_done will swap out b->data for another buffer */
+ bn = b->data;
/*
* Grab journal_seq here because we want the max journal_seq of
* any bset; read_done sorts down to a single set and picks the
* max journal_seq
*/
- n.journal_seq = le64_to_cpu(b->data->keys.journal_seq),
+ n.journal_seq = le64_to_cpu(bn->keys.journal_seq),
n.sectors_written = b->written;
mutex_lock(&f->lock);
diff --git a/fs/bcachefs/data_update.c b/fs/bcachefs/data_update.c
index 3968f3be7f3b..e848e210a9bf 100644
--- a/fs/bcachefs/data_update.c
+++ b/fs/bcachefs/data_update.c
@@ -783,9 +783,6 @@ static int can_write_extent(struct bch_fs *c, struct data_update *m)
darray_for_each(m->op.devs_have, i)
__clear_bit(*i, devs.d);
- CLASS(printbuf, buf)();
- buf.atomic++;
-
guard(rcu)();
unsigned nr_replicas = 0, i;
@@ -797,11 +794,7 @@ static int can_write_extent(struct bch_fs *c, struct data_update *m)
struct bch_dev_usage usage;
bch2_dev_usage_read_fast(ca, &usage);
- u64 nr_free = dev_buckets_free(ca, usage, m->op.watermark);
-
- prt_printf(&buf, "%s=%llu ", ca->name, nr_free);
-
- if (!nr_free)
+ if (!dev_buckets_free(ca, usage, m->op.watermark))
continue;
nr_replicas += ca->mi.durability;
@@ -809,10 +802,8 @@ static int can_write_extent(struct bch_fs *c, struct data_update *m)
break;
}
- if (!nr_replicas) {
- trace_data_update_done_no_rw_devs(c, buf.buf);
+ if (!nr_replicas)
return bch_err_throw(c, data_update_done_no_rw_devs);
- }
if (nr_replicas < m->op.nr_replicas)
return bch_err_throw(c, insufficient_devices);
return 0;
diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c
index 687c3ba98095..71956ee86a9c 100644
--- a/fs/bcachefs/ec.c
+++ b/fs/bcachefs/ec.c
@@ -1720,8 +1720,7 @@ err:
static int new_stripe_alloc_buckets(struct btree_trans *trans,
struct alloc_request *req,
- struct ec_stripe_head *h, struct ec_stripe_new *s,
- struct closure *cl)
+ struct ec_stripe_head *h, struct ec_stripe_new *s)
{
struct bch_fs *c = trans->c;
struct open_bucket *ob;
@@ -1771,7 +1770,7 @@ static int new_stripe_alloc_buckets(struct btree_trans *trans,
req->nr_effective = nr_have_parity;
req->data_type = BCH_DATA_parity;
- ret = bch2_bucket_alloc_set_trans(trans, req, &h->parity_stripe, cl);
+ ret = bch2_bucket_alloc_set_trans(trans, req, &h->parity_stripe);
open_bucket_for_each(c, &req->ptrs, ob, i) {
j = find_next_zero_bit(s->blocks_gotten,
@@ -1794,7 +1793,7 @@ static int new_stripe_alloc_buckets(struct btree_trans *trans,
req->nr_effective = nr_have_data;
req->data_type = BCH_DATA_user;
- ret = bch2_bucket_alloc_set_trans(trans, req, &h->block_stripe, cl);
+ ret = bch2_bucket_alloc_set_trans(trans, req, &h->block_stripe);
open_bucket_for_each(c, &req->ptrs, ob, i) {
j = find_next_zero_bit(s->blocks_gotten,
@@ -1926,7 +1925,7 @@ static int __bch2_ec_stripe_head_reuse(struct btree_trans *trans, struct ec_stri
}
bch2_trans_iter_exit(trans, &lru_iter);
if (!ret)
- ret = bch_err_throw(c, stripe_alloc_blocked);
+ return bch_err_throw(c, stripe_alloc_blocked);
if (ret == 1)
ret = 0;
if (ret)
@@ -1998,8 +1997,7 @@ err:
struct ec_stripe_head *bch2_ec_stripe_head_get(struct btree_trans *trans,
struct alloc_request *req,
- unsigned algo,
- struct closure *cl)
+ unsigned algo)
{
struct bch_fs *c = trans->c;
unsigned redundancy = req->nr_replicas - 1;
@@ -2041,12 +2039,18 @@ struct ec_stripe_head *bch2_ec_stripe_head_get(struct btree_trans *trans,
if (s->have_existing_stripe)
goto alloc_existing;
+
/* First, try to allocate a full stripe: */
enum bch_watermark saved_watermark = BCH_WATERMARK_stripe;
- swap(req->watermark, saved_watermark);
- ret = new_stripe_alloc_buckets(trans, req, h, s, NULL) ?:
+ unsigned saved_flags = req->flags | BCH_WRITE_alloc_nowait;
+ swap(req->watermark, saved_watermark);
+ swap(req->flags, saved_flags);
+
+ ret = new_stripe_alloc_buckets(trans, req, h, s) ?:
__bch2_ec_stripe_head_reserve(trans, h, s);
- swap(req->watermark, saved_watermark);
+
+ swap(req->watermark, saved_watermark);
+ swap(req->flags, saved_flags);
if (!ret)
goto allocate_buf;
@@ -2062,19 +2066,25 @@ struct ec_stripe_head *bch2_ec_stripe_head_get(struct btree_trans *trans,
ret = __bch2_ec_stripe_head_reuse(trans, h, s);
if (!ret)
break;
- if (waiting || !cl || ret != -BCH_ERR_stripe_alloc_blocked)
+ if (waiting ||
+ (req->flags & BCH_WRITE_alloc_nowait) ||
+ ret != -BCH_ERR_stripe_alloc_blocked)
goto err;
if (req->watermark == BCH_WATERMARK_copygc) {
- ret = new_stripe_alloc_buckets(trans, req, h, s, NULL) ?:
+ /* Don't self-deadlock copygc */
+ swap(req->flags, saved_flags);
+ ret = new_stripe_alloc_buckets(trans, req, h, s) ?:
__bch2_ec_stripe_head_reserve(trans, h, s);
+ swap(req->flags, saved_flags);
+
if (ret)
goto err;
goto allocate_buf;
}
/* XXX freelist_wait? */
- closure_wait(&c->freelist_wait, cl);
+ closure_wait(&c->freelist_wait, req->cl);
waiting = true;
}
@@ -2085,7 +2095,7 @@ alloc_existing:
* Retry allocating buckets, with the watermark for this
* particular write:
*/
- ret = new_stripe_alloc_buckets(trans, req, h, s, cl);
+ ret = new_stripe_alloc_buckets(trans, req, h, s);
if (ret)
goto err;
diff --git a/fs/bcachefs/ec.h b/fs/bcachefs/ec.h
index 548048adf0d5..756f14bd7bb7 100644
--- a/fs/bcachefs/ec.h
+++ b/fs/bcachefs/ec.h
@@ -258,7 +258,7 @@ void bch2_ec_stripe_head_put(struct bch_fs *, struct ec_stripe_head *);
struct alloc_request;
struct ec_stripe_head *bch2_ec_stripe_head_get(struct btree_trans *,
- struct alloc_request *, unsigned, struct closure *);
+ struct alloc_request *, unsigned);
void bch2_do_stripe_deletes(struct bch_fs *);
void bch2_ec_do_stripe_creates(struct bch_fs *);
diff --git a/fs/bcachefs/errcode.h b/fs/bcachefs/errcode.h
index 2de0dc91a69e..a66c96bd9556 100644
--- a/fs/bcachefs/errcode.h
+++ b/fs/bcachefs/errcode.h
@@ -236,6 +236,9 @@
x(0, operation_blocked) \
x(BCH_ERR_operation_blocked, btree_cache_cannibalize_lock_blocked) \
x(BCH_ERR_operation_blocked, journal_res_blocked) \
+ x(BCH_ERR_operation_blocked, bucket_alloc_blocked) \
+ x(BCH_ERR_operation_blocked, open_bucket_alloc_blocked) \
+ x(BCH_ERR_operation_blocked, stripe_alloc_blocked) \
x(BCH_ERR_journal_res_blocked, journal_blocked) \
x(BCH_ERR_journal_res_blocked, journal_max_in_flight) \
x(BCH_ERR_journal_res_blocked, journal_max_open) \
@@ -244,8 +247,6 @@
x(BCH_ERR_journal_res_blocked, journal_buf_enomem) \
x(BCH_ERR_journal_res_blocked, journal_stuck) \
x(BCH_ERR_journal_res_blocked, journal_retry_open) \
- x(BCH_ERR_journal_res_blocked, bucket_alloc_blocked) \
- x(BCH_ERR_journal_res_blocked, stripe_alloc_blocked) \
x(BCH_ERR_invalid, invalid_sb) \
x(BCH_ERR_invalid_sb, invalid_sb_magic) \
x(BCH_ERR_invalid_sb, invalid_sb_version) \
diff --git a/fs/bcachefs/journal.c b/fs/bcachefs/journal.c
index ce5340611de6..f22b05e02c1e 100644
--- a/fs/bcachefs/journal.c
+++ b/fs/bcachefs/journal.c
@@ -1376,6 +1376,7 @@ int bch2_dev_journal_alloc(struct bch_dev *ca, bool new_fs)
return bch_err_throw(c, erofs_filesystem_full);
}
+ unsigned nr;
int ret;
if (dynamic_fault("bcachefs:add:journal_alloc")) {
@@ -1384,19 +1385,16 @@ int bch2_dev_journal_alloc(struct bch_dev *ca, bool new_fs)
}
/* 1/128th of the device by default: */
- unsigned nr = ca->mi.nbuckets >> 7;
+ nr = ca->mi.nbuckets >> 7;
/*
- * clamp journal size to 8GB, or 32GB with large_journal option:
+ * clamp journal size to 8192 buckets or 8GB (in sectors), whichever
+ * is smaller:
*/
- unsigned max_sectors = 1 << 24;
-
- if (c->opts.large_journal)
- max_sectors *= 4;
-
nr = clamp_t(unsigned, nr,
BCH_JOURNAL_BUCKETS_MIN,
- max_sectors / ca->mi.bucket_size);
+ min(1 << 13,
+ (1 << 24) / ca->mi.bucket_size));
ret = bch2_set_nr_journal_buckets_loop(c, ca, nr, new_fs);
err:
diff --git a/fs/bcachefs/opts.h b/fs/bcachefs/opts.h
index 4a7a60588c10..63f8e254495c 100644
--- a/fs/bcachefs/opts.h
+++ b/fs/bcachefs/opts.h
@@ -343,12 +343,6 @@ enum fsck_err_opts {
OPT_UINT(0, U32_MAX), \
BCH_SB_JOURNAL_RECLAIM_DELAY, 100, \
NULL, "Delay in milliseconds before automatic journal reclaim")\
- x(large_journal, bool, \
- OPT_FS|OPT_MOUNT|OPT_FORMAT, \
- OPT_BOOL(), \
- BCH2_NO_SB_OPT, false, \
- NULL, "Allocate a bigger than normal journal: recovery from unclean "\
- "shutdown will be slower, but more info will be available for debugging")\
x(move_bytes_in_flight, u32, \
OPT_HUMAN_READABLE|OPT_FS|OPT_MOUNT|OPT_RUNTIME, \
OPT_UINT(1024, U32_MAX), \
@@ -395,11 +389,6 @@ enum fsck_err_opts {
OPT_UINT(0, U64_MAX), \
BCH2_NO_SB_OPT, 0, \
NULL, "Rewind journal") \
- x(journal_rewind_no_extents, bool, \
- OPT_FS|OPT_MOUNT, \
- OPT_BOOL(), \
- BCH2_NO_SB_OPT, 0, \
- NULL, "Don't rewind extents when rewinding journal") \
x(recovery_passes, u64, \
OPT_FS|OPT_MOUNT, \
OPT_BITFIELD(bch2_recovery_passes), \
diff --git a/fs/bcachefs/trace.h b/fs/bcachefs/trace.h
index 9324ef32903d..b5dae1145afa 100644
--- a/fs/bcachefs/trace.h
+++ b/fs/bcachefs/trace.h
@@ -1330,11 +1330,6 @@ DEFINE_EVENT(fs_str, data_update,
TP_ARGS(c, str)
);
-DEFINE_EVENT(fs_str, data_update_done_no_rw_devs,
- TP_PROTO(struct bch_fs *c, const char *str),
- TP_ARGS(c, str)
-);
-
DEFINE_EVENT(fs_str, io_move_pred,
TP_PROTO(struct bch_fs *c, const char *str),
TP_ARGS(c, str)