mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'bcachefs-2025-06-04' of git://evilpiepirate.org/bcachefs
Pull more bcachefs updates from Kent Overstreet:
"More bcachefs updates:
- More stack usage improvements (~600 bytes)
- Define CLASS()es for some commonly used types, and convert most
rcu_read_lock() uses to the new lock guards
- New introspection:
- Superblock error counters are now available in sysfs:
previously, they were only visible with 'show-super', which
doesn't provide a live view
- New tracepoint, error_throw(), which is called any time we
return an error and start to unwind
- Repair
- check_fix_ptrs() can now repair btree node roots
- We can now repair when we've somehow ended up with the journal
using a superblock bucket
- Revert some leftovers from the aborted directory i_size feature,
and add repair code: some userspace programs (e.g. sshfs) were
getting confused
It seems in 6.15 there's a bug where i_nlink on the vfs inode has been
getting incorrectly set to 0, with some unfortunate results;
list_journal analysis showed bch2_inode_rm() being called (by
bch2_evict_inode()) when it clearly should not have been.
- bch2_inode_rm() now runs "should we be deleting this inode?" checks
that were previously only run when deleting unlinked inodes in
recovery
- check_subvol() was treating a dangling subvol (pointing to a
missing root inode) like a dangling dirent, and deleting it. This
was the really unfortunate one: check_subvol() will now recreate
the root inode if necessary
This took longer to debug than it should have, and we lost several
filesystems unnecessarily, because users have been ignoring the
release notes and blindly running 'fsck -y'. Debugging required
reconstructing what happened through analyzing the journal, when
ideally someone would have noticed 'hey, fsck is asking me if I want
to repair this: it usually doesn't, maybe I should run this in dry run
mode and check what's going on?'
As a reminder, fsck errors are being marked as autofix once we've
verified, in real world usage, that they're working correctly; blindly
running 'fsck -y' on an experimental filesystem is playing with fire
Up to this incident we've had an excellent track record of not losing
data, so let's try to learn from this one
This is a community effort, I wouldn't be able to get this done
without the help of all the people QAing and providing excellent bug
reports and feedback based on real world usage. But please don't
ignore advice and expect me to pick up the pieces
If an error isn't marked as autofix, and it /is/ happening in the
wild, that's also something I need to know about so we can check it
out and add it to the autofix list if repair looks good. I haven't
been getting those reports, and I should be; since we don't have any
sort of telemetry yet I am absolutely dependent on user reports
Now I'll be spending the weekend working on new repair code to see if
I can get a filesystem back for a user who didn't have backups"
* tag 'bcachefs-2025-06-04' of git://evilpiepirate.org/bcachefs: (69 commits)
bcachefs: add cond_resched() to handle_overwrites()
bcachefs: Make journal read log message a bit quieter
bcachefs: Fix subvol to missing root repair
bcachefs: Run may_delete_deleted_inode() checks in bch2_inode_rm()
bcachefs: delete dead code from may_delete_deleted_inode()
bcachefs: Add flags to subvolume_to_text()
bcachefs: Fix oops in btree_node_seq_matches()
bcachefs: Fix dirent_casefold_mismatch repair
bcachefs: Fix bch2_fsck_rename_dirent() for casefold
bcachefs: Redo bch2_dirent_init_name()
bcachefs: Fix -Wc23-extensions in bch2_check_dirents()
bcachefs: Run check_dirents second time if required
bcachefs: Run snapshot deletion out of system_long_wq
bcachefs: Make check_key_has_snapshot safer
bcachefs: BCH_RECOVERY_PASS_NO_RATELIMIT
bcachefs: bch2_require_recovery_pass()
bcachefs: bch_err_throw()
bcachefs: Repair code for directory i_size
bcachefs: Kill un-reverted directory i_size code
bcachefs: Delete redundant fsck_err()
...
This commit is contained in:
commit
ff0905bbf9
98 files changed
+2334
-1768
No files matched your search
@@ -21,7 +21,6 @@
|
||||
#include "error.h"
|
||||
#include "lru.h"
|
||||
#include "recovery.h"
|
||||
#include "trace.h"
|
||||
#include "varint.h"
|
||||
|
||||
#include <linux/kthread.h>
|
||||
@@ -337,11 +336,10 @@ void bch2_alloc_v4_swab(struct bkey_s k)
|
||||
a->stripe_sectors = swab32(a->stripe_sectors);
|
||||
}
|
||||
|
||||
void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k)
|
||||
static inline void __bch2_alloc_v4_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
unsigned dev, const struct bch_alloc_v4 *a)
|
||||
{
|
||||
struct bch_alloc_v4 _a;
|
||||
const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k, &_a);
|
||||
struct bch_dev *ca = c ? bch2_dev_bucket_tryget_noerror(c, k.k->p) : NULL;
|
||||
struct bch_dev *ca = c ? bch2_dev_tryget_noerror(c, dev) : NULL;
|
||||
|
||||
prt_newline(out);
|
||||
printbuf_indent_add(out, 2);
|
||||
@@ -369,6 +367,19 @@ void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c
|
||||
bch2_dev_put(ca);
|
||||
}
|
||||
|
||||
void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k)
|
||||
{
|
||||
struct bch_alloc_v4 _a;
|
||||
const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k, &_a);
|
||||
|
||||
__bch2_alloc_v4_to_text(out, c, k.k->p.inode, a);
|
||||
}
|
||||
|
||||
void bch2_alloc_v4_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k)
|
||||
{
|
||||
__bch2_alloc_v4_to_text(out, c, k.k->p.inode, bkey_s_c_to_alloc_v4(k).v);
|
||||
}
|
||||
|
||||
void __bch2_alloc_to_v4(struct bkey_s_c k, struct bch_alloc_v4 *out)
|
||||
{
|
||||
if (k.k->type == KEY_TYPE_alloc_v4) {
|
||||
@@ -697,8 +708,8 @@ static int __need_discard_or_freespace_err(struct btree_trans *trans,
|
||||
set ? "" : "un",
|
||||
bch2_btree_id_str(btree),
|
||||
buf.buf);
|
||||
if (ret == -BCH_ERR_fsck_ignore ||
|
||||
ret == -BCH_ERR_fsck_errors_not_fixed)
|
||||
if (bch2_err_matches(ret, BCH_ERR_fsck_ignore) ||
|
||||
bch2_err_matches(ret, BCH_ERR_fsck_errors_not_fixed))
|
||||
ret = 0;
|
||||
|
||||
printbuf_exit(&buf);
|
||||
@@ -854,7 +865,7 @@ int bch2_trigger_alloc(struct btree_trans *trans,
|
||||
|
||||
struct bch_dev *ca = bch2_dev_bucket_tryget(c, new.k->p);
|
||||
if (!ca)
|
||||
return -BCH_ERR_trigger_alloc;
|
||||
return bch_err_throw(c, trigger_alloc);
|
||||
|
||||
struct bch_alloc_v4 old_a_convert;
|
||||
const struct bch_alloc_v4 *old_a = bch2_alloc_to_v4(old, &old_a_convert);
|
||||
@@ -988,14 +999,11 @@ int bch2_trigger_alloc(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (new_a->gen != old_a->gen) {
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
u8 *gen = bucket_gen(ca, new.k->p.offset);
|
||||
if (unlikely(!gen)) {
|
||||
rcu_read_unlock();
|
||||
if (unlikely(!gen))
|
||||
goto invalid_bucket;
|
||||
}
|
||||
*gen = new_a->gen;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
#define eval_state(_a, expr) ({ const struct bch_alloc_v4 *a = _a; expr; })
|
||||
@@ -1021,15 +1029,12 @@ int bch2_trigger_alloc(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if ((flags & BTREE_TRIGGER_gc) && (flags & BTREE_TRIGGER_insert)) {
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
struct bucket *g = gc_bucket(ca, new.k->p.offset);
|
||||
if (unlikely(!g)) {
|
||||
rcu_read_unlock();
|
||||
if (unlikely(!g))
|
||||
goto invalid_bucket;
|
||||
}
|
||||
g->gen_valid = 1;
|
||||
g->gen = new_a->gen;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
err:
|
||||
fsck_err:
|
||||
@@ -1039,7 +1044,7 @@ fsck_err:
|
||||
invalid_bucket:
|
||||
bch2_fs_inconsistent(c, "reference to invalid bucket\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, new.s_c), buf.buf));
|
||||
ret = -BCH_ERR_trigger_alloc;
|
||||
ret = bch_err_throw(c, trigger_alloc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -1105,13 +1110,12 @@ static bool next_bucket(struct bch_fs *c, struct bch_dev **ca, struct bpos *buck
|
||||
bucket->offset = 0;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
*ca = __bch2_next_dev_idx(c, bucket->inode, NULL);
|
||||
if (*ca) {
|
||||
*bucket = POS((*ca)->dev_idx, (*ca)->mi.first_bucket);
|
||||
bch2_dev_get(*ca);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return *ca != NULL;
|
||||
}
|
||||
@@ -1454,7 +1458,7 @@ delete:
|
||||
ret = bch2_btree_bit_mod_iter(trans, iter, false) ?:
|
||||
bch2_trans_commit(trans, NULL, NULL,
|
||||
BCH_TRANS_COMMIT_no_enospc) ?:
|
||||
-BCH_ERR_transaction_restart_commit;
|
||||
bch_err_throw(c, transaction_restart_commit);
|
||||
goto out;
|
||||
} else {
|
||||
/*
|
||||
@@ -1777,14 +1781,16 @@ int bch2_check_alloc_to_lru_refs(struct bch_fs *c)
|
||||
|
||||
static int discard_in_flight_add(struct bch_dev *ca, u64 bucket, bool in_progress)
|
||||
{
|
||||
struct bch_fs *c = ca->fs;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ca->discard_buckets_in_flight_lock);
|
||||
darray_for_each(ca->discard_buckets_in_flight, i)
|
||||
if (i->bucket == bucket) {
|
||||
ret = -BCH_ERR_EEXIST_discard_in_flight_add;
|
||||
goto out;
|
||||
}
|
||||
struct discard_in_flight *i =
|
||||
darray_find_p(ca->discard_buckets_in_flight, i, i->bucket == bucket);
|
||||
if (i) {
|
||||
ret = bch_err_throw(c, EEXIST_discard_in_flight_add);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = darray_push(&ca->discard_buckets_in_flight, ((struct discard_in_flight) {
|
||||
.in_progress = in_progress,
|
||||
@@ -1798,14 +1804,11 @@ out:
|
||||
static void discard_in_flight_remove(struct bch_dev *ca, u64 bucket)
|
||||
{
|
||||
mutex_lock(&ca->discard_buckets_in_flight_lock);
|
||||
darray_for_each(ca->discard_buckets_in_flight, i)
|
||||
if (i->bucket == bucket) {
|
||||
BUG_ON(!i->in_progress);
|
||||
darray_remove_item(&ca->discard_buckets_in_flight, i);
|
||||
goto found;
|
||||
}
|
||||
BUG();
|
||||
found:
|
||||
struct discard_in_flight *i =
|
||||
darray_find_p(ca->discard_buckets_in_flight, i, i->bucket == bucket);
|
||||
BUG_ON(!i || !i->in_progress);
|
||||
|
||||
darray_remove_item(&ca->discard_buckets_in_flight, i);
|
||||
mutex_unlock(&ca->discard_buckets_in_flight_lock);
|
||||
}
|
||||
|
||||
@@ -2504,7 +2507,7 @@ void bch2_recalc_capacity(struct bch_fs *c)
|
||||
|
||||
lockdep_assert_held(&c->state_lock);
|
||||
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
for_each_member_device_rcu(c, ca, NULL) {
|
||||
struct block_device *bdev = READ_ONCE(ca->disk_sb.bdev);
|
||||
if (bdev)
|
||||
@@ -2549,7 +2552,6 @@ void bch2_recalc_capacity(struct bch_fs *c)
|
||||
bucket_size_max = max_t(unsigned, bucket_size_max,
|
||||
ca->mi.bucket_size);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
bch2_set_ra_pages(c, ra_pages);
|
||||
|
||||
@@ -2574,10 +2576,9 @@ u64 bch2_min_rw_member_capacity(struct bch_fs *c)
|
||||
{
|
||||
u64 ret = U64_MAX;
|
||||
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
for_each_rw_member_rcu(c, ca)
|
||||
ret = min(ret, ca->mi.nbuckets * ca->mi.bucket_size);
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,9 @@
|
||||
|
||||
static inline bool bch2_dev_bucket_exists(struct bch_fs *c, struct bpos pos)
|
||||
{
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
struct bch_dev *ca = bch2_dev_rcu_noerror(c, pos.inode);
|
||||
bool ret = ca && bucket_valid(ca, pos.offset);
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
return ca && bucket_valid(ca, pos.offset);
|
||||
}
|
||||
|
||||
static inline u64 bucket_to_u64(struct bpos bucket)
|
||||
@@ -253,6 +251,7 @@ int bch2_alloc_v4_validate(struct bch_fs *, struct bkey_s_c,
|
||||
struct bkey_validate_context);
|
||||
void bch2_alloc_v4_swab(struct bkey_s);
|
||||
void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
void bch2_alloc_v4_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
#define bch2_bkey_ops_alloc ((struct bkey_ops) { \
|
||||
.key_validate = bch2_alloc_v1_validate, \
|
||||
@@ -277,7 +276,7 @@ void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
#define bch2_bkey_ops_alloc_v4 ((struct bkey_ops) { \
|
||||
.key_validate = bch2_alloc_v4_validate, \
|
||||
.val_to_text = bch2_alloc_to_text, \
|
||||
.val_to_text = bch2_alloc_v4_to_text, \
|
||||
.swab = bch2_alloc_v4_swab, \
|
||||
.trigger = bch2_trigger_alloc, \
|
||||
.min_val_size = 48, \
|
||||
|
||||
@@ -69,10 +69,9 @@ const char * const bch2_watermarks[] = {
|
||||
|
||||
void bch2_reset_alloc_cursors(struct bch_fs *c)
|
||||
{
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
for_each_member_device_rcu(c, ca, NULL)
|
||||
memset(ca->alloc_cursor, 0, sizeof(ca->alloc_cursor));
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void bch2_open_bucket_hash_add(struct bch_fs *c, struct open_bucket *ob)
|
||||
@@ -166,9 +165,8 @@ static void open_bucket_free_unused(struct bch_fs *c, struct open_bucket *ob)
|
||||
ARRAY_SIZE(c->open_buckets_partial));
|
||||
|
||||
spin_lock(&c->freelist_lock);
|
||||
rcu_read_lock();
|
||||
bch2_dev_rcu(c, ob->dev)->nr_partial_buckets++;
|
||||
rcu_read_unlock();
|
||||
scoped_guard(rcu)
|
||||
bch2_dev_rcu(c, ob->dev)->nr_partial_buckets++;
|
||||
|
||||
ob->on_partial_list = true;
|
||||
c->open_buckets_partial[c->open_buckets_partial_nr++] =
|
||||
@@ -229,7 +227,7 @@ static struct open_bucket *__try_alloc_bucket(struct bch_fs *c,
|
||||
|
||||
track_event_change(&c->times[BCH_TIME_blocked_allocate_open_bucket], true);
|
||||
spin_unlock(&c->freelist_lock);
|
||||
return ERR_PTR(-BCH_ERR_open_buckets_empty);
|
||||
return ERR_PTR(bch_err_throw(c, open_buckets_empty));
|
||||
}
|
||||
|
||||
/* Recheck under lock: */
|
||||
@@ -535,7 +533,7 @@ again:
|
||||
|
||||
track_event_change(&c->times[BCH_TIME_blocked_allocate], true);
|
||||
|
||||
ob = ERR_PTR(-BCH_ERR_freelist_empty);
|
||||
ob = ERR_PTR(bch_err_throw(c, freelist_empty));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -560,7 +558,7 @@ alloc:
|
||||
}
|
||||
err:
|
||||
if (!ob)
|
||||
ob = ERR_PTR(-BCH_ERR_no_buckets_found);
|
||||
ob = ERR_PTR(bch_err_throw(c, no_buckets_found));
|
||||
|
||||
if (!IS_ERR(ob))
|
||||
ob->data_type = req->data_type;
|
||||
@@ -603,18 +601,18 @@ static int __dev_stripe_cmp(struct dev_stripe_state *stripe,
|
||||
|
||||
#define dev_stripe_cmp(l, r) __dev_stripe_cmp(stripe, l, r)
|
||||
|
||||
struct dev_alloc_list bch2_dev_alloc_list(struct bch_fs *c,
|
||||
struct dev_stripe_state *stripe,
|
||||
struct bch_devs_mask *devs)
|
||||
void bch2_dev_alloc_list(struct bch_fs *c,
|
||||
struct dev_stripe_state *stripe,
|
||||
struct bch_devs_mask *devs,
|
||||
struct dev_alloc_list *ret)
|
||||
{
|
||||
struct dev_alloc_list ret = { .nr = 0 };
|
||||
ret->nr = 0;
|
||||
|
||||
unsigned i;
|
||||
|
||||
for_each_set_bit(i, devs->d, BCH_SB_MEMBERS_MAX)
|
||||
ret.data[ret.nr++] = i;
|
||||
ret->data[ret->nr++] = i;
|
||||
|
||||
bubble_sort(ret.data, ret.nr, dev_stripe_cmp);
|
||||
return ret;
|
||||
bubble_sort(ret->data, ret->nr, dev_stripe_cmp);
|
||||
}
|
||||
|
||||
static const u64 stripe_clock_hand_rescale = 1ULL << 62; /* trigger rescale at */
|
||||
@@ -705,18 +703,19 @@ 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,
|
||||
struct closure *cl)
|
||||
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;
|
||||
int ret = -BCH_ERR_insufficient_devices;
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(req->nr_effective >= req->nr_replicas);
|
||||
|
||||
struct dev_alloc_list devs_sorted = bch2_dev_alloc_list(c, stripe, &req->devs_may_alloc);
|
||||
darray_for_each(devs_sorted, i) {
|
||||
bch2_dev_alloc_list(c, stripe, &req->devs_may_alloc, &req->devs_sorted);
|
||||
|
||||
darray_for_each(req->devs_sorted, i) {
|
||||
req->ca = bch2_dev_tryget_noerror(c, *i);
|
||||
if (!req->ca)
|
||||
continue;
|
||||
@@ -739,13 +738,16 @@ int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (add_new_bucket(c, req, ob)) {
|
||||
ret = 0;
|
||||
ret = add_new_bucket(c, req, ob);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
if (ret == 1)
|
||||
return 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
return bch_err_throw(c, insufficient_devices);
|
||||
}
|
||||
|
||||
/* Allocate from stripes: */
|
||||
@@ -776,9 +778,9 @@ static int bucket_alloc_from_stripe(struct btree_trans *trans,
|
||||
if (!h)
|
||||
return 0;
|
||||
|
||||
struct dev_alloc_list devs_sorted =
|
||||
bch2_dev_alloc_list(c, &req->wp->stripe, &req->devs_may_alloc);
|
||||
darray_for_each(devs_sorted, i)
|
||||
bch2_dev_alloc_list(c, &req->wp->stripe, &req->devs_may_alloc, &req->devs_sorted);
|
||||
|
||||
darray_for_each(req->devs_sorted, i)
|
||||
for (unsigned ec_idx = 0; ec_idx < h->s->nr_data; ec_idx++) {
|
||||
if (!h->s->blocks[ec_idx])
|
||||
continue;
|
||||
@@ -872,9 +874,8 @@ static int bucket_alloc_set_partial(struct bch_fs *c,
|
||||
i);
|
||||
ob->on_partial_list = false;
|
||||
|
||||
rcu_read_lock();
|
||||
bch2_dev_rcu(c, ob->dev)->nr_partial_buckets--;
|
||||
rcu_read_unlock();
|
||||
scoped_guard(rcu)
|
||||
bch2_dev_rcu(c, ob->dev)->nr_partial_buckets--;
|
||||
|
||||
ret = add_new_bucket(c, req, ob);
|
||||
if (ret)
|
||||
@@ -1056,9 +1057,8 @@ void bch2_open_buckets_stop(struct bch_fs *c, struct bch_dev *ca,
|
||||
|
||||
ob->on_partial_list = false;
|
||||
|
||||
rcu_read_lock();
|
||||
bch2_dev_rcu(c, ob->dev)->nr_partial_buckets--;
|
||||
rcu_read_unlock();
|
||||
scoped_guard(rcu)
|
||||
bch2_dev_rcu(c, ob->dev)->nr_partial_buckets--;
|
||||
|
||||
spin_unlock(&c->freelist_lock);
|
||||
bch2_open_bucket_put(c, ob);
|
||||
@@ -1086,14 +1086,11 @@ static struct write_point *__writepoint_find(struct hlist_head *head,
|
||||
{
|
||||
struct write_point *wp;
|
||||
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
hlist_for_each_entry_rcu(wp, head, node)
|
||||
if (wp->write_point == write_point)
|
||||
goto out;
|
||||
wp = NULL;
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return wp;
|
||||
return wp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool too_many_writepoints(struct bch_fs *c, unsigned factor)
|
||||
@@ -1104,7 +1101,7 @@ static inline bool too_many_writepoints(struct bch_fs *c, unsigned factor)
|
||||
return stranded * factor > free;
|
||||
}
|
||||
|
||||
static bool try_increase_writepoints(struct bch_fs *c)
|
||||
static noinline bool try_increase_writepoints(struct bch_fs *c)
|
||||
{
|
||||
struct write_point *wp;
|
||||
|
||||
@@ -1117,7 +1114,7 @@ static bool try_increase_writepoints(struct bch_fs *c)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool try_decrease_writepoints(struct btree_trans *trans, unsigned old_nr)
|
||||
static noinline bool try_decrease_writepoints(struct btree_trans *trans, unsigned old_nr)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct write_point *wp;
|
||||
@@ -1379,11 +1376,11 @@ err:
|
||||
goto retry;
|
||||
|
||||
if (cl && bch2_err_matches(ret, BCH_ERR_open_buckets_empty))
|
||||
ret = -BCH_ERR_bucket_alloc_blocked;
|
||||
ret = bch_err_throw(c, bucket_alloc_blocked);
|
||||
|
||||
if (cl && !(flags & BCH_WRITE_alloc_nowait) &&
|
||||
bch2_err_matches(ret, BCH_ERR_freelist_empty))
|
||||
ret = -BCH_ERR_bucket_alloc_blocked;
|
||||
ret = bch_err_throw(c, bucket_alloc_blocked);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1637,19 +1634,16 @@ static noinline void bch2_print_allocator_stuck(struct bch_fs *c)
|
||||
|
||||
bch2_printbuf_make_room(&buf, 4096);
|
||||
|
||||
rcu_read_lock();
|
||||
buf.atomic++;
|
||||
|
||||
for_each_online_member_rcu(c, ca) {
|
||||
prt_printf(&buf, "Dev %u:\n", ca->dev_idx);
|
||||
printbuf_indent_add(&buf, 2);
|
||||
bch2_dev_alloc_debug_to_text(&buf, ca);
|
||||
printbuf_indent_sub(&buf, 2);
|
||||
prt_newline(&buf);
|
||||
}
|
||||
|
||||
scoped_guard(rcu)
|
||||
for_each_online_member_rcu(c, ca) {
|
||||
prt_printf(&buf, "Dev %u:\n", ca->dev_idx);
|
||||
printbuf_indent_add(&buf, 2);
|
||||
bch2_dev_alloc_debug_to_text(&buf, ca);
|
||||
printbuf_indent_sub(&buf, 2);
|
||||
prt_newline(&buf);
|
||||
}
|
||||
--buf.atomic;
|
||||
rcu_read_unlock();
|
||||
|
||||
prt_printf(&buf, "Copygc debug:\n");
|
||||
printbuf_indent_add(&buf, 2);
|
||||
|
||||
@@ -42,6 +42,7 @@ struct alloc_request {
|
||||
struct bch_devs_mask devs_may_alloc;
|
||||
|
||||
/* bch2_bucket_alloc_set_trans(): */
|
||||
struct dev_alloc_list devs_sorted;
|
||||
struct bch_dev_usage usage;
|
||||
|
||||
/* bch2_bucket_alloc_trans(): */
|
||||
@@ -71,9 +72,10 @@ struct alloc_request {
|
||||
struct bch_devs_mask scratch_devs_may_alloc;
|
||||
};
|
||||
|
||||
struct dev_alloc_list bch2_dev_alloc_list(struct bch_fs *,
|
||||
struct dev_stripe_state *,
|
||||
struct bch_devs_mask *);
|
||||
void bch2_dev_alloc_list(struct bch_fs *,
|
||||
struct dev_stripe_state *,
|
||||
struct bch_devs_mask *,
|
||||
struct dev_alloc_list *);
|
||||
void bch2_dev_stripe_increment(struct bch_dev *, struct dev_stripe_state *);
|
||||
|
||||
static inline struct bch_dev *ob_dev(struct bch_fs *c, struct open_bucket *ob)
|
||||
|
||||
+37
-37
@@ -48,18 +48,20 @@ void bch2_backpointer_to_text(struct printbuf *out, struct bch_fs *c, struct bke
|
||||
{
|
||||
struct bkey_s_c_backpointer bp = bkey_s_c_to_backpointer(k);
|
||||
|
||||
rcu_read_lock();
|
||||
struct bch_dev *ca = bch2_dev_rcu_noerror(c, bp.k->p.inode);
|
||||
if (ca) {
|
||||
u32 bucket_offset;
|
||||
struct bpos bucket = bp_pos_to_bucket_and_offset(ca, bp.k->p, &bucket_offset);
|
||||
rcu_read_unlock();
|
||||
prt_printf(out, "bucket=%llu:%llu:%u ", bucket.inode, bucket.offset, bucket_offset);
|
||||
} else {
|
||||
rcu_read_unlock();
|
||||
prt_printf(out, "sector=%llu:%llu ", bp.k->p.inode, bp.k->p.offset >> MAX_EXTENT_COMPRESS_RATIO_SHIFT);
|
||||
struct bch_dev *ca;
|
||||
u32 bucket_offset;
|
||||
struct bpos bucket;
|
||||
scoped_guard(rcu) {
|
||||
ca = bch2_dev_rcu_noerror(c, bp.k->p.inode);
|
||||
if (ca)
|
||||
bucket = bp_pos_to_bucket_and_offset(ca, bp.k->p, &bucket_offset);
|
||||
}
|
||||
|
||||
if (ca)
|
||||
prt_printf(out, "bucket=%llu:%llu:%u ", bucket.inode, bucket.offset, bucket_offset);
|
||||
else
|
||||
prt_printf(out, "sector=%llu:%llu ", bp.k->p.inode, bp.k->p.offset >> MAX_EXTENT_COMPRESS_RATIO_SHIFT);
|
||||
|
||||
bch2_btree_id_level_to_text(out, bp.v->btree_id, bp.v->level);
|
||||
prt_str(out, " data_type=");
|
||||
bch2_prt_data_type(out, bp.v->data_type);
|
||||
@@ -140,7 +142,7 @@ static noinline int backpointer_mod_err(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (!will_check && __bch2_inconsistent_error(c, &buf))
|
||||
ret = -BCH_ERR_erofs_unfixed_errors;
|
||||
ret = bch_err_throw(c, erofs_unfixed_errors);
|
||||
|
||||
bch_err(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
@@ -293,7 +295,7 @@ static struct btree *__bch2_backpointer_get_node(struct btree_trans *trans,
|
||||
return b;
|
||||
|
||||
if (btree_node_will_make_reachable(b)) {
|
||||
b = ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node);
|
||||
b = ERR_PTR(bch_err_throw(c, backpointer_to_overwritten_btree_node));
|
||||
} else {
|
||||
int ret = backpointer_target_not_found(trans, bp, bkey_i_to_s_c(&b->key),
|
||||
last_flushed, commit);
|
||||
@@ -351,7 +353,7 @@ static struct bkey_s_c __bch2_backpointer_get_key(struct btree_trans *trans,
|
||||
return ret ? bkey_s_c_err(ret) : bkey_s_c_null;
|
||||
} else {
|
||||
struct btree *b = __bch2_backpointer_get_node(trans, bp, iter, last_flushed, commit);
|
||||
if (b == ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node))
|
||||
if (b == ERR_PTR(bch_err_throw(c, backpointer_to_overwritten_btree_node)))
|
||||
return bkey_s_c_null;
|
||||
if (IS_ERR_OR_NULL(b))
|
||||
return ((struct bkey_s_c) { .k = ERR_CAST(b) });
|
||||
@@ -591,6 +593,7 @@ check_existing_bp:
|
||||
bkey_for_each_ptr(other_extent_ptrs, ptr)
|
||||
if (ptr->dev == bp->k.p.inode &&
|
||||
dev_ptr_stale_rcu(ca, ptr)) {
|
||||
rcu_read_unlock();
|
||||
ret = drop_dev_and_update(trans, other_bp.v->btree_id,
|
||||
other_extent, bp->k.p.inode);
|
||||
if (ret)
|
||||
@@ -648,7 +651,7 @@ check_existing_bp:
|
||||
prt_newline(&buf);
|
||||
bch2_bkey_val_to_text(&buf, c, other_extent);
|
||||
bch_err(c, "%s", buf.buf);
|
||||
ret = -BCH_ERR_fsck_repair_unimplemented;
|
||||
ret = bch_err_throw(c, fsck_repair_unimplemented);
|
||||
goto err;
|
||||
missing:
|
||||
printbuf_reset(&buf);
|
||||
@@ -679,26 +682,23 @@ static int check_extent_to_backpointers(struct btree_trans *trans,
|
||||
if (p.ptr.dev == BCH_SB_MEMBER_INVALID)
|
||||
continue;
|
||||
|
||||
rcu_read_lock();
|
||||
struct bch_dev *ca = bch2_dev_rcu_noerror(c, p.ptr.dev);
|
||||
if (!ca) {
|
||||
rcu_read_unlock();
|
||||
continue;
|
||||
}
|
||||
bool empty;
|
||||
{
|
||||
/* scoped_guard() is a loop, so it breaks continue */
|
||||
guard(rcu)();
|
||||
struct bch_dev *ca = bch2_dev_rcu_noerror(c, p.ptr.dev);
|
||||
if (!ca)
|
||||
continue;
|
||||
|
||||
if (p.ptr.cached && dev_ptr_stale_rcu(ca, &p.ptr)) {
|
||||
rcu_read_unlock();
|
||||
continue;
|
||||
}
|
||||
if (p.ptr.cached && dev_ptr_stale_rcu(ca, &p.ptr))
|
||||
continue;
|
||||
|
||||
u64 b = PTR_BUCKET_NR(ca, &p.ptr);
|
||||
if (!bch2_bucket_bitmap_test(&ca->bucket_backpointer_mismatch, b)) {
|
||||
rcu_read_unlock();
|
||||
continue;
|
||||
}
|
||||
u64 b = PTR_BUCKET_NR(ca, &p.ptr);
|
||||
if (!bch2_bucket_bitmap_test(&ca->bucket_backpointer_mismatch, b))
|
||||
continue;
|
||||
|
||||
bool empty = bch2_bucket_bitmap_test(&ca->bucket_backpointer_empty, b);
|
||||
rcu_read_unlock();
|
||||
empty = bch2_bucket_bitmap_test(&ca->bucket_backpointer_empty, b);
|
||||
}
|
||||
|
||||
struct bkey_i_backpointer bp;
|
||||
bch2_extent_ptr_to_bp(c, btree, level, k, p, entry, &bp);
|
||||
@@ -953,7 +953,7 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
|
||||
sectors[ALLOC_cached] > a->cached_sectors ||
|
||||
sectors[ALLOC_stripe] > a->stripe_sectors) {
|
||||
ret = check_bucket_backpointers_to_extents(trans, ca, alloc_k.k->p) ?:
|
||||
-BCH_ERR_transaction_restart_nested;
|
||||
bch_err_throw(c, transaction_restart_nested);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -981,7 +981,7 @@ static bool backpointer_node_has_missing(struct bch_fs *c, struct bkey_s_c k)
|
||||
case KEY_TYPE_btree_ptr_v2: {
|
||||
bool ret = false;
|
||||
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
struct bpos pos = bkey_s_c_to_btree_ptr_v2(k).v->min_key;
|
||||
while (pos.inode <= k.k->p.inode) {
|
||||
if (pos.inode >= c->sb.nr_devices)
|
||||
@@ -1009,7 +1009,6 @@ static bool backpointer_node_has_missing(struct bch_fs *c, struct bkey_s_c k)
|
||||
next:
|
||||
pos = SPOS(pos.inode + 1, 0, 0);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1352,7 +1351,7 @@ static int bch2_bucket_bitmap_set(struct bch_dev *ca, struct bucket_bitmap *b, u
|
||||
b->buckets = kvcalloc(BITS_TO_LONGS(ca->mi.nbuckets),
|
||||
sizeof(unsigned long), GFP_KERNEL);
|
||||
if (!b->buckets)
|
||||
return -BCH_ERR_ENOMEM_backpointer_mismatches_bitmap;
|
||||
return bch_err_throw(ca->fs, ENOMEM_backpointer_mismatches_bitmap);
|
||||
}
|
||||
|
||||
b->nr += !__test_and_set_bit(bit, b->buckets);
|
||||
@@ -1361,7 +1360,8 @@ static int bch2_bucket_bitmap_set(struct bch_dev *ca, struct bucket_bitmap *b, u
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bch2_bucket_bitmap_resize(struct bucket_bitmap *b, u64 old_size, u64 new_size)
|
||||
int bch2_bucket_bitmap_resize(struct bch_dev *ca, struct bucket_bitmap *b,
|
||||
u64 old_size, u64 new_size)
|
||||
{
|
||||
scoped_guard(mutex, &b->lock) {
|
||||
if (!b->buckets)
|
||||
@@ -1370,7 +1370,7 @@ int bch2_bucket_bitmap_resize(struct bucket_bitmap *b, u64 old_size, u64 new_siz
|
||||
unsigned long *n = kvcalloc(BITS_TO_LONGS(new_size),
|
||||
sizeof(unsigned long), GFP_KERNEL);
|
||||
if (!n)
|
||||
return -BCH_ERR_ENOMEM_backpointer_mismatches_bitmap;
|
||||
return bch_err_throw(ca->fs, ENOMEM_backpointer_mismatches_bitmap);
|
||||
|
||||
memcpy(n, b->buckets,
|
||||
BITS_TO_LONGS(min(old_size, new_size)) * sizeof(unsigned long));
|
||||
|
||||
@@ -53,11 +53,10 @@ static inline struct bpos bp_pos_to_bucket_and_offset(const struct bch_dev *ca,
|
||||
|
||||
static inline bool bp_pos_to_bucket_nodev_noerror(struct bch_fs *c, struct bpos bp_pos, struct bpos *bucket)
|
||||
{
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
struct bch_dev *ca = bch2_dev_rcu_noerror(c, bp_pos.inode);
|
||||
if (ca)
|
||||
*bucket = bp_pos_to_bucket(ca, bp_pos);
|
||||
rcu_read_unlock();
|
||||
return ca != NULL;
|
||||
}
|
||||
|
||||
@@ -195,7 +194,7 @@ static inline bool bch2_bucket_bitmap_test(struct bucket_bitmap *b, u64 i)
|
||||
return bitmap && test_bit(i, bitmap);
|
||||
}
|
||||
|
||||
int bch2_bucket_bitmap_resize(struct bucket_bitmap *, u64, u64);
|
||||
int bch2_bucket_bitmap_resize(struct bch_dev *, struct bucket_bitmap *, u64, u64);
|
||||
void bch2_bucket_bitmap_free(struct bucket_bitmap *);
|
||||
|
||||
#endif /* _BCACHEFS_BACKPOINTERS_BACKGROUND_H */
|
||||
+41
-31
@@ -183,6 +183,16 @@
|
||||
#define pr_fmt(fmt) "%s() " fmt "\n", __func__
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BCACHEFS_DEBUG
|
||||
#define ENUMERATED_REF_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef dynamic_fault
|
||||
#define dynamic_fault(...) 0
|
||||
#endif
|
||||
|
||||
#define race_fault(...) dynamic_fault("bcachefs:race")
|
||||
|
||||
#include <linux/backing-dev-defs.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/bio.h>
|
||||
@@ -219,15 +229,30 @@
|
||||
#include "time_stats.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef CONFIG_BCACHEFS_DEBUG
|
||||
#define ENUMERATED_REF_DEBUG
|
||||
#endif
|
||||
#include "alloc_types.h"
|
||||
#include "async_objs_types.h"
|
||||
#include "btree_gc_types.h"
|
||||
#include "btree_types.h"
|
||||
#include "btree_node_scan_types.h"
|
||||
#include "btree_write_buffer_types.h"
|
||||
#include "buckets_types.h"
|
||||
#include "buckets_waiting_for_journal_types.h"
|
||||
#include "clock_types.h"
|
||||
#include "disk_groups_types.h"
|
||||
#include "ec_types.h"
|
||||
#include "enumerated_ref_types.h"
|
||||
#include "journal_types.h"
|
||||
#include "keylist_types.h"
|
||||
#include "quota_types.h"
|
||||
#include "rebalance_types.h"
|
||||
#include "recovery_passes_types.h"
|
||||
#include "replicas_types.h"
|
||||
#include "sb-members_types.h"
|
||||
#include "subvolume_types.h"
|
||||
#include "super_types.h"
|
||||
#include "thread_with_file_types.h"
|
||||
|
||||
#ifndef dynamic_fault
|
||||
#define dynamic_fault(...) 0
|
||||
#endif
|
||||
|
||||
#define race_fault(...) dynamic_fault("bcachefs:race")
|
||||
#include "trace.h"
|
||||
|
||||
#define count_event(_c, _name) this_cpu_inc((_c)->counters[BCH_COUNTER_##_name])
|
||||
|
||||
@@ -380,6 +405,14 @@ do { \
|
||||
pr_info(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static inline int __bch2_err_trace(struct bch_fs *c, int err)
|
||||
{
|
||||
trace_error_throw(c, err, _THIS_IP_);
|
||||
return err;
|
||||
}
|
||||
|
||||
#define bch_err_throw(_c, _err) __bch2_err_trace(_c, -BCH_ERR_##_err)
|
||||
|
||||
/* Parameters that are useful for debugging, but should always be compiled in: */
|
||||
#define BCH_DEBUG_PARAMS_ALWAYS() \
|
||||
BCH_DEBUG_PARAM(key_merging_disabled, \
|
||||
@@ -486,29 +519,6 @@ enum bch_time_stats {
|
||||
BCH_TIME_STAT_NR
|
||||
};
|
||||
|
||||
#include "alloc_types.h"
|
||||
#include "async_objs_types.h"
|
||||
#include "btree_gc_types.h"
|
||||
#include "btree_types.h"
|
||||
#include "btree_node_scan_types.h"
|
||||
#include "btree_write_buffer_types.h"
|
||||
#include "buckets_types.h"
|
||||
#include "buckets_waiting_for_journal_types.h"
|
||||
#include "clock_types.h"
|
||||
#include "disk_groups_types.h"
|
||||
#include "ec_types.h"
|
||||
#include "enumerated_ref_types.h"
|
||||
#include "journal_types.h"
|
||||
#include "keylist_types.h"
|
||||
#include "quota_types.h"
|
||||
#include "rebalance_types.h"
|
||||
#include "recovery_passes_types.h"
|
||||
#include "replicas_types.h"
|
||||
#include "sb-members_types.h"
|
||||
#include "subvolume_types.h"
|
||||
#include "super_types.h"
|
||||
#include "thread_with_file_types.h"
|
||||
|
||||
/* Number of nodes btree coalesce will try to coalesce at once */
|
||||
#define GC_MERGE_NODES 4U
|
||||
|
||||
|
||||
+12
-12
@@ -149,7 +149,7 @@ static int btree_node_data_alloc(struct bch_fs *c, struct btree *b, gfp_t gfp)
|
||||
|
||||
b->data = kvmalloc(btree_buf_bytes(b), gfp);
|
||||
if (!b->data)
|
||||
return -BCH_ERR_ENOMEM_btree_node_mem_alloc;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_mem_alloc);
|
||||
#ifdef __KERNEL__
|
||||
b->aux_data = kvmalloc(btree_aux_data_bytes(b), gfp);
|
||||
#else
|
||||
@@ -162,7 +162,7 @@ static int btree_node_data_alloc(struct bch_fs *c, struct btree *b, gfp_t gfp)
|
||||
if (!b->aux_data) {
|
||||
kvfree(b->data);
|
||||
b->data = NULL;
|
||||
return -BCH_ERR_ENOMEM_btree_node_mem_alloc;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_mem_alloc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -353,21 +353,21 @@ static int __btree_node_reclaim_checks(struct bch_fs *c, struct btree *b,
|
||||
|
||||
if (btree_node_noevict(b)) {
|
||||
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_noevict]++;
|
||||
return -BCH_ERR_ENOMEM_btree_node_reclaim;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_reclaim);
|
||||
}
|
||||
if (btree_node_write_blocked(b)) {
|
||||
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_write_blocked]++;
|
||||
return -BCH_ERR_ENOMEM_btree_node_reclaim;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_reclaim);
|
||||
}
|
||||
if (btree_node_will_make_reachable(b)) {
|
||||
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_will_make_reachable]++;
|
||||
return -BCH_ERR_ENOMEM_btree_node_reclaim;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_reclaim);
|
||||
}
|
||||
|
||||
if (btree_node_dirty(b)) {
|
||||
if (!flush) {
|
||||
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_dirty]++;
|
||||
return -BCH_ERR_ENOMEM_btree_node_reclaim;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_reclaim);
|
||||
}
|
||||
|
||||
if (locked) {
|
||||
@@ -393,7 +393,7 @@ static int __btree_node_reclaim_checks(struct bch_fs *c, struct btree *b,
|
||||
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_read_in_flight]++;
|
||||
else if (btree_node_write_in_flight(b))
|
||||
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_write_in_flight]++;
|
||||
return -BCH_ERR_ENOMEM_btree_node_reclaim;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_reclaim);
|
||||
}
|
||||
|
||||
if (locked)
|
||||
@@ -424,13 +424,13 @@ retry_unlocked:
|
||||
|
||||
if (!six_trylock_intent(&b->c.lock)) {
|
||||
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_lock_intent]++;
|
||||
return -BCH_ERR_ENOMEM_btree_node_reclaim;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_reclaim);
|
||||
}
|
||||
|
||||
if (!six_trylock_write(&b->c.lock)) {
|
||||
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_lock_write]++;
|
||||
six_unlock_intent(&b->c.lock);
|
||||
return -BCH_ERR_ENOMEM_btree_node_reclaim;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_reclaim);
|
||||
}
|
||||
|
||||
/* recheck under lock */
|
||||
@@ -682,7 +682,7 @@ int bch2_fs_btree_cache_init(struct bch_fs *c)
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return -BCH_ERR_ENOMEM_fs_btree_cache_init;
|
||||
return bch_err_throw(c, ENOMEM_fs_btree_cache_init);
|
||||
}
|
||||
|
||||
void bch2_fs_btree_cache_init_early(struct btree_cache *bc)
|
||||
@@ -727,7 +727,7 @@ int bch2_btree_cache_cannibalize_lock(struct btree_trans *trans, struct closure
|
||||
|
||||
if (!cl) {
|
||||
trace_and_count(c, btree_cache_cannibalize_lock_fail, trans);
|
||||
return -BCH_ERR_ENOMEM_btree_cache_cannibalize_lock;
|
||||
return bch_err_throw(c, ENOMEM_btree_cache_cannibalize_lock);
|
||||
}
|
||||
|
||||
closure_wait(&bc->alloc_wait, cl);
|
||||
@@ -741,7 +741,7 @@ int bch2_btree_cache_cannibalize_lock(struct btree_trans *trans, struct closure
|
||||
}
|
||||
|
||||
trace_and_count(c, btree_cache_cannibalize_lock_fail, trans);
|
||||
return -BCH_ERR_btree_cache_cannibalize_lock_blocked;
|
||||
return bch_err_throw(c, btree_cache_cannibalize_lock_blocked);
|
||||
|
||||
success:
|
||||
trace_and_count(c, btree_cache_cannibalize_lock, trans);
|
||||
|
||||
+28
-29
@@ -150,7 +150,7 @@ static int set_node_min(struct bch_fs *c, struct btree *b, struct bpos new_min)
|
||||
|
||||
new = kmalloc_array(BKEY_BTREE_PTR_U64s_MAX, sizeof(u64), GFP_KERNEL);
|
||||
if (!new)
|
||||
return -BCH_ERR_ENOMEM_gc_repair_key;
|
||||
return bch_err_throw(c, ENOMEM_gc_repair_key);
|
||||
|
||||
btree_ptr_to_v2(b, new);
|
||||
b->data->min_key = new_min;
|
||||
@@ -190,7 +190,7 @@ static int set_node_max(struct bch_fs *c, struct btree *b, struct bpos new_max)
|
||||
|
||||
new = kmalloc_array(BKEY_BTREE_PTR_U64s_MAX, sizeof(u64), GFP_KERNEL);
|
||||
if (!new)
|
||||
return -BCH_ERR_ENOMEM_gc_repair_key;
|
||||
return bch_err_throw(c, ENOMEM_gc_repair_key);
|
||||
|
||||
btree_ptr_to_v2(b, new);
|
||||
b->data->max_key = new_max;
|
||||
@@ -935,7 +935,7 @@ static int bch2_gc_alloc_start(struct bch_fs *c)
|
||||
ret = genradix_prealloc(&ca->buckets_gc, ca->mi.nbuckets, GFP_KERNEL);
|
||||
if (ret) {
|
||||
bch2_dev_put(ca);
|
||||
ret = -BCH_ERR_ENOMEM_gc_alloc_start;
|
||||
ret = bch_err_throw(c, ENOMEM_gc_alloc_start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1093,42 +1093,41 @@ static int gc_btree_gens_key(struct btree_trans *trans,
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
|
||||
struct bkey_i *u;
|
||||
int ret;
|
||||
|
||||
if (unlikely(test_bit(BCH_FS_going_ro, &c->flags)))
|
||||
return -EROFS;
|
||||
|
||||
rcu_read_lock();
|
||||
bkey_for_each_ptr(ptrs, ptr) {
|
||||
struct bch_dev *ca = bch2_dev_rcu(c, ptr->dev);
|
||||
if (!ca)
|
||||
continue;
|
||||
bool too_stale = false;
|
||||
scoped_guard(rcu) {
|
||||
bkey_for_each_ptr(ptrs, ptr) {
|
||||
struct bch_dev *ca = bch2_dev_rcu(c, ptr->dev);
|
||||
if (!ca)
|
||||
continue;
|
||||
|
||||
if (dev_ptr_stale(ca, ptr) > 16) {
|
||||
rcu_read_unlock();
|
||||
goto update;
|
||||
too_stale |= dev_ptr_stale(ca, ptr) > 16;
|
||||
}
|
||||
|
||||
if (!too_stale)
|
||||
bkey_for_each_ptr(ptrs, ptr) {
|
||||
struct bch_dev *ca = bch2_dev_rcu(c, ptr->dev);
|
||||
if (!ca)
|
||||
continue;
|
||||
|
||||
u8 *gen = &ca->oldest_gen[PTR_BUCKET_NR(ca, ptr)];
|
||||
if (gen_after(*gen, ptr->gen))
|
||||
*gen = ptr->gen;
|
||||
}
|
||||
}
|
||||
|
||||
bkey_for_each_ptr(ptrs, ptr) {
|
||||
struct bch_dev *ca = bch2_dev_rcu(c, ptr->dev);
|
||||
if (!ca)
|
||||
continue;
|
||||
if (too_stale) {
|
||||
struct bkey_i *u = bch2_bkey_make_mut(trans, iter, &k, 0);
|
||||
int ret = PTR_ERR_OR_ZERO(u);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
u8 *gen = &ca->oldest_gen[PTR_BUCKET_NR(ca, ptr)];
|
||||
if (gen_after(*gen, ptr->gen))
|
||||
*gen = ptr->gen;
|
||||
bch2_extent_normalize(c, bkey_i_to_s(u));
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
update:
|
||||
u = bch2_bkey_make_mut(trans, iter, &k, 0);
|
||||
ret = PTR_ERR_OR_ZERO(u);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bch2_extent_normalize(c, bkey_i_to_s(u));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1181,7 +1180,7 @@ int bch2_gc_gens(struct bch_fs *c)
|
||||
ca->oldest_gen = kvmalloc(gens->nbuckets, GFP_KERNEL);
|
||||
if (!ca->oldest_gen) {
|
||||
bch2_dev_put(ca);
|
||||
ret = -BCH_ERR_ENOMEM_gc_gens;
|
||||
ret = bch_err_throw(c, ENOMEM_gc_gens);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
+21
-22
@@ -557,7 +557,7 @@ static int __btree_err(int ret,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
if (c->recovery.curr_pass == BCH_RECOVERY_PASS_scan_for_btree_nodes)
|
||||
return -BCH_ERR_fsck_fix;
|
||||
return bch_err_throw(c, fsck_fix);
|
||||
|
||||
bool have_retry = false;
|
||||
int ret2;
|
||||
@@ -572,9 +572,9 @@ static int __btree_err(int ret,
|
||||
}
|
||||
|
||||
if (!have_retry && ret == -BCH_ERR_btree_node_read_err_want_retry)
|
||||
ret = -BCH_ERR_btree_node_read_err_fixable;
|
||||
ret = bch_err_throw(c, btree_node_read_err_fixable);
|
||||
if (!have_retry && ret == -BCH_ERR_btree_node_read_err_must_retry)
|
||||
ret = -BCH_ERR_btree_node_read_err_bad_node;
|
||||
ret = bch_err_throw(c, btree_node_read_err_bad_node);
|
||||
|
||||
bch2_sb_error_count(c, err_type);
|
||||
|
||||
@@ -602,14 +602,14 @@ static int __btree_err(int ret,
|
||||
switch (ret) {
|
||||
case -BCH_ERR_btree_node_read_err_fixable:
|
||||
ret2 = bch2_fsck_err_opt(c, FSCK_CAN_FIX, err_type);
|
||||
if (ret2 != -BCH_ERR_fsck_fix &&
|
||||
ret2 != -BCH_ERR_fsck_ignore) {
|
||||
if (!bch2_err_matches(ret2, BCH_ERR_fsck_fix) &&
|
||||
!bch2_err_matches(ret2, BCH_ERR_fsck_ignore)) {
|
||||
ret = ret2;
|
||||
goto fsck_err;
|
||||
}
|
||||
|
||||
if (!have_retry)
|
||||
ret = -BCH_ERR_fsck_fix;
|
||||
ret = bch_err_throw(c, fsck_fix);
|
||||
goto out;
|
||||
case -BCH_ERR_btree_node_read_err_bad_node:
|
||||
prt_str(&out, ", ");
|
||||
@@ -631,14 +631,14 @@ static int __btree_err(int ret,
|
||||
switch (ret) {
|
||||
case -BCH_ERR_btree_node_read_err_fixable:
|
||||
ret2 = __bch2_fsck_err(c, NULL, FSCK_CAN_FIX, err_type, "%s", out.buf);
|
||||
if (ret2 != -BCH_ERR_fsck_fix &&
|
||||
ret2 != -BCH_ERR_fsck_ignore) {
|
||||
if (!bch2_err_matches(ret2, BCH_ERR_fsck_fix) &&
|
||||
!bch2_err_matches(ret2, BCH_ERR_fsck_ignore)) {
|
||||
ret = ret2;
|
||||
goto fsck_err;
|
||||
}
|
||||
|
||||
if (!have_retry)
|
||||
ret = -BCH_ERR_fsck_fix;
|
||||
ret = bch_err_throw(c, fsck_fix);
|
||||
goto out;
|
||||
case -BCH_ERR_btree_node_read_err_bad_node:
|
||||
prt_str(&out, ", ");
|
||||
@@ -660,7 +660,7 @@ fsck_err:
|
||||
failed, err_msg, \
|
||||
msg, ##__VA_ARGS__); \
|
||||
\
|
||||
if (_ret != -BCH_ERR_fsck_fix) { \
|
||||
if (!bch2_err_matches(_ret, BCH_ERR_fsck_fix)) { \
|
||||
ret = _ret; \
|
||||
goto fsck_err; \
|
||||
} \
|
||||
@@ -1325,14 +1325,13 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
|
||||
btree_node_reset_sib_u64s(b);
|
||||
|
||||
rcu_read_lock();
|
||||
bkey_for_each_ptr(bch2_bkey_ptrs(bkey_i_to_s(&b->key)), ptr) {
|
||||
struct bch_dev *ca2 = bch2_dev_rcu(c, ptr->dev);
|
||||
scoped_guard(rcu)
|
||||
bkey_for_each_ptr(bch2_bkey_ptrs(bkey_i_to_s(&b->key)), ptr) {
|
||||
struct bch_dev *ca2 = bch2_dev_rcu(c, ptr->dev);
|
||||
|
||||
if (!ca2 || ca2->mi.state != BCH_MEMBER_STATE_rw)
|
||||
set_btree_node_need_rewrite(b);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!ca2 || ca2->mi.state != BCH_MEMBER_STATE_rw)
|
||||
set_btree_node_need_rewrite(b);
|
||||
}
|
||||
|
||||
if (!ptr_written)
|
||||
set_btree_node_need_rewrite(b);
|
||||
@@ -1688,7 +1687,7 @@ static int btree_node_read_all_replicas(struct bch_fs *c, struct btree *b, bool
|
||||
|
||||
ra = kzalloc(sizeof(*ra), GFP_NOFS);
|
||||
if (!ra)
|
||||
return -BCH_ERR_ENOMEM_btree_node_read_all_replicas;
|
||||
return bch_err_throw(c, ENOMEM_btree_node_read_all_replicas);
|
||||
|
||||
closure_init(&ra->cl, NULL);
|
||||
ra->c = c;
|
||||
@@ -1870,7 +1869,7 @@ static int __bch2_btree_root_read(struct btree_trans *trans, enum btree_id id,
|
||||
bch2_btree_node_hash_remove(&c->btree_cache, b);
|
||||
mutex_unlock(&c->btree_cache.lock);
|
||||
|
||||
ret = -BCH_ERR_btree_node_read_error;
|
||||
ret = bch_err_throw(c, btree_node_read_error);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -2020,7 +2019,7 @@ int bch2_btree_node_scrub(struct btree_trans *trans,
|
||||
struct bch_fs *c = trans->c;
|
||||
|
||||
if (!enumerated_ref_tryget(&c->writes, BCH_WRITE_REF_btree_node_scrub))
|
||||
return -BCH_ERR_erofs_no_writes;
|
||||
return bch_err_throw(c, erofs_no_writes);
|
||||
|
||||
struct extent_ptr_decoded pick;
|
||||
int ret = bch2_bkey_pick_read_device(c, k, NULL, &pick, dev);
|
||||
@@ -2030,7 +2029,7 @@ int bch2_btree_node_scrub(struct btree_trans *trans,
|
||||
struct bch_dev *ca = bch2_dev_get_ioref(c, pick.ptr.dev, READ,
|
||||
BCH_DEV_READ_REF_btree_node_scrub);
|
||||
if (!ca) {
|
||||
ret = -BCH_ERR_device_offline;
|
||||
ret = bch_err_throw(c, device_offline);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -2167,7 +2166,7 @@ static void btree_node_write_work(struct work_struct *work)
|
||||
bch2_dev_list_has_dev(wbio->wbio.failed, ptr->dev));
|
||||
|
||||
if (!bch2_bkey_nr_ptrs(bkey_i_to_s_c(&wbio->key))) {
|
||||
ret = -BCH_ERR_btree_node_write_all_failed;
|
||||
ret = bch_err_throw(c, btree_node_write_all_failed);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
+38
-40
@@ -890,8 +890,7 @@ static noinline void btree_node_mem_ptr_set(struct btree_trans *trans,
|
||||
|
||||
static noinline int btree_node_iter_and_journal_peek(struct btree_trans *trans,
|
||||
struct btree_path *path,
|
||||
unsigned flags,
|
||||
struct bkey_buf *out)
|
||||
unsigned flags)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_path_level *l = path_l(path);
|
||||
@@ -915,7 +914,7 @@ static noinline int btree_node_iter_and_journal_peek(struct btree_trans *trans,
|
||||
goto err;
|
||||
}
|
||||
|
||||
bch2_bkey_buf_reassemble(out, c, k);
|
||||
bkey_reassemble(&trans->btree_path_down, k);
|
||||
|
||||
if ((flags & BTREE_ITER_prefetch) &&
|
||||
c->opts.btree_node_prefetch)
|
||||
@@ -926,6 +925,22 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static noinline_for_stack int btree_node_missing_err(struct btree_trans *trans,
|
||||
struct btree_path *path)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
prt_str(&buf, "node not found at pos ");
|
||||
bch2_bpos_to_text(&buf, path->pos);
|
||||
prt_str(&buf, " within parent node ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&path_l(path)->b->key));
|
||||
|
||||
bch2_fs_fatal_error(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return bch_err_throw(c, btree_need_topology_repair);
|
||||
}
|
||||
|
||||
static __always_inline int btree_path_down(struct btree_trans *trans,
|
||||
struct btree_path *path,
|
||||
unsigned flags,
|
||||
@@ -936,51 +951,38 @@ static __always_inline int btree_path_down(struct btree_trans *trans,
|
||||
struct btree *b;
|
||||
unsigned level = path->level - 1;
|
||||
enum six_lock_type lock_type = __btree_lock_want(path, level);
|
||||
struct bkey_buf tmp;
|
||||
int ret;
|
||||
|
||||
EBUG_ON(!btree_node_locked(path, path->level));
|
||||
|
||||
bch2_bkey_buf_init(&tmp);
|
||||
|
||||
if (unlikely(trans->journal_replay_not_finished)) {
|
||||
ret = btree_node_iter_and_journal_peek(trans, path, flags, &tmp);
|
||||
ret = btree_node_iter_and_journal_peek(trans, path, flags);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
} else {
|
||||
struct bkey_packed *k = bch2_btree_node_iter_peek(&l->iter, l->b);
|
||||
if (!k) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
if (unlikely(!k))
|
||||
return btree_node_missing_err(trans, path);
|
||||
|
||||
prt_str(&buf, "node not found at pos ");
|
||||
bch2_bpos_to_text(&buf, path->pos);
|
||||
prt_str(&buf, " within parent node ");
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&l->b->key));
|
||||
bch2_bkey_unpack(l->b, &trans->btree_path_down, k);
|
||||
|
||||
bch2_fs_fatal_error(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
ret = -BCH_ERR_btree_need_topology_repair;
|
||||
goto err;
|
||||
}
|
||||
|
||||
bch2_bkey_buf_unpack(&tmp, c, l->b, k);
|
||||
|
||||
if ((flags & BTREE_ITER_prefetch) &&
|
||||
if (unlikely((flags & BTREE_ITER_prefetch)) &&
|
||||
c->opts.btree_node_prefetch) {
|
||||
ret = btree_path_prefetch(trans, path);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
b = bch2_btree_node_get(trans, path, tmp.k, level, lock_type, trace_ip);
|
||||
b = bch2_btree_node_get(trans, path, &trans->btree_path_down,
|
||||
level, lock_type, trace_ip);
|
||||
ret = PTR_ERR_OR_ZERO(b);
|
||||
if (unlikely(ret))
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
if (likely(!trans->journal_replay_not_finished &&
|
||||
tmp.k->k.type == KEY_TYPE_btree_ptr_v2) &&
|
||||
unlikely(b != btree_node_mem_ptr(tmp.k)))
|
||||
if (unlikely(b != btree_node_mem_ptr(&trans->btree_path_down)) &&
|
||||
likely(!trans->journal_replay_not_finished &&
|
||||
trans->btree_path_down.k.type == KEY_TYPE_btree_ptr_v2))
|
||||
btree_node_mem_ptr_set(trans, path, level + 1, b);
|
||||
|
||||
if (btree_node_read_locked(path, level + 1))
|
||||
@@ -992,9 +994,7 @@ static __always_inline int btree_path_down(struct btree_trans *trans,
|
||||
bch2_btree_path_level_init(trans, path, b);
|
||||
|
||||
bch2_btree_path_verify_locks(trans, path);
|
||||
err:
|
||||
bch2_bkey_buf_exit(&tmp, c);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bch2_btree_path_traverse_all(struct btree_trans *trans)
|
||||
@@ -1006,7 +1006,7 @@ static int bch2_btree_path_traverse_all(struct btree_trans *trans)
|
||||
int ret = 0;
|
||||
|
||||
if (trans->in_traverse_all)
|
||||
return -BCH_ERR_transaction_restart_in_traverse_all;
|
||||
return bch_err_throw(c, transaction_restart_in_traverse_all);
|
||||
|
||||
trans->in_traverse_all = true;
|
||||
retry_all:
|
||||
@@ -3568,13 +3568,12 @@ bch2_btree_bkey_cached_common_to_text(struct printbuf *out,
|
||||
struct btree_bkey_cached_common *b)
|
||||
{
|
||||
struct six_lock_count c = six_lock_counts(&b->lock);
|
||||
struct task_struct *owner;
|
||||
pid_t pid;
|
||||
|
||||
rcu_read_lock();
|
||||
owner = READ_ONCE(b->lock.owner);
|
||||
pid = owner ? owner->pid : 0;
|
||||
rcu_read_unlock();
|
||||
scoped_guard(rcu) {
|
||||
struct task_struct *owner = READ_ONCE(b->lock.owner);
|
||||
pid = owner ? owner->pid : 0;
|
||||
}
|
||||
|
||||
prt_printf(out, "\t%px %c ", b, b->cached ? 'c' : 'b');
|
||||
bch2_btree_id_to_text(out, b->btree_id);
|
||||
@@ -3603,7 +3602,7 @@ void bch2_btree_trans_to_text(struct printbuf *out, struct btree_trans *trans)
|
||||
prt_printf(out, "%i %s\n", task ? task->pid : 0, trans->fn);
|
||||
|
||||
/* trans->paths is rcu protected vs. freeing */
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
out->atomic++;
|
||||
|
||||
struct btree_path *paths = rcu_dereference(trans->paths);
|
||||
@@ -3646,7 +3645,6 @@ void bch2_btree_trans_to_text(struct printbuf *out, struct btree_trans *trans)
|
||||
}
|
||||
out:
|
||||
--out->atomic;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void bch2_fs_btree_iter_exit(struct bch_fs *c)
|
||||
|
||||
+21
-10
@@ -963,16 +963,6 @@ struct bkey_s_c bch2_btree_iter_peek_and_restart_outlined(struct btree_trans *,
|
||||
_p; \
|
||||
})
|
||||
|
||||
#define bch2_trans_run(_c, _do) \
|
||||
({ \
|
||||
struct btree_trans *trans = bch2_trans_get(_c); \
|
||||
int _ret = (_do); \
|
||||
bch2_trans_put(trans); \
|
||||
_ret; \
|
||||
})
|
||||
|
||||
#define bch2_trans_do(_c, _do) bch2_trans_run(_c, lockrestart_do(trans, _do))
|
||||
|
||||
struct btree_trans *__bch2_trans_get(struct bch_fs *, unsigned);
|
||||
void bch2_trans_put(struct btree_trans *);
|
||||
|
||||
@@ -990,6 +980,27 @@ unsigned bch2_trans_get_fn_idx(const char *);
|
||||
__bch2_trans_get(_c, trans_fn_idx); \
|
||||
})
|
||||
|
||||
/*
|
||||
* We don't use DEFINE_CLASS() because using a function for the constructor
|
||||
* breaks bch2_trans_get()'s use of __func__
|
||||
*/
|
||||
typedef struct btree_trans * class_btree_trans_t;
|
||||
static inline void class_btree_trans_destructor(struct btree_trans **p)
|
||||
{
|
||||
struct btree_trans *trans = *p;
|
||||
bch2_trans_put(trans);
|
||||
}
|
||||
|
||||
#define class_btree_trans_constructor(_c) bch2_trans_get(_c)
|
||||
|
||||
#define bch2_trans_run(_c, _do) \
|
||||
({ \
|
||||
CLASS(btree_trans, trans)(_c); \
|
||||
(_do); \
|
||||
})
|
||||
|
||||
#define bch2_trans_do(_c, _do) bch2_trans_run(_c, lockrestart_do(trans, _do))
|
||||
|
||||
void bch2_btree_trans_to_text(struct printbuf *, struct btree_trans *);
|
||||
|
||||
void bch2_fs_btree_iter_exit(struct bch_fs *);
|
||||
|
||||
@@ -292,7 +292,7 @@ int bch2_journal_key_insert_take(struct bch_fs *c, enum btree_id id,
|
||||
if (!new_keys.data) {
|
||||
bch_err(c, "%s: error allocating new key array (size %zu)",
|
||||
__func__, new_keys.size);
|
||||
return -BCH_ERR_ENOMEM_journal_key_insert;
|
||||
return bch_err_throw(c, ENOMEM_journal_key_insert);
|
||||
}
|
||||
|
||||
/* Since @keys was full, there was no gap: */
|
||||
@@ -331,7 +331,7 @@ int bch2_journal_key_insert(struct bch_fs *c, enum btree_id id,
|
||||
|
||||
n = kmalloc(bkey_bytes(&k->k), GFP_KERNEL);
|
||||
if (!n)
|
||||
return -BCH_ERR_ENOMEM_journal_key_insert;
|
||||
return bch_err_throw(c, ENOMEM_journal_key_insert);
|
||||
|
||||
bkey_copy(n, k);
|
||||
ret = bch2_journal_key_insert_take(c, id, level, n);
|
||||
@@ -457,11 +457,9 @@ static void bch2_journal_iter_advance(struct journal_iter *iter)
|
||||
|
||||
static struct bkey_s_c bch2_journal_iter_peek(struct journal_iter *iter)
|
||||
{
|
||||
struct bkey_s_c ret = bkey_s_c_null;
|
||||
|
||||
journal_iter_verify(iter);
|
||||
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
while (iter->idx < iter->keys->size) {
|
||||
struct journal_key *k = iter->keys->data + iter->idx;
|
||||
|
||||
@@ -470,19 +468,16 @@ static struct bkey_s_c bch2_journal_iter_peek(struct journal_iter *iter)
|
||||
break;
|
||||
BUG_ON(cmp);
|
||||
|
||||
if (!k->overwritten) {
|
||||
ret = bkey_i_to_s_c(k->k);
|
||||
break;
|
||||
}
|
||||
if (!k->overwritten)
|
||||
return bkey_i_to_s_c(k->k);
|
||||
|
||||
if (k->overwritten_range)
|
||||
iter->idx = idx_to_pos(iter->keys, rcu_dereference(k->overwritten_range)->end);
|
||||
else
|
||||
bch2_journal_iter_advance(iter);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
return bkey_s_c_null;
|
||||
}
|
||||
|
||||
static void bch2_journal_iter_exit(struct journal_iter *iter)
|
||||
@@ -741,7 +736,7 @@ int bch2_journal_keys_sort(struct bch_fs *c)
|
||||
if (keys->nr * 8 > keys->size * 7) {
|
||||
bch_err(c, "Too many journal keys for slowpath; have %zu compacted, buf size %zu, processed %zu keys at seq %llu",
|
||||
keys->nr, keys->size, nr_read, le64_to_cpu(i->j.seq));
|
||||
return -BCH_ERR_ENOMEM_journal_keys_sort;
|
||||
return bch_err_throw(c, ENOMEM_journal_keys_sort);
|
||||
}
|
||||
|
||||
BUG_ON(darray_push(keys, n));
|
||||
|
||||
@@ -187,27 +187,23 @@ lock:
|
||||
static struct bkey_cached *
|
||||
bkey_cached_reuse(struct btree_key_cache *c)
|
||||
{
|
||||
struct bucket_table *tbl;
|
||||
|
||||
guard(rcu)();
|
||||
struct bucket_table *tbl = rht_dereference_rcu(c->table.tbl, &c->table);
|
||||
struct rhash_head *pos;
|
||||
struct bkey_cached *ck;
|
||||
unsigned i;
|
||||
|
||||
rcu_read_lock();
|
||||
tbl = rht_dereference_rcu(c->table.tbl, &c->table);
|
||||
for (i = 0; i < tbl->size; i++)
|
||||
for (unsigned i = 0; i < tbl->size; i++)
|
||||
rht_for_each_entry_rcu(ck, pos, tbl, i, hash) {
|
||||
if (!test_bit(BKEY_CACHED_DIRTY, &ck->flags) &&
|
||||
bkey_cached_lock_for_evict(ck)) {
|
||||
if (bkey_cached_evict(c, ck))
|
||||
goto out;
|
||||
return ck;
|
||||
six_unlock_write(&ck->c.lock);
|
||||
six_unlock_intent(&ck->c.lock);
|
||||
}
|
||||
}
|
||||
ck = NULL;
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return ck;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int btree_key_cache_create(struct btree_trans *trans,
|
||||
@@ -242,7 +238,7 @@ static int btree_key_cache_create(struct btree_trans *trans,
|
||||
if (unlikely(!ck)) {
|
||||
bch_err(c, "error allocating memory for key cache item, btree %s",
|
||||
bch2_btree_id_str(ck_path->btree_id));
|
||||
return -BCH_ERR_ENOMEM_btree_key_cache_create;
|
||||
return bch_err_throw(c, ENOMEM_btree_key_cache_create);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +256,7 @@ static int btree_key_cache_create(struct btree_trans *trans,
|
||||
if (unlikely(!new_k)) {
|
||||
bch_err(trans->c, "error allocating memory for key cache key, btree %s u64s %u",
|
||||
bch2_btree_id_str(ck->key.btree_id), key_u64s);
|
||||
ret = -BCH_ERR_ENOMEM_btree_key_cache_fill;
|
||||
ret = bch_err_throw(c, ENOMEM_btree_key_cache_fill);
|
||||
} else if (ret) {
|
||||
kfree(new_k);
|
||||
goto err;
|
||||
@@ -826,20 +822,20 @@ int bch2_fs_btree_key_cache_init(struct btree_key_cache *bc)
|
||||
|
||||
bc->nr_pending = alloc_percpu(size_t);
|
||||
if (!bc->nr_pending)
|
||||
return -BCH_ERR_ENOMEM_fs_btree_cache_init;
|
||||
return bch_err_throw(c, ENOMEM_fs_btree_cache_init);
|
||||
|
||||
if (rcu_pending_init(&bc->pending[0], &c->btree_trans_barrier, __bkey_cached_free) ||
|
||||
rcu_pending_init(&bc->pending[1], &c->btree_trans_barrier, __bkey_cached_free))
|
||||
return -BCH_ERR_ENOMEM_fs_btree_cache_init;
|
||||
return bch_err_throw(c, ENOMEM_fs_btree_cache_init);
|
||||
|
||||
if (rhashtable_init(&bc->table, &bch2_btree_key_cache_params))
|
||||
return -BCH_ERR_ENOMEM_fs_btree_cache_init;
|
||||
return bch_err_throw(c, ENOMEM_fs_btree_cache_init);
|
||||
|
||||
bc->table_init_done = true;
|
||||
|
||||
shrink = shrinker_alloc(0, "%s-btree_key_cache", c->name);
|
||||
if (!shrink)
|
||||
return -BCH_ERR_ENOMEM_fs_btree_cache_init;
|
||||
return bch_err_throw(c, ENOMEM_fs_btree_cache_init);
|
||||
bc->shrink = shrink;
|
||||
shrink->count_objects = bch2_btree_key_cache_count;
|
||||
shrink->scan_objects = bch2_btree_key_cache_scan;
|
||||
|
||||
+29
-27
@@ -194,6 +194,30 @@ static int btree_trans_abort_preference(struct btree_trans *trans)
|
||||
return 3;
|
||||
}
|
||||
|
||||
static noinline __noreturn void break_cycle_fail(struct lock_graph *g)
|
||||
{
|
||||
struct printbuf buf = PRINTBUF;
|
||||
buf.atomic++;
|
||||
|
||||
prt_printf(&buf, bch2_fmt(g->g->trans->c, "cycle of nofail locks"));
|
||||
|
||||
for (struct trans_waiting_for_lock *i = g->g; i < g->g + g->nr; i++) {
|
||||
struct btree_trans *trans = i->trans;
|
||||
|
||||
bch2_btree_trans_to_text(&buf, trans);
|
||||
|
||||
prt_printf(&buf, "backtrace:\n");
|
||||
printbuf_indent_add(&buf, 2);
|
||||
bch2_prt_task_backtrace(&buf, trans->locking_wait.task, 2, GFP_NOWAIT);
|
||||
printbuf_indent_sub(&buf, 2);
|
||||
prt_newline(&buf);
|
||||
}
|
||||
|
||||
bch2_print_str_nonblocking(g->g->trans->c, KERN_ERR, buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
BUG();
|
||||
}
|
||||
|
||||
static noinline int break_cycle(struct lock_graph *g, struct printbuf *cycle,
|
||||
struct trans_waiting_for_lock *from)
|
||||
{
|
||||
@@ -219,28 +243,8 @@ static noinline int break_cycle(struct lock_graph *g, struct printbuf *cycle,
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(!best)) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
buf.atomic++;
|
||||
|
||||
prt_printf(&buf, bch2_fmt(g->g->trans->c, "cycle of nofail locks"));
|
||||
|
||||
for (i = g->g; i < g->g + g->nr; i++) {
|
||||
struct btree_trans *trans = i->trans;
|
||||
|
||||
bch2_btree_trans_to_text(&buf, trans);
|
||||
|
||||
prt_printf(&buf, "backtrace:\n");
|
||||
printbuf_indent_add(&buf, 2);
|
||||
bch2_prt_task_backtrace(&buf, trans->locking_wait.task, 2, GFP_NOWAIT);
|
||||
printbuf_indent_sub(&buf, 2);
|
||||
prt_newline(&buf);
|
||||
}
|
||||
|
||||
bch2_print_str_nonblocking(g->g->trans->c, KERN_ERR, buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
BUG();
|
||||
}
|
||||
if (unlikely(!best))
|
||||
break_cycle_fail(g);
|
||||
|
||||
ret = abort_lock(g, abort);
|
||||
out:
|
||||
@@ -255,15 +259,14 @@ static int lock_graph_descend(struct lock_graph *g, struct btree_trans *trans,
|
||||
struct printbuf *cycle)
|
||||
{
|
||||
struct btree_trans *orig_trans = g->g->trans;
|
||||
struct trans_waiting_for_lock *i;
|
||||
|
||||
for (i = g->g; i < g->g + g->nr; i++)
|
||||
for (struct trans_waiting_for_lock *i = g->g; i < g->g + g->nr; i++)
|
||||
if (i->trans == trans) {
|
||||
closure_put(&trans->ref);
|
||||
return break_cycle(g, cycle, i);
|
||||
}
|
||||
|
||||
if (g->nr == ARRAY_SIZE(g->g)) {
|
||||
if (unlikely(g->nr == ARRAY_SIZE(g->g))) {
|
||||
closure_put(&trans->ref);
|
||||
|
||||
if (orig_trans->lock_may_not_fail)
|
||||
@@ -308,7 +311,7 @@ int bch2_check_for_deadlock(struct btree_trans *trans, struct printbuf *cycle)
|
||||
lock_graph_down(&g, trans);
|
||||
|
||||
/* trans->paths is rcu protected vs. freeing */
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
if (cycle)
|
||||
cycle->atomic++;
|
||||
next:
|
||||
@@ -406,7 +409,6 @@ up:
|
||||
out:
|
||||
if (cycle)
|
||||
--cycle->atomic;
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -363,6 +363,8 @@ static int handle_overwrites(struct bch_fs *c,
|
||||
min_heap_sift_down(nodes_heap, 0, &found_btree_node_heap_cbs, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -376,7 +376,7 @@ static inline int btree_key_can_insert(struct btree_trans *trans,
|
||||
struct btree *b, unsigned u64s)
|
||||
{
|
||||
if (!bch2_btree_node_insert_fits(b, u64s))
|
||||
return -BCH_ERR_btree_insert_btree_node_full;
|
||||
return bch_err_throw(trans->c, btree_insert_btree_node_full);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -394,9 +394,10 @@ btree_key_can_insert_cached_slowpath(struct btree_trans *trans, unsigned flags,
|
||||
|
||||
new_k = kmalloc(new_u64s * sizeof(u64), GFP_KERNEL);
|
||||
if (!new_k) {
|
||||
bch_err(trans->c, "error allocating memory for key cache key, btree %s u64s %u",
|
||||
struct bch_fs *c = trans->c;
|
||||
bch_err(c, "error allocating memory for key cache key, btree %s u64s %u",
|
||||
bch2_btree_id_str(path->btree_id), new_u64s);
|
||||
return -BCH_ERR_ENOMEM_btree_key_cache_insert;
|
||||
return bch_err_throw(c, ENOMEM_btree_key_cache_insert);
|
||||
}
|
||||
|
||||
ret = bch2_trans_relock(trans) ?:
|
||||
@@ -432,7 +433,7 @@ static int btree_key_can_insert_cached(struct btree_trans *trans, unsigned flags
|
||||
if (watermark < BCH_WATERMARK_reclaim &&
|
||||
!test_bit(BKEY_CACHED_DIRTY, &ck->flags) &&
|
||||
bch2_btree_key_cache_must_wait(c))
|
||||
return -BCH_ERR_btree_insert_need_journal_reclaim;
|
||||
return bch_err_throw(c, btree_insert_need_journal_reclaim);
|
||||
|
||||
/*
|
||||
* bch2_varint_decode can read past the end of the buffer by at most 7
|
||||
@@ -894,7 +895,7 @@ int bch2_trans_commit_error(struct btree_trans *trans, unsigned flags,
|
||||
*/
|
||||
if ((flags & BCH_TRANS_COMMIT_journal_reclaim) &&
|
||||
watermark < BCH_WATERMARK_reclaim) {
|
||||
ret = -BCH_ERR_journal_reclaim_would_deadlock;
|
||||
ret = bch_err_throw(c, journal_reclaim_would_deadlock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -966,14 +967,27 @@ do_bch2_trans_commit_to_journal_replay(struct btree_trans *trans)
|
||||
|
||||
for (struct jset_entry *i = btree_trans_journal_entries_start(trans);
|
||||
i != btree_trans_journal_entries_top(trans);
|
||||
i = vstruct_next(i))
|
||||
i = vstruct_next(i)) {
|
||||
if (i->type == BCH_JSET_ENTRY_btree_keys ||
|
||||
i->type == BCH_JSET_ENTRY_write_buffer_keys) {
|
||||
int ret = bch2_journal_key_insert(c, i->btree_id, i->level, i->start);
|
||||
if (ret)
|
||||
return ret;
|
||||
jset_entry_for_each_key(i, k) {
|
||||
int ret = bch2_journal_key_insert(c, i->btree_id, i->level, k);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (i->type == BCH_JSET_ENTRY_btree_root) {
|
||||
guard(mutex)(&c->btree_root_lock);
|
||||
|
||||
struct btree_root *r = bch2_btree_id_root(c, i->btree_id);
|
||||
|
||||
bkey_copy(&r->key, i->start);
|
||||
r->level = i->level;
|
||||
r->alive = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (struct bkey_i *i = btree_trans_subbuf_base(trans, &trans->accounting);
|
||||
i != btree_trans_subbuf_top(trans, &trans->accounting);
|
||||
i = bkey_next(i)) {
|
||||
@@ -1011,7 +1025,7 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
|
||||
if (unlikely(!test_bit(BCH_FS_may_go_rw, &c->flags)))
|
||||
ret = do_bch2_trans_commit_to_journal_replay(trans);
|
||||
else
|
||||
ret = -BCH_ERR_erofs_trans_commit;
|
||||
ret = bch_err_throw(c, erofs_trans_commit);
|
||||
goto out_reset;
|
||||
}
|
||||
|
||||
@@ -1093,7 +1107,7 @@ err:
|
||||
* restart:
|
||||
*/
|
||||
if (flags & BCH_TRANS_COMMIT_no_journal_res) {
|
||||
ret = -BCH_ERR_transaction_restart_nested;
|
||||
ret = bch_err_throw(c, transaction_restart_nested);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -555,6 +555,8 @@ struct btree_trans {
|
||||
unsigned journal_u64s;
|
||||
unsigned extra_disk_res; /* XXX kill */
|
||||
|
||||
__BKEY_PADDED(btree_path_down, BKEY_BTREE_PTR_VAL_U64s_MAX);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
struct lockdep_map dep_map;
|
||||
#endif
|
||||
|
||||
+19
-40
@@ -123,65 +123,44 @@ static int need_whiteout_for_snapshot(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
int __bch2_insert_snapshot_whiteouts(struct btree_trans *trans,
|
||||
enum btree_id id,
|
||||
struct bpos old_pos,
|
||||
struct bpos new_pos)
|
||||
enum btree_id btree, struct bpos pos,
|
||||
snapshot_id_list *s)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_iter old_iter, new_iter = {};
|
||||
struct bkey_s_c old_k, new_k;
|
||||
snapshot_id_list s;
|
||||
struct bkey_i *update;
|
||||
int ret = 0;
|
||||
|
||||
if (!bch2_snapshot_has_children(c, old_pos.snapshot))
|
||||
return 0;
|
||||
darray_for_each(*s, id) {
|
||||
pos.snapshot = *id;
|
||||
|
||||
darray_init(&s);
|
||||
|
||||
bch2_trans_iter_init(trans, &old_iter, id, old_pos,
|
||||
BTREE_ITER_not_extents|
|
||||
BTREE_ITER_all_snapshots);
|
||||
while ((old_k = bch2_btree_iter_prev(trans, &old_iter)).k &&
|
||||
!(ret = bkey_err(old_k)) &&
|
||||
bkey_eq(old_pos, old_k.k->p)) {
|
||||
struct bpos whiteout_pos =
|
||||
SPOS(new_pos.inode, new_pos.offset, old_k.k->p.snapshot);
|
||||
|
||||
if (!bch2_snapshot_is_ancestor(c, old_k.k->p.snapshot, old_pos.snapshot) ||
|
||||
snapshot_list_has_ancestor(c, &s, old_k.k->p.snapshot))
|
||||
continue;
|
||||
|
||||
new_k = bch2_bkey_get_iter(trans, &new_iter, id, whiteout_pos,
|
||||
BTREE_ITER_not_extents|
|
||||
BTREE_ITER_intent);
|
||||
ret = bkey_err(new_k);
|
||||
struct btree_iter iter;
|
||||
struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, btree, pos,
|
||||
BTREE_ITER_not_extents|
|
||||
BTREE_ITER_intent);
|
||||
ret = bkey_err(k);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (new_k.k->type == KEY_TYPE_deleted) {
|
||||
update = bch2_trans_kmalloc(trans, sizeof(struct bkey_i));
|
||||
if (k.k->type == KEY_TYPE_deleted) {
|
||||
struct bkey_i *update = bch2_trans_kmalloc(trans, sizeof(struct bkey_i));
|
||||
ret = PTR_ERR_OR_ZERO(update);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
break;
|
||||
}
|
||||
|
||||
bkey_init(&update->k);
|
||||
update->k.p = whiteout_pos;
|
||||
update->k.p = pos;
|
||||
update->k.type = KEY_TYPE_whiteout;
|
||||
|
||||
ret = bch2_trans_update(trans, &new_iter, update,
|
||||
ret = bch2_trans_update(trans, &iter, update,
|
||||
BTREE_UPDATE_internal_snapshot_node);
|
||||
}
|
||||
bch2_trans_iter_exit(trans, &new_iter);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
|
||||
ret = snapshot_list_add(c, &s, old_k.k->p.snapshot);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
bch2_trans_iter_exit(trans, &new_iter);
|
||||
bch2_trans_iter_exit(trans, &old_iter);
|
||||
darray_exit(&s);
|
||||
|
||||
darray_exit(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -608,7 +587,7 @@ int bch2_bkey_get_empty_slot(struct btree_trans *trans, struct btree_iter *iter,
|
||||
BUG_ON(k.k->type != KEY_TYPE_deleted);
|
||||
|
||||
if (bkey_gt(k.k->p, end)) {
|
||||
ret = -BCH_ERR_ENOSPC_btree_slot;
|
||||
ret = bch_err_throw(trans->c, ENOSPC_btree_slot);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "btree_iter.h"
|
||||
#include "journal.h"
|
||||
#include "snapshot.h"
|
||||
|
||||
struct bch_fs;
|
||||
struct btree;
|
||||
@@ -74,7 +75,7 @@ static inline int bch2_btree_delete_at_buffered(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
int __bch2_insert_snapshot_whiteouts(struct btree_trans *, enum btree_id,
|
||||
struct bpos, struct bpos);
|
||||
struct bpos, snapshot_id_list *);
|
||||
|
||||
/*
|
||||
* For use when splitting extents in existing snapshots:
|
||||
@@ -88,11 +89,20 @@ static inline int bch2_insert_snapshot_whiteouts(struct btree_trans *trans,
|
||||
struct bpos old_pos,
|
||||
struct bpos new_pos)
|
||||
{
|
||||
BUG_ON(old_pos.snapshot != new_pos.snapshot);
|
||||
|
||||
if (!btree_type_has_snapshots(btree) ||
|
||||
bkey_eq(old_pos, new_pos))
|
||||
return 0;
|
||||
|
||||
return __bch2_insert_snapshot_whiteouts(trans, btree, old_pos, new_pos);
|
||||
snapshot_id_list s;
|
||||
int ret = bch2_get_snapshot_overwrites(trans, btree, old_pos, &s);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return s.nr
|
||||
? __bch2_insert_snapshot_whiteouts(trans, btree, new_pos, &s)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int bch2_trans_update_extent_overwrite(struct btree_trans *, struct btree_iter *,
|
||||
|
||||
Loaded 20 of 98 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user