summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/bcachefs/alloc_foreground.c284
-rw-r--r--fs/bcachefs/alloc_foreground.h7
-rw-r--r--fs/bcachefs/backpointers.c3
-rw-r--r--fs/bcachefs/btree_io.c1
-rw-r--r--fs/bcachefs/btree_trans_commit.c2
-rw-r--r--fs/bcachefs/btree_update.c27
-rw-r--r--fs/bcachefs/btree_update.h21
-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.h7
-rw-r--r--fs/bcachefs/fs.c4
-rw-r--r--fs/bcachefs/io_write.c1
-rw-r--r--fs/bcachefs/journal.c3
-rw-r--r--fs/bcachefs/recovery_passes.c2
-rw-r--r--fs/bcachefs/sb-members.c1
-rw-r--r--fs/bcachefs/super.c10
-rw-r--r--fs/bcachefs/trace.h5
18 files changed, 246 insertions, 185 deletions
diff --git a/fs/bcachefs/alloc_foreground.c b/fs/bcachefs/alloc_foreground.c
index 1ecd635852a9..23a9fbb36f49 100644
--- a/fs/bcachefs/alloc_foreground.c
+++ b/fs/bcachefs/alloc_foreground.c
@@ -206,7 +206,8 @@ 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)
+ u64 bucket, u8 gen,
+ struct closure *cl)
{
struct bch_dev *ca = req->ca;
@@ -221,18 +222,12 @@ 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))) {
- 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);
- }
+ if (cl)
+ closure_wait(&c->open_buckets_wait, cl);
+ track_event_change(&c->times[BCH_TIME_blocked_allocate_open_bucket], true);
spin_unlock(&c->freelist_lock);
- return ERR_PTR(ret);
+ return ERR_PTR(bch_err_throw(c, open_buckets_empty));
}
/* Recheck under lock: */
@@ -264,7 +259,8 @@ 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 btree_iter *freespace_iter,
+ struct closure *cl)
{
struct bch_fs *c = trans->c;
u64 b = freespace_iter->pos.offset & ~(~0ULL << 56);
@@ -279,7 +275,7 @@ static struct open_bucket *try_alloc_bucket(struct btree_trans *trans,
if (ret)
return NULL;
- return __try_alloc_bucket(c, req, b, gen);
+ return __try_alloc_bucket(c, req, b, gen, cl);
}
/*
@@ -287,7 +283,8 @@ 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 alloc_request *req,
+ struct closure *cl)
{
struct bch_fs *c = trans->c;
struct bch_dev *ca = req->ca;
@@ -351,7 +348,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)
+ ? __try_alloc_bucket(c, req, k.k->p.offset, a->gen, cl)
: NULL;
next:
bch2_set_btree_iter_dontneed(trans, &citer);
@@ -377,7 +374,8 @@ next:
}
static struct open_bucket *bch2_bucket_alloc_freelist(struct btree_trans *trans,
- struct alloc_request *req)
+ struct alloc_request *req,
+ struct closure *cl)
{
struct bch_dev *ca = req->ca;
struct btree_iter iter;
@@ -419,7 +417,7 @@ again:
goto next;
}
- ob = try_alloc_bucket(trans, req, &iter);
+ ob = try_alloc_bucket(trans, req, &iter, cl);
if (ob) {
if (!IS_ERR(ob))
*dev_alloc_cursor = iter.pos.offset;
@@ -452,6 +450,7 @@ 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;
@@ -461,8 +460,7 @@ 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", !req->will_retry_target_devices &&
- !req->will_retry_all_devices);
+ prt_printf(&buf, "blocking\t%u\n", cl != NULL);
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",
@@ -490,23 +488,28 @@ 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 alloc_request *req,
+ struct closure *cl,
+ bool nowait)
{
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);
- bool waiting = false;
+ u64 avail;
+ bool waiting = nowait;
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);
- u64 avail = dev_buckets_free(ca, req->usage, req->watermark);
+ avail = dev_buckets_free(ca, req->usage, req->watermark);
if (req->usage.buckets[BCH_DATA_need_discard] > avail)
bch2_dev_do_discards(ca);
@@ -522,12 +525,8 @@ again:
c->recovery.pass_done < BCH_RECOVERY_PASS_check_allocations)
goto alloc;
- if (!waiting &&
- 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);
+ if (cl && !waiting) {
+ closure_wait(&c->freelist_wait, cl);
waiting = true;
goto again;
}
@@ -542,8 +541,8 @@ again:
closure_wake_up(&c->freelist_wait);
alloc:
ob = likely(freespace)
- ? bch2_bucket_alloc_freelist(trans, req)
- : bch2_bucket_alloc_early(trans, req);
+ ? bch2_bucket_alloc_freelist(trans, req, cl)
+ : bch2_bucket_alloc_early(trans, req, cl);
if (req->counters.need_journal_commit * 2 > avail)
bch2_journal_flush_async(&c->journal, NULL);
@@ -572,7 +571,7 @@ err:
if (!IS_ERR(ob)
? trace_bucket_alloc_enabled()
: trace_bucket_alloc_fail_enabled())
- trace_bucket_alloc2(c, req, ob);
+ trace_bucket_alloc2(c, req, cl, ob);
return ob;
}
@@ -584,14 +583,13 @@ 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)));
+ PTR_ERR_OR_ZERO(ob = bch2_bucket_alloc_trans(trans, &req, cl, false)));
return ob;
}
@@ -705,26 +703,18 @@ static int add_new_bucket(struct bch_fs *c,
return 0;
}
-int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
- struct alloc_request *req,
- struct dev_stripe_state *stripe)
+inline int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
+ struct alloc_request *req,
+ struct dev_stripe_state *stripe,
+ struct closure *cl)
{
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)
@@ -735,7 +725,8 @@ retry_blocking:
continue;
}
- struct open_bucket *ob = bch2_bucket_alloc_trans(trans, req);
+ struct open_bucket *ob = bch2_bucket_alloc_trans(trans, req, cl,
+ req->flags & BCH_WRITE_alloc_nowait);
if (!IS_ERR(ob))
bch2_dev_stripe_increment_inlined(req->ca, stripe, &req->usage);
bch2_dev_put(req->ca);
@@ -754,14 +745,6 @@ retry_blocking:
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);
@@ -776,13 +759,20 @@ retry_blocking:
*/
static int bucket_alloc_from_stripe(struct btree_trans *trans,
- struct alloc_request *req)
+ struct alloc_request *req,
+ struct closure *cl)
{
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);
+ bch2_ec_stripe_head_get(trans, req, 0, cl);
if (IS_ERR(h))
return PTR_ERR(h);
if (!h)
@@ -897,6 +887,79 @@ 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
@@ -1192,95 +1255,72 @@ 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->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;
+ 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;
- 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);
-
- 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));
+ /* metadata may not allocate on cache devices: */
+ if (req->data_type != BCH_DATA_user)
+ req->have_cache = true;
- if (bch2_err_matches(ret, BCH_ERR_transaction_restart) ||
- bch2_err_matches(ret, BCH_ERR_operation_blocked) ||
- bch2_err_matches(ret, BCH_ERR_open_buckets_empty))
- 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 (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;
}
-
- if (ret == -BCH_ERR_insufficient_devices &&
- req->nr_effective >= nr_replicas_required)
- ret = 0;
- else
- goto err;
}
- if (req->nr_effective < req->nr_replicas && req->ec) {
- req->ec = false;
- req->will_retry_target_devices = true;
- req->will_retry_all_devices = true;
- continue;
- }
+ /*
+ * Only try to allocate cache (durability = 0 devices) from the
+ * specified target:
+ */
+ req->have_cache = true;
+ req->target = 0;
- BUG_ON(req->nr_effective < nr_replicas_required);
- BUG_ON(ret < 0);
- break;
+ 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);
diff --git a/fs/bcachefs/alloc_foreground.h b/fs/bcachefs/alloc_foreground.h
index 90eb8604a0a2..1b3fc8460096 100644
--- a/fs/bcachefs/alloc_foreground.h
+++ b/fs/bcachefs/alloc_foreground.h
@@ -26,12 +26,9 @@ struct dev_alloc_list {
};
struct alloc_request {
- struct closure *cl;
unsigned nr_replicas;
unsigned target;
- bool ec:1;
- bool will_retry_target_devices:1;
- bool will_retry_all_devices:1;
+ bool ec;
enum bch_watermark watermark;
enum bch_write_flags flags;
enum bch_data_type data_type;
@@ -227,7 +224,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 dev_stripe_state *, struct closure *);
int bch2_alloc_sectors_start_trans(struct btree_trans *,
unsigned, unsigned,
diff --git a/fs/bcachefs/backpointers.c b/fs/bcachefs/backpointers.c
index 77d93beb3c8f..bc277f42cf5f 100644
--- a/fs/bcachefs/backpointers.c
+++ b/fs/bcachefs/backpointers.c
@@ -144,7 +144,8 @@ static noinline int backpointer_mod_err(struct btree_trans *trans,
if (!will_check && __bch2_inconsistent_error(c, &buf))
ret = bch_err_throw(c, erofs_unfixed_errors);
- bch_err(c, "%s", buf.buf);
+ if (buf.buf)
+ bch_err(c, "%s", buf.buf);
printbuf_exit(&buf);
return ret;
}
diff --git a/fs/bcachefs/btree_io.c b/fs/bcachefs/btree_io.c
index b30799e494eb..8924dae15d41 100644
--- a/fs/bcachefs/btree_io.c
+++ b/fs/bcachefs/btree_io.c
@@ -24,6 +24,7 @@
#include "super-io.h"
#include "trace.h"
+#include <linux/moduleparam.h>
#include <linux/sched/mm.h>
#ifdef CONFIG_BCACHEFS_DEBUG
diff --git a/fs/bcachefs/btree_trans_commit.c b/fs/bcachefs/btree_trans_commit.c
index 7fcf248a9a76..a7e9d8916848 100644
--- a/fs/bcachefs/btree_trans_commit.c
+++ b/fs/bcachefs/btree_trans_commit.c
@@ -1008,7 +1008,7 @@ do_bch2_trans_commit_to_journal_replay(struct btree_trans *trans)
return 0;
}
-int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
+int __bch2_trans_commit(struct btree_trans *trans, enum bch_trans_commit_flags flags)
{
struct btree_insert_entry *errored_at = NULL;
struct bch_fs *c = trans->c;
diff --git a/fs/bcachefs/btree_update.c b/fs/bcachefs/btree_update.c
index 5d9e02370aff..7983c4940b3b 100644
--- a/fs/bcachefs/btree_update.c
+++ b/fs/bcachefs/btree_update.c
@@ -661,21 +661,22 @@ int bch2_btree_insert_trans(struct btree_trans *trans, enum btree_id id,
* @k: key to insert
* @disk_res: must be non-NULL whenever inserting or potentially
* splitting data extents
- * @flags: transaction commit flags
+ * @commit_flags: transaction commit flags
* @iter_flags: btree iter update trigger flags
*
* Returns: 0 on success, error code on failure
*/
int bch2_btree_insert(struct bch_fs *c, enum btree_id id, struct bkey_i *k,
- struct disk_reservation *disk_res, int flags,
+ struct disk_reservation *disk_res,
+ enum bch_trans_commit_flags commit_flags,
enum btree_iter_update_trigger_flags iter_flags)
{
- return bch2_trans_commit_do(c, disk_res, NULL, flags,
+ return bch2_trans_commit_do(c, disk_res, NULL, commit_flags,
bch2_btree_insert_trans(trans, id, k, iter_flags));
}
-int bch2_btree_delete_at(struct btree_trans *trans,
- struct btree_iter *iter, unsigned update_flags)
+int bch2_btree_delete_at(struct btree_trans *trans, struct btree_iter *iter,
+ enum btree_iter_update_trigger_flags flags)
{
struct bkey_i *k = bch2_trans_kmalloc(trans, sizeof(*k));
int ret = PTR_ERR_OR_ZERO(k);
@@ -684,12 +685,12 @@ int bch2_btree_delete_at(struct btree_trans *trans,
bkey_init(&k->k);
k->k.p = iter->pos;
- return bch2_trans_update(trans, iter, k, update_flags);
+ return bch2_trans_update(trans, iter, k, flags);
}
int bch2_btree_delete(struct btree_trans *trans,
enum btree_id btree, struct bpos pos,
- unsigned update_flags)
+ enum btree_iter_update_trigger_flags flags)
{
struct btree_iter iter;
int ret;
@@ -698,7 +699,7 @@ int bch2_btree_delete(struct btree_trans *trans,
BTREE_ITER_cached|
BTREE_ITER_intent);
ret = bch2_btree_iter_traverse(trans, &iter) ?:
- bch2_btree_delete_at(trans, &iter, update_flags);
+ bch2_btree_delete_at(trans, &iter, flags);
bch2_trans_iter_exit(trans, &iter);
return ret;
@@ -706,7 +707,7 @@ int bch2_btree_delete(struct btree_trans *trans,
int bch2_btree_delete_range_trans(struct btree_trans *trans, enum btree_id id,
struct bpos start, struct bpos end,
- unsigned update_flags,
+ enum btree_iter_update_trigger_flags flags,
u64 *journal_seq)
{
u32 restart_count = trans->restart_count;
@@ -714,7 +715,7 @@ int bch2_btree_delete_range_trans(struct btree_trans *trans, enum btree_id id,
struct bkey_s_c k;
int ret = 0;
- bch2_trans_iter_init(trans, &iter, id, start, BTREE_ITER_intent);
+ bch2_trans_iter_init(trans, &iter, id, start, BTREE_ITER_intent|flags);
while ((k = bch2_btree_iter_peek_max(trans, &iter, end)).k) {
struct disk_reservation disk_res =
bch2_disk_reservation_init(trans->c, 0);
@@ -747,7 +748,7 @@ int bch2_btree_delete_range_trans(struct btree_trans *trans, enum btree_id id,
bpos_min(end, k.k->p).offset -
iter.pos.offset);
- ret = bch2_trans_update(trans, &iter, &delete, update_flags) ?:
+ ret = bch2_trans_update(trans, &iter, &delete, flags) ?:
bch2_trans_commit(trans, &disk_res, journal_seq,
BCH_TRANS_COMMIT_no_enospc);
bch2_disk_reservation_put(trans->c, &disk_res);
@@ -777,12 +778,12 @@ err:
*/
int bch2_btree_delete_range(struct bch_fs *c, enum btree_id id,
struct bpos start, struct bpos end,
- unsigned update_flags,
+ enum btree_iter_update_trigger_flags flags,
u64 *journal_seq)
{
int ret = bch2_trans_run(c,
bch2_btree_delete_range_trans(trans, id, start, end,
- update_flags, journal_seq));
+ flags, journal_seq));
if (ret == -BCH_ERR_transaction_restart_nested)
ret = 0;
return ret;
diff --git a/fs/bcachefs/btree_update.h b/fs/bcachefs/btree_update.h
index 2c6f9b44d888..222a9f8ffbd5 100644
--- a/fs/bcachefs/btree_update.h
+++ b/fs/bcachefs/btree_update.h
@@ -47,22 +47,27 @@ enum bch_trans_commit_flags {
void bch2_trans_commit_flags_to_text(struct printbuf *, enum bch_trans_commit_flags);
-int bch2_btree_delete_at(struct btree_trans *, struct btree_iter *, unsigned);
-int bch2_btree_delete(struct btree_trans *, enum btree_id, struct bpos, unsigned);
+int bch2_btree_delete_at(struct btree_trans *, struct btree_iter *,
+ enum btree_iter_update_trigger_flags);
+int bch2_btree_delete(struct btree_trans *, enum btree_id, struct bpos,
+ enum btree_iter_update_trigger_flags);
int bch2_btree_insert_nonextent(struct btree_trans *, enum btree_id,
struct bkey_i *, enum btree_iter_update_trigger_flags);
int bch2_btree_insert_trans(struct btree_trans *, enum btree_id, struct bkey_i *,
enum btree_iter_update_trigger_flags);
-int bch2_btree_insert(struct bch_fs *, enum btree_id, struct bkey_i *, struct
- disk_reservation *, int flags, enum
- btree_iter_update_trigger_flags iter_flags);
+int bch2_btree_insert(struct bch_fs *, enum btree_id, struct bkey_i *,
+ struct disk_reservation *,
+ enum bch_trans_commit_flags,
+ enum btree_iter_update_trigger_flags);
int bch2_btree_delete_range_trans(struct btree_trans *, enum btree_id,
- struct bpos, struct bpos, unsigned, u64 *);
+ struct bpos, struct bpos,
+ enum btree_iter_update_trigger_flags, u64 *);
int bch2_btree_delete_range(struct bch_fs *, enum btree_id,
- struct bpos, struct bpos, unsigned, u64 *);
+ struct bpos, struct bpos,
+ enum btree_iter_update_trigger_flags, u64 *);
int bch2_btree_bit_mod_iter(struct btree_trans *, struct btree_iter *, bool);
int bch2_btree_bit_mod(struct btree_trans *, enum btree_id, struct bpos, bool);
@@ -226,7 +231,7 @@ static inline int __must_check bch2_trans_update_buffered(struct btree_trans *tr
void bch2_trans_commit_hook(struct btree_trans *,
struct btree_trans_commit_hook *);
-int __bch2_trans_commit(struct btree_trans *, unsigned);
+int __bch2_trans_commit(struct btree_trans *, enum bch_trans_commit_flags);
int bch2_trans_log_str(struct btree_trans *, const char *);
int bch2_trans_log_msg(struct btree_trans *, struct printbuf *);
diff --git a/fs/bcachefs/data_update.c b/fs/bcachefs/data_update.c
index e848e210a9bf..3968f3be7f3b 100644
--- a/fs/bcachefs/data_update.c
+++ b/fs/bcachefs/data_update.c
@@ -783,6 +783,9 @@ 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;
@@ -794,7 +797,11 @@ 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);
- if (!dev_buckets_free(ca, usage, m->op.watermark))
+ u64 nr_free = dev_buckets_free(ca, usage, m->op.watermark);
+
+ prt_printf(&buf, "%s=%llu ", ca->name, nr_free);
+
+ if (!nr_free)
continue;
nr_replicas += ca->mi.durability;
@@ -802,8 +809,10 @@ static int can_write_extent(struct bch_fs *c, struct data_update *m)
break;
}
- if (!nr_replicas)
+ if (!nr_replicas) {
+ trace_data_update_done_no_rw_devs(c, buf.buf);
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 71956ee86a9c..687c3ba98095 100644
--- a/fs/bcachefs/ec.c
+++ b/fs/bcachefs/ec.c
@@ -1720,7 +1720,8 @@ 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 ec_stripe_head *h, struct ec_stripe_new *s,
+ struct closure *cl)
{
struct bch_fs *c = trans->c;
struct open_bucket *ob;
@@ -1770,7 +1771,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);
+ ret = bch2_bucket_alloc_set_trans(trans, req, &h->parity_stripe, cl);
open_bucket_for_each(c, &req->ptrs, ob, i) {
j = find_next_zero_bit(s->blocks_gotten,
@@ -1793,7 +1794,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);
+ ret = bch2_bucket_alloc_set_trans(trans, req, &h->block_stripe, cl);
open_bucket_for_each(c, &req->ptrs, ob, i) {
j = find_next_zero_bit(s->blocks_gotten,
@@ -1925,7 +1926,7 @@ static int __bch2_ec_stripe_head_reuse(struct btree_trans *trans, struct ec_stri
}
bch2_trans_iter_exit(trans, &lru_iter);
if (!ret)
- return bch_err_throw(c, stripe_alloc_blocked);
+ ret = bch_err_throw(c, stripe_alloc_blocked);
if (ret == 1)
ret = 0;
if (ret)
@@ -1997,7 +1998,8 @@ err:
struct ec_stripe_head *bch2_ec_stripe_head_get(struct btree_trans *trans,
struct alloc_request *req,
- unsigned algo)
+ unsigned algo,
+ struct closure *cl)
{
struct bch_fs *c = trans->c;
unsigned redundancy = req->nr_replicas - 1;
@@ -2039,18 +2041,12 @@ 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;
- 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) ?:
+ swap(req->watermark, saved_watermark);
+ ret = new_stripe_alloc_buckets(trans, req, h, s, NULL) ?:
__bch2_ec_stripe_head_reserve(trans, h, s);
-
- swap(req->watermark, saved_watermark);
- swap(req->flags, saved_flags);
+ swap(req->watermark, saved_watermark);
if (!ret)
goto allocate_buf;
@@ -2066,25 +2062,19 @@ 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 ||
- (req->flags & BCH_WRITE_alloc_nowait) ||
- ret != -BCH_ERR_stripe_alloc_blocked)
+ if (waiting || !cl || ret != -BCH_ERR_stripe_alloc_blocked)
goto err;
if (req->watermark == BCH_WATERMARK_copygc) {
- /* Don't self-deadlock copygc */
- swap(req->flags, saved_flags);
- ret = new_stripe_alloc_buckets(trans, req, h, s) ?:
+ ret = new_stripe_alloc_buckets(trans, req, h, s, NULL) ?:
__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, req->cl);
+ closure_wait(&c->freelist_wait, cl);
waiting = true;
}
@@ -2095,7 +2085,7 @@ alloc_existing:
* Retry allocating buckets, with the watermark for this
* particular write:
*/
- ret = new_stripe_alloc_buckets(trans, req, h, s);
+ ret = new_stripe_alloc_buckets(trans, req, h, s, cl);
if (ret)
goto err;
diff --git a/fs/bcachefs/ec.h b/fs/bcachefs/ec.h
index 756f14bd7bb7..548048adf0d5 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 alloc_request *, unsigned, struct closure *);
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 defa06eb3466..2de0dc91a69e 100644
--- a/fs/bcachefs/errcode.h
+++ b/fs/bcachefs/errcode.h
@@ -130,7 +130,7 @@
x(EEXIST, EEXIST_str_hash_set) \
x(EEXIST, EEXIST_discard_in_flight_add) \
x(EEXIST, EEXIST_subvolume_create) \
- x(EAGAIN, open_buckets_empty) \
+ x(ENOSPC, open_buckets_empty) \
x(ENOSPC, freelist_empty) \
x(BCH_ERR_freelist_empty, no_buckets_found) \
x(0, transaction_restart) \
@@ -236,9 +236,6 @@
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) \
@@ -247,6 +244,8 @@
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/fs.c b/fs/bcachefs/fs.c
index 3b0783f117ae..7fd2551372d7 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -1010,6 +1010,10 @@ err_tx_restart:
goto err;
}
+ BUG_ON(src_inode->ei_inum.inum != src_inode_u.bi_inum);
+ BUG_ON(dst_inode &&
+ dst_inode->ei_inum.inum != dst_inode_u.bi_inum);
+
BUG_ON(src_inode->v.i_ino != src_inode_u.bi_inum);
BUG_ON(dst_inode &&
dst_inode->v.i_ino != dst_inode_u.bi_inum);
diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c
index 88b1eec8eff3..fa077341d2ef 100644
--- a/fs/bcachefs/io_write.c
+++ b/fs/bcachefs/io_write.c
@@ -32,6 +32,7 @@
#include "trace.h"
#include <linux/blkdev.h>
+#include <linux/moduleparam.h>
#include <linux/prefetch.h>
#include <linux/random.h>
#include <linux/sched/mm.h>
diff --git a/fs/bcachefs/journal.c b/fs/bcachefs/journal.c
index 436a86b8ecc3..f22b05e02c1e 100644
--- a/fs/bcachefs/journal.c
+++ b/fs/bcachefs/journal.c
@@ -1278,7 +1278,8 @@ static int bch2_set_nr_journal_buckets_loop(struct bch_fs *c, struct bch_dev *ca
ret = bch2_set_nr_journal_buckets_iter(ca, nr, new_fs, &cl);
- if (bch2_err_matches(ret, BCH_ERR_operation_blocked))
+ if (ret == -BCH_ERR_bucket_alloc_blocked ||
+ ret == -BCH_ERR_open_buckets_empty)
ret = 0; /* wait and retry */
bch2_disk_reservation_put(c, &disk_res);
diff --git a/fs/bcachefs/recovery_passes.c b/fs/bcachefs/recovery_passes.c
index c09ed2dd4639..6a039e011064 100644
--- a/fs/bcachefs/recovery_passes.c
+++ b/fs/bcachefs/recovery_passes.c
@@ -360,7 +360,7 @@ int __bch2_run_explicit_recovery_pass(struct bch_fs *c,
!(r->passes_complete & BIT_ULL(pass));
bool ratelimit = flags & RUN_RECOVERY_PASS_ratelimit;
- if (!(in_recovery && (flags & RUN_RECOVERY_PASS_nopersistent))) {
+ if (!(flags & RUN_RECOVERY_PASS_nopersistent)) {
struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
__set_bit_le64(bch2_recovery_pass_to_stable(pass), ext->recovery_passes_required);
}
diff --git a/fs/bcachefs/sb-members.c b/fs/bcachefs/sb-members.c
index f2abe92ca130..340d4fb7f9b6 100644
--- a/fs/bcachefs/sb-members.c
+++ b/fs/bcachefs/sb-members.c
@@ -20,6 +20,7 @@ int bch2_dev_missing_bkey(struct bch_fs *c, struct bkey_s_c k, unsigned dev)
prt_printf(&buf, "pointer to %s device %u in key\n",
removed ? "removed" : "nonexistent", dev);
bch2_bkey_val_to_text(&buf, c, k);
+ prt_newline(&buf);
bool print = removed
? bch2_count_fsck_err(c, ptr_to_removed_device, &buf)
diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index 6980cd5b0ca8..a3438b0dc0a9 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -1974,11 +1974,15 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
ca->disk_sb.sb->dev_idx = dev_idx;
bch2_dev_attach(c, ca, dev_idx);
+ set_bit(ca->dev_idx, c->online_devs.d);
+
if (BCH_MEMBER_GROUP(&dev_mi)) {
ret = __bch2_dev_group_set(c, ca, label.buf);
bch_err_msg(c, ret, "creating new label");
- if (ret)
- goto err_unlock;
+ if (ret) {
+ mutex_unlock(&c->sb_lock);
+ goto err_late;
+ }
}
bch2_write_super(c);
@@ -2526,6 +2530,8 @@ static int bch2_param_get_static_key_t(char *buffer, const struct kernel_param *
return sprintf(buffer, "%c\n", static_key_enabled(key) ? 'N' : 'Y');
}
+/* this is unused in userspace - silence the warning */
+__maybe_unused
static const struct kernel_param_ops bch2_param_ops_static_key_t = {
.flags = KERNEL_PARAM_OPS_FL_NOARG,
.set = bch2_param_set_static_key_t,
diff --git a/fs/bcachefs/trace.h b/fs/bcachefs/trace.h
index b5dae1145afa..9324ef32903d 100644
--- a/fs/bcachefs/trace.h
+++ b/fs/bcachefs/trace.h
@@ -1330,6 +1330,11 @@ 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)