Update bcachefs sources to c1ec91699f60 workqueue: Basic memory allocation profiling support
Some checks failed
build / bcachefs-tools-deb (ubuntu-22.04) (push) Has been cancelled
build / bcachefs-tools-deb (ubuntu-24.04) (push) Has been cancelled
build / bcachefs-tools-rpm (push) Has been cancelled
build / bcachefs-tools-msrv (push) Has been cancelled
Nix Flake actions / nix-matrix (push) Has been cancelled
Nix Flake actions / ${{ matrix.name }} (${{ matrix.system }}) (push) Has been cancelled

This commit is contained in:
Kent Overstreet 2025-07-19 14:29:30 -04:00
parent ffe083c31d
commit e17d8841ef
34 changed files with 304 additions and 195 deletions

View File

@ -1 +1 @@
deeffbdc52f1092dadb3d523c4429e002c7fc485 c1ec91699f6089754548cfc01391fff17a633516

View File

@ -138,8 +138,8 @@ static struct posix_acl *bch2_acl_from_disk(struct btree_trans *trans,
acl = allocate_dropping_locks(trans, ret, acl = allocate_dropping_locks(trans, ret,
posix_acl_alloc(count, _gfp)); posix_acl_alloc(count, _gfp));
if (!acl) if (!acl && !ret)
return ERR_PTR(-ENOMEM); ret = bch_err_throw(trans->c, ENOMEM_acl);
if (ret) { if (ret) {
kfree(acl); kfree(acl);
return ERR_PTR(ret); return ERR_PTR(ret);

View File

@ -337,9 +337,10 @@ void bch2_alloc_v4_swab(struct bkey_s k)
} }
static inline void __bch2_alloc_v4_to_text(struct printbuf *out, struct bch_fs *c, static inline void __bch2_alloc_v4_to_text(struct printbuf *out, struct bch_fs *c,
unsigned dev, const struct bch_alloc_v4 *a) struct bkey_s_c k,
const struct bch_alloc_v4 *a)
{ {
struct bch_dev *ca = c ? bch2_dev_tryget_noerror(c, dev) : NULL; struct bch_dev *ca = c ? bch2_dev_tryget_noerror(c, k.k->p.inode) : NULL;
prt_newline(out); prt_newline(out);
printbuf_indent_add(out, 2); printbuf_indent_add(out, 2);
@ -348,11 +349,14 @@ static inline void __bch2_alloc_v4_to_text(struct printbuf *out, struct bch_fs *
bch2_prt_data_type(out, a->data_type); bch2_prt_data_type(out, a->data_type);
prt_newline(out); prt_newline(out);
prt_printf(out, "journal_seq_nonempty %llu\n", a->journal_seq_nonempty); prt_printf(out, "journal_seq_nonempty %llu\n", a->journal_seq_nonempty);
prt_printf(out, "journal_seq_empty %llu\n", a->journal_seq_empty); if (bkey_val_bytes(k.k) > offsetof(struct bch_alloc_v4, journal_seq_empty))
prt_printf(out, "journal_seq_empty %llu\n", a->journal_seq_empty);
prt_printf(out, "need_discard %llu\n", BCH_ALLOC_V4_NEED_DISCARD(a)); prt_printf(out, "need_discard %llu\n", BCH_ALLOC_V4_NEED_DISCARD(a));
prt_printf(out, "need_inc_gen %llu\n", BCH_ALLOC_V4_NEED_INC_GEN(a)); prt_printf(out, "need_inc_gen %llu\n", BCH_ALLOC_V4_NEED_INC_GEN(a));
prt_printf(out, "dirty_sectors %u\n", a->dirty_sectors); prt_printf(out, "dirty_sectors %u\n", a->dirty_sectors);
prt_printf(out, "stripe_sectors %u\n", a->stripe_sectors); if (bkey_val_bytes(k.k) > offsetof(struct bch_alloc_v4, stripe_sectors))
prt_printf(out, "stripe_sectors %u\n", a->stripe_sectors);
prt_printf(out, "cached_sectors %u\n", a->cached_sectors); prt_printf(out, "cached_sectors %u\n", a->cached_sectors);
prt_printf(out, "stripe %u\n", a->stripe); prt_printf(out, "stripe %u\n", a->stripe);
prt_printf(out, "stripe_redundancy %u\n", a->stripe_redundancy); prt_printf(out, "stripe_redundancy %u\n", a->stripe_redundancy);
@ -372,12 +376,12 @@ void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c
struct bch_alloc_v4 _a; struct bch_alloc_v4 _a;
const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k, &_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); __bch2_alloc_v4_to_text(out, c, k, a);
} }
void bch2_alloc_v4_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k) 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); __bch2_alloc_v4_to_text(out, c, k, bkey_s_c_to_alloc_v4(k).v);
} }
void __bch2_alloc_to_v4(struct bkey_s_c k, struct bch_alloc_v4 *out) void __bch2_alloc_to_v4(struct bkey_s_c k, struct bch_alloc_v4 *out)

View File

@ -44,10 +44,6 @@
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/sched/task.h> #include <linux/sched/task.h>
#define DROP_THIS_NODE 10
#define DROP_PREV_NODE 11
#define DID_FILL_FROM_SCAN 12
/* /*
* Returns true if it's a btree we can easily reconstruct, or otherwise won't * Returns true if it's a btree we can easily reconstruct, or otherwise won't
* cause data loss if it's missing: * cause data loss if it's missing:
@ -252,7 +248,7 @@ static int btree_check_node_boundaries(struct btree_trans *trans, struct btree *
return ret; return ret;
*pulled_from_scan = cur->data->min_key; *pulled_from_scan = cur->data->min_key;
ret = DID_FILL_FROM_SCAN; ret = bch_err_throw(c, topology_repair_did_fill_from_scan);
} else { } else {
if (mustfix_fsck_err(trans, btree_node_topology_bad_min_key, if (mustfix_fsck_err(trans, btree_node_topology_bad_min_key,
"btree node with incorrect min_key%s", buf.buf)) "btree node with incorrect min_key%s", buf.buf))
@ -263,7 +259,7 @@ static int btree_check_node_boundaries(struct btree_trans *trans, struct btree *
if (bpos_ge(prev->data->min_key, cur->data->min_key)) { /* fully? */ if (bpos_ge(prev->data->min_key, cur->data->min_key)) { /* fully? */
if (mustfix_fsck_err(trans, btree_node_topology_overwritten_by_next_node, if (mustfix_fsck_err(trans, btree_node_topology_overwritten_by_next_node,
"btree node overwritten by next node%s", buf.buf)) "btree node overwritten by next node%s", buf.buf))
ret = DROP_PREV_NODE; ret = bch_err_throw(c, topology_repair_drop_prev_node);
} else { } else {
if (mustfix_fsck_err(trans, btree_node_topology_bad_max_key, if (mustfix_fsck_err(trans, btree_node_topology_bad_max_key,
"btree node with incorrect max_key%s", buf.buf)) "btree node with incorrect max_key%s", buf.buf))
@ -274,7 +270,7 @@ static int btree_check_node_boundaries(struct btree_trans *trans, struct btree *
if (bpos_ge(expected_start, cur->data->max_key)) { /* fully? */ if (bpos_ge(expected_start, cur->data->max_key)) { /* fully? */
if (mustfix_fsck_err(trans, btree_node_topology_overwritten_by_prev_node, if (mustfix_fsck_err(trans, btree_node_topology_overwritten_by_prev_node,
"btree node overwritten by prev node%s", buf.buf)) "btree node overwritten by prev node%s", buf.buf))
ret = DROP_THIS_NODE; ret = bch_err_throw(c, topology_repair_drop_this_node);
} else { } else {
if (mustfix_fsck_err(trans, btree_node_topology_bad_min_key, if (mustfix_fsck_err(trans, btree_node_topology_bad_min_key,
"btree node with incorrect min_key%s", buf.buf)) "btree node with incorrect min_key%s", buf.buf))
@ -314,7 +310,7 @@ static int btree_repair_node_end(struct btree_trans *trans, struct btree *b,
return ret; return ret;
*pulled_from_scan = b->key.k.p; *pulled_from_scan = b->key.k.p;
ret = DID_FILL_FROM_SCAN; ret = bch_err_throw(c, topology_repair_did_fill_from_scan);
} else { } else {
ret = set_node_max(c, child, b->key.k.p); ret = set_node_max(c, child, b->key.k.p);
} }
@ -391,15 +387,15 @@ again:
ret = lockrestart_do(trans, ret = lockrestart_do(trans,
btree_check_node_boundaries(trans, b, prev, cur, pulled_from_scan)); btree_check_node_boundaries(trans, b, prev, cur, pulled_from_scan));
if (ret < 0) if (ret && !bch2_err_matches(ret, BCH_ERR_topology_repair))
goto err; goto err;
if (ret == DID_FILL_FROM_SCAN) { if (bch2_err_matches(ret, BCH_ERR_topology_repair_did_fill_from_scan)) {
new_pass = true; new_pass = true;
ret = 0; ret = 0;
} }
if (ret == DROP_THIS_NODE) { if (bch2_err_matches(ret, BCH_ERR_topology_repair_drop_this_node)) {
six_unlock_read(&cur->c.lock); six_unlock_read(&cur->c.lock);
bch2_btree_node_evict(trans, cur_k.k); bch2_btree_node_evict(trans, cur_k.k);
ret = bch2_journal_key_delete(c, b->c.btree_id, ret = bch2_journal_key_delete(c, b->c.btree_id,
@ -414,7 +410,7 @@ again:
six_unlock_read(&prev->c.lock); six_unlock_read(&prev->c.lock);
prev = NULL; prev = NULL;
if (ret == DROP_PREV_NODE) { if (bch2_err_matches(ret, BCH_ERR_topology_repair_drop_prev_node)) {
bch_info(c, "dropped prev node"); bch_info(c, "dropped prev node");
bch2_btree_node_evict(trans, prev_k.k); bch2_btree_node_evict(trans, prev_k.k);
ret = bch2_journal_key_delete(c, b->c.btree_id, ret = bch2_journal_key_delete(c, b->c.btree_id,
@ -436,7 +432,7 @@ again:
BUG_ON(cur); BUG_ON(cur);
ret = lockrestart_do(trans, ret = lockrestart_do(trans,
btree_repair_node_end(trans, b, prev, pulled_from_scan)); btree_repair_node_end(trans, b, prev, pulled_from_scan));
if (ret == DID_FILL_FROM_SCAN) { if (bch2_err_matches(ret, BCH_ERR_topology_repair_did_fill_from_scan)) {
new_pass = true; new_pass = true;
ret = 0; ret = 0;
} }
@ -477,7 +473,7 @@ again:
six_unlock_read(&cur->c.lock); six_unlock_read(&cur->c.lock);
cur = NULL; cur = NULL;
if (ret == DROP_THIS_NODE) { if (bch2_err_matches(ret, BCH_ERR_topology_repair_drop_this_node)) {
bch2_btree_node_evict(trans, cur_k.k); bch2_btree_node_evict(trans, cur_k.k);
ret = bch2_journal_key_delete(c, b->c.btree_id, ret = bch2_journal_key_delete(c, b->c.btree_id,
b->c.level, cur_k.k->k.p); b->c.level, cur_k.k->k.p);
@ -504,7 +500,7 @@ again:
if (mustfix_fsck_err_on(!have_child, if (mustfix_fsck_err_on(!have_child,
c, btree_node_topology_interior_node_empty, c, btree_node_topology_interior_node_empty,
"empty interior btree node at %s", buf.buf)) "empty interior btree node at %s", buf.buf))
ret = DROP_THIS_NODE; ret = bch_err_throw(c, topology_repair_drop_this_node);
err: err:
fsck_err: fsck_err:
if (!IS_ERR_OR_NULL(prev)) if (!IS_ERR_OR_NULL(prev))
@ -521,7 +517,8 @@ fsck_err:
bch2_bkey_buf_exit(&prev_k, c); bch2_bkey_buf_exit(&prev_k, c);
bch2_bkey_buf_exit(&cur_k, c); bch2_bkey_buf_exit(&cur_k, c);
bch_err_fn(c, ret); if (!bch2_err_matches(ret, BCH_ERR_topology_repair))
bch_err_fn(c, ret);
return ret; return ret;
} }
@ -592,7 +589,7 @@ recover:
ret = bch2_btree_repair_topology_recurse(trans, b, &pulled_from_scan); ret = bch2_btree_repair_topology_recurse(trans, b, &pulled_from_scan);
six_unlock_read(&b->c.lock); six_unlock_read(&b->c.lock);
if (ret == DROP_THIS_NODE) { if (bch2_err_matches(ret, BCH_ERR_topology_repair_drop_this_node)) {
scoped_guard(mutex, &c->btree_cache.lock) scoped_guard(mutex, &c->btree_cache.lock)
bch2_btree_node_hash_remove(&c->btree_cache, b); bch2_btree_node_hash_remove(&c->btree_cache, b);

View File

@ -2860,8 +2860,9 @@ struct bkey_s_c bch2_btree_iter_peek_slot(struct btree_trans *trans, struct btre
!bkey_deleted(k.k) && !bkey_deleted(k.k) &&
(k2 = btree_trans_peek_key_cache(trans, iter, iter->pos)).k) { (k2 = btree_trans_peek_key_cache(trans, iter, iter->pos)).k) {
k = k2; k = k2;
if (!bkey_err(k)) if (bkey_err(k))
iter->k = *k.k; goto out;
iter->k = *k.k;
} }
if (unlikely(k.k->type == KEY_TYPE_whiteout && if (unlikely(k.k->type == KEY_TYPE_whiteout &&

View File

@ -254,11 +254,13 @@ static int btree_key_cache_create(struct btree_trans *trans,
struct bkey_i *new_k = allocate_dropping_locks(trans, ret, struct bkey_i *new_k = allocate_dropping_locks(trans, ret,
kmalloc(key_u64s * sizeof(u64), _gfp)); kmalloc(key_u64s * sizeof(u64), _gfp));
if (unlikely(!new_k)) { if (unlikely(!new_k && !ret)) {
bch_err(trans->c, "error allocating memory for key cache key, btree %s u64s %u", 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); bch2_btree_id_str(ck->key.btree_id), key_u64s);
ret = bch_err_throw(c, ENOMEM_btree_key_cache_fill); ret = bch_err_throw(c, ENOMEM_btree_key_cache_fill);
} else if (ret) { }
if (unlikely(ret)) {
kfree(new_k); kfree(new_k);
goto err; goto err;
} }
@ -407,7 +409,7 @@ int bch2_btree_path_traverse_cached(struct btree_trans *trans,
btree_node_unlock(trans, path, 0); btree_node_unlock(trans, path, 0);
path->l[0].b = ERR_PTR(ret); path->l[0].b = ERR_PTR(ret);
} }
} else { } else if (!(flags & BTREE_ITER_cached_nofill)) {
BUG_ON(path->uptodate); BUG_ON(path->uptodate);
BUG_ON(!path->nodes_locked); BUG_ON(!path->nodes_locked);
} }

View File

@ -270,6 +270,9 @@ static int read_btree_nodes(struct find_btree_nodes *f)
int ret = 0; int ret = 0;
closure_init_stack(&cl); closure_init_stack(&cl);
CLASS(printbuf, buf)();
prt_printf(&buf, "scanning for btree nodes on");
for_each_online_member(c, ca, BCH_DEV_READ_REF_btree_node_scan) { for_each_online_member(c, ca, BCH_DEV_READ_REF_btree_node_scan) {
if (!(ca->mi.data_allowed & BIT(BCH_DATA_btree))) if (!(ca->mi.data_allowed & BIT(BCH_DATA_btree)))
@ -295,10 +298,14 @@ static int read_btree_nodes(struct find_btree_nodes *f)
break; break;
} }
prt_printf(&buf, " %s", ca->name);
closure_get(&cl); closure_get(&cl);
enumerated_ref_get(&ca->io_ref[READ], BCH_DEV_READ_REF_btree_node_scan); enumerated_ref_get(&ca->io_ref[READ], BCH_DEV_READ_REF_btree_node_scan);
wake_up_process(t); wake_up_process(t);
} }
bch_notice(c, "%s", buf.buf);
err: err:
while (closure_sync_timeout(&cl, sysctl_hung_task_timeout_secs * HZ / 2)) while (closure_sync_timeout(&cl, sysctl_hung_task_timeout_secs * HZ / 2))
; ;

View File

@ -772,12 +772,13 @@ bch2_trans_commit_write_locked(struct btree_trans *trans,
trans->journal_res.offset += trans->journal_entries.u64s; trans->journal_res.offset += trans->journal_entries.u64s;
trans->journal_res.u64s -= trans->journal_entries.u64s; trans->journal_res.u64s -= trans->journal_entries.u64s;
memcpy_u64s_small(bch2_journal_add_entry(j, &trans->journal_res, if (trans->accounting.u64s)
BCH_JSET_ENTRY_write_buffer_keys, memcpy_u64s_small(bch2_journal_add_entry(j, &trans->journal_res,
BTREE_ID_accounting, 0, BCH_JSET_ENTRY_write_buffer_keys,
trans->accounting.u64s)->_data, BTREE_ID_accounting, 0,
btree_trans_subbuf_base(trans, &trans->accounting), trans->accounting.u64s)->_data,
trans->accounting.u64s); btree_trans_subbuf_base(trans, &trans->accounting),
trans->accounting.u64s);
if (trans->journal_seq) if (trans->journal_seq)
*trans->journal_seq = trans->journal_res.seq; *trans->journal_seq = trans->journal_res.seq;
@ -1065,11 +1066,15 @@ int __bch2_trans_commit(struct btree_trans *trans, enum bch_trans_commit_flags f
EBUG_ON(test_bit(BCH_FS_clean_shutdown, &c->flags)); EBUG_ON(test_bit(BCH_FS_clean_shutdown, &c->flags));
journal_u64s = jset_u64s(trans->accounting.u64s); journal_u64s = 0;
trans->journal_transaction_names = READ_ONCE(c->opts.journal_transaction_names); trans->journal_transaction_names = READ_ONCE(c->opts.journal_transaction_names);
if (trans->journal_transaction_names) if (trans->journal_transaction_names)
journal_u64s += jset_u64s(JSET_ENTRY_LOG_U64s); journal_u64s += jset_u64s(JSET_ENTRY_LOG_U64s);
if (trans->accounting.u64s)
journal_u64s += jset_u64s(trans->accounting.u64s);
trans_for_each_update(trans, i) { trans_for_each_update(trans, i) {
struct btree_path *path = trans->paths + i->path; struct btree_path *path = trans->paths + i->path;

View File

@ -14,6 +14,7 @@
#include "btree_locking.h" #include "btree_locking.h"
#include "buckets.h" #include "buckets.h"
#include "clock.h" #include "clock.h"
#include "disk_groups.h"
#include "enumerated_ref.h" #include "enumerated_ref.h"
#include "error.h" #include "error.h"
#include "extents.h" #include "extents.h"
@ -277,6 +278,36 @@ static void bch2_btree_node_free_never_used(struct btree_update *as,
bch2_trans_node_drop(trans, b); bch2_trans_node_drop(trans, b);
} }
static bool can_use_btree_node(struct bch_fs *c,
struct disk_reservation *res,
unsigned target,
struct bkey_s_c k)
{
if (!bch2_bkey_devs_rw(c, k))
return false;
if (target && !bch2_bkey_in_target(c, k, target))
return false;
unsigned durability = bch2_bkey_durability(c, k);
if (durability >= res->nr_replicas)
return true;
struct bch_devs_mask devs = target_rw_devs(c, BCH_DATA_btree, target);
guard(rcu)();
unsigned durability_available = 0, i;
for_each_set_bit(i, devs.d, BCH_SB_MEMBERS_MAX) {
struct bch_dev *ca = bch2_dev_rcu_noerror(c, i);
if (ca)
durability_available += ca->mi.durability;
}
return durability >= durability_available;
}
static struct btree *__bch2_btree_node_alloc(struct btree_trans *trans, static struct btree *__bch2_btree_node_alloc(struct btree_trans *trans,
struct disk_reservation *res, struct disk_reservation *res,
struct closure *cl, struct closure *cl,
@ -303,10 +334,14 @@ static struct btree *__bch2_btree_node_alloc(struct btree_trans *trans,
mutex_lock(&c->btree_reserve_cache_lock); mutex_lock(&c->btree_reserve_cache_lock);
if (c->btree_reserve_cache_nr > nr_reserve) { if (c->btree_reserve_cache_nr > nr_reserve) {
for (struct btree_alloc *a = c->btree_reserve_cache; for (struct btree_alloc *a = c->btree_reserve_cache;
a < c->btree_reserve_cache + c->btree_reserve_cache_nr; a < c->btree_reserve_cache + c->btree_reserve_cache_nr;) {
a++) { /* check if it has sufficient durability */
if (target && !bch2_bkey_in_target(c, bkey_i_to_s_c(&a->k), target))
if (!can_use_btree_node(c, res, target, bkey_i_to_s_c(&a->k))) {
bch2_open_buckets_put(c, &a->ob);
*a = c->btree_reserve_cache[--c->btree_reserve_cache_nr];
continue; continue;
}
bkey_copy(&b->key, &a->k); bkey_copy(&b->key, &a->k);
b->ob = a->ob; b->ob = a->ob;

View File

@ -21,7 +21,7 @@ static const struct min_heap_callbacks callbacks = {
void bch2_io_timer_add(struct io_clock *clock, struct io_timer *timer) void bch2_io_timer_add(struct io_clock *clock, struct io_timer *timer)
{ {
guard(spinlock)(&clock->timer_lock); spin_lock(&clock->timer_lock);
if (time_after_eq64((u64) atomic64_read(&clock->now), timer->expire)) { if (time_after_eq64((u64) atomic64_read(&clock->now), timer->expire)) {
spin_unlock(&clock->timer_lock); spin_unlock(&clock->timer_lock);
@ -31,9 +31,11 @@ void bch2_io_timer_add(struct io_clock *clock, struct io_timer *timer)
for (size_t i = 0; i < clock->timers.nr; i++) for (size_t i = 0; i < clock->timers.nr; i++)
if (clock->timers.data[i] == timer) if (clock->timers.data[i] == timer)
return; goto out;
BUG_ON(!min_heap_push(&clock->timers, &timer, &callbacks, NULL)); BUG_ON(!min_heap_push(&clock->timers, &timer, &callbacks, NULL));
out:
spin_unlock(&clock->timer_lock);
} }
void bch2_io_timer_del(struct io_clock *clock, struct io_timer *timer) void bch2_io_timer_del(struct io_clock *clock, struct io_timer *timer)

View File

@ -462,7 +462,7 @@ static void bch2_cached_btree_node_to_text(struct printbuf *out, struct bch_fs *
struct btree *b) struct btree *b)
{ {
if (!out->nr_tabstops) if (!out->nr_tabstops)
printbuf_tabstop_push(out, 32); printbuf_tabstop_push(out, 36);
prt_printf(out, "%px ", b); prt_printf(out, "%px ", b);
bch2_btree_id_level_to_text(out, b->c.btree_id, b->c.level); bch2_btree_id_level_to_text(out, b->c.btree_id, b->c.level);

View File

@ -26,6 +26,13 @@ struct bch_inode_info;
#if IS_ENABLED(CONFIG_UNICODE) #if IS_ENABLED(CONFIG_UNICODE)
int bch2_casefold(struct btree_trans *, const struct bch_hash_info *, int bch2_casefold(struct btree_trans *, const struct bch_hash_info *,
const struct qstr *, struct qstr *); const struct qstr *, struct qstr *);
#else
static inline int bch2_casefold(struct btree_trans *trans, const struct bch_hash_info *info,
const struct qstr *str, struct qstr *out_cf)
{
return bch_err_throw(trans->c, no_casefolding_without_utf8);
}
#endif
static inline int bch2_maybe_casefold(struct btree_trans *trans, static inline int bch2_maybe_casefold(struct btree_trans *trans,
const struct bch_hash_info *info, const struct bch_hash_info *info,
@ -38,14 +45,6 @@ static inline int bch2_maybe_casefold(struct btree_trans *trans,
return bch2_casefold(trans, info, str, out_cf); return bch2_casefold(trans, info, str, out_cf);
} }
} }
#else
static inline int bch2_maybe_casefold(struct btree_trans *trans,
const struct bch_hash_info *info,
const struct qstr *str, struct qstr *out_cf)
{
return bch_err_throw(trans->c, no_casefolding_without_utf8);
}
#endif
struct qstr bch2_dirent_get_name(struct bkey_s_c_dirent); struct qstr bch2_dirent_get_name(struct bkey_s_c_dirent);

View File

@ -2060,6 +2060,9 @@ allocated:
BUG_ON(trans->restarted); BUG_ON(trans->restarted);
return h; return h;
err: err:
if (waiting &&
!bch2_err_matches(ret, BCH_ERR_operation_blocked))
closure_wake_up(&c->freelist_wait);
bch2_ec_stripe_head_put(c, h); bch2_ec_stripe_head_put(c, h);
return ERR_PTR(ret); return ERR_PTR(ret);
} }

View File

@ -90,6 +90,8 @@
x(ENOMEM, ENOMEM_disk_accounting) \ x(ENOMEM, ENOMEM_disk_accounting) \
x(ENOMEM, ENOMEM_stripe_head_alloc) \ x(ENOMEM, ENOMEM_stripe_head_alloc) \
x(ENOMEM, ENOMEM_journal_read_bucket) \ x(ENOMEM, ENOMEM_journal_read_bucket) \
x(ENOMEM, ENOMEM_acl) \
x(ENOMEM, ENOMEM_move_extent) \
x(ENOSPC, ENOSPC_disk_reservation) \ x(ENOSPC, ENOSPC_disk_reservation) \
x(ENOSPC, ENOSPC_bucket_alloc) \ x(ENOSPC, ENOSPC_bucket_alloc) \
x(ENOSPC, ENOSPC_disk_label_add) \ x(ENOSPC, ENOSPC_disk_label_add) \
@ -216,9 +218,13 @@
x(EINVAL, varint_decode_error) \ x(EINVAL, varint_decode_error) \
x(EINVAL, erasure_coding_found_btree_node) \ x(EINVAL, erasure_coding_found_btree_node) \
x(EINVAL, option_negative) \ x(EINVAL, option_negative) \
x(EINVAL, topology_repair) \
x(BCH_ERR_topology_repair, topology_repair_drop_this_node) \
x(BCH_ERR_topology_repair, topology_repair_drop_prev_node) \
x(BCH_ERR_topology_repair, topology_repair_did_fill_from_scan) \
x(EOPNOTSUPP, may_not_use_incompat_feature) \ x(EOPNOTSUPP, may_not_use_incompat_feature) \
x(EOPNOTSUPP, no_casefolding_without_utf8) \ x(EOPNOTSUPP, no_casefolding_without_utf8) \
x(EOPNOTSUPP, casefolding_disabled) \ x(EOPNOTSUPP, casefolding_disabled) \
x(EOPNOTSUPP, casefold_opt_is_dir_only) \ x(EOPNOTSUPP, casefold_opt_is_dir_only) \
x(EOPNOTSUPP, unsupported_fsx_flag) \ x(EOPNOTSUPP, unsupported_fsx_flag) \
x(EOPNOTSUPP, unsupported_fa_flag) \ x(EOPNOTSUPP, unsupported_fa_flag) \

View File

@ -1006,6 +1006,20 @@ const struct bch_extent_ptr *bch2_bkey_has_device_c(struct bkey_s_c k, unsigned
return NULL; return NULL;
} }
bool bch2_bkey_devs_rw(struct bch_fs *c, struct bkey_s_c k)
{
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
guard(rcu)();
bkey_for_each_ptr(ptrs, ptr) {
CLASS(bch2_dev_tryget, ca)(c, ptr->dev);
if (!ca || ca->mi.state != BCH_MEMBER_STATE_rw)
return false;
}
return true;
}
bool bch2_bkey_has_target(struct bch_fs *c, struct bkey_s_c k, unsigned target) bool bch2_bkey_has_target(struct bch_fs *c, struct bkey_s_c k, unsigned target)
{ {
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);

View File

@ -614,6 +614,8 @@ static inline struct bch_extent_ptr *bch2_bkey_has_device(struct bkey_s k, unsig
return (void *) bch2_bkey_has_device_c(k.s_c, dev); return (void *) bch2_bkey_has_device_c(k.s_c, dev);
} }
bool bch2_bkey_devs_rw(struct bch_fs *, struct bkey_s_c);
bool bch2_bkey_has_target(struct bch_fs *, struct bkey_s_c, unsigned); bool bch2_bkey_has_target(struct bch_fs *, struct bkey_s_c, unsigned);
bool bch2_bkey_in_target(struct bch_fs *, struct bkey_s_c, unsigned); bool bch2_bkey_in_target(struct bch_fs *, struct bkey_s_c, unsigned);

View File

@ -127,7 +127,7 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
* the dirtying of requests that are internal from the kernel (i.e. from * the dirtying of requests that are internal from the kernel (i.e. from
* loopback), because we'll deadlock on page_lock. * loopback), because we'll deadlock on page_lock.
*/ */
dio->should_dirty = iter_is_iovec(iter); dio->should_dirty = user_backed_iter(iter);
blk_start_plug(&plug); blk_start_plug(&plug);

View File

@ -223,9 +223,8 @@ static int bch2_flush_inode(struct bch_fs *c,
if (!enumerated_ref_tryget(&c->writes, BCH_WRITE_REF_fsync)) if (!enumerated_ref_tryget(&c->writes, BCH_WRITE_REF_fsync))
return -EROFS; return -EROFS;
CLASS(btree_trans, trans)(c);
u64 seq; u64 seq;
int ret = commit_do(trans, NULL, NULL, 0, int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_get_inode_journal_seq_trans(trans, inode_inum(inode), &seq)) ?: bch2_get_inode_journal_seq_trans(trans, inode_inum(inode), &seq)) ?:
bch2_journal_flush_seq(&c->journal, seq, TASK_INTERRUPTIBLE) ?: bch2_journal_flush_seq(&c->journal, seq, TASK_INTERRUPTIBLE) ?:
bch2_inode_flush_nocow_writes(c, inode); bch2_inode_flush_nocow_writes(c, inode);

View File

@ -1295,8 +1295,14 @@ static int bch2_fill_extent(struct bch_fs *c,
flags| flags|
FIEMAP_EXTENT_DELALLOC| FIEMAP_EXTENT_DELALLOC|
FIEMAP_EXTENT_UNWRITTEN); FIEMAP_EXTENT_UNWRITTEN);
} else if (k.k->type == KEY_TYPE_error) {
return 0;
} else { } else {
BUG(); WARN_ONCE(1, "unhandled key type %s",
k.k->type < KEY_TYPE_MAX
? bch2_bkey_types[k.k->type]
: "(unknown)");
return 0;
} }
} }

View File

@ -1975,6 +1975,10 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
} }
} }
ret = check_extent_overbig(trans, iter, k);
if (ret)
goto err;
ret = bch2_trans_commit(trans, res, NULL, BCH_TRANS_COMMIT_no_enospc); ret = bch2_trans_commit(trans, res, NULL, BCH_TRANS_COMMIT_no_enospc);
if (ret) if (ret)
goto err; goto err;
@ -2021,8 +2025,7 @@ int bch2_check_extents(struct bch_fs *c)
POS(BCACHEFS_ROOT_INO, 0), POS(BCACHEFS_ROOT_INO, 0),
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, ({ BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, ({
bch2_disk_reservation_put(c, &res); bch2_disk_reservation_put(c, &res);
check_extent(trans, &iter, k, &w, &s, &extent_ends, &res) ?: check_extent(trans, &iter, k, &w, &s, &extent_ends, &res);
check_extent_overbig(trans, &iter, k);
})) ?: })) ?:
check_i_sectors_notnested(trans, &w); check_i_sectors_notnested(trans, &w);

View File

@ -182,6 +182,8 @@ journal_error_check_stuck(struct journal *j, int error, unsigned flags)
void bch2_journal_do_writes(struct journal *j) void bch2_journal_do_writes(struct journal *j)
{ {
struct bch_fs *c = container_of(j, struct bch_fs, journal);
for (u64 seq = journal_last_unwritten_seq(j); for (u64 seq = journal_last_unwritten_seq(j);
seq <= journal_cur_seq(j); seq <= journal_cur_seq(j);
seq++) { seq++) {
@ -196,6 +198,7 @@ void bch2_journal_do_writes(struct journal *j)
if (!journal_state_seq_count(j, j->reservations, seq)) { if (!journal_state_seq_count(j, j->reservations, seq)) {
j->seq_write_started = seq; j->seq_write_started = seq;
w->write_started = true; w->write_started = true;
closure_get(&c->cl);
closure_call(&w->io, bch2_journal_write, j->wq, NULL); closure_call(&w->io, bch2_journal_write, j->wq, NULL);
} }
@ -1063,6 +1066,8 @@ static struct journal_buf *__bch2_next_write_buffer_flush_journal_buf(struct jou
ret = journal_state_count(s, idx & JOURNAL_STATE_BUF_MASK) > open ret = journal_state_count(s, idx & JOURNAL_STATE_BUF_MASK) > open
? ERR_PTR(-EAGAIN) ? ERR_PTR(-EAGAIN)
: buf; : buf;
if (!ret)
smp_mb();
break; break;
} }
} }

View File

@ -267,7 +267,7 @@ static inline union journal_res_state journal_state_buf_put(struct journal *j, u
{ {
union journal_res_state s; union journal_res_state s;
s.v = atomic64_sub_return(((union journal_res_state) { s.v = atomic64_sub_return_release(((union journal_res_state) {
.buf0_count = idx == 0, .buf0_count = idx == 0,
.buf1_count = idx == 1, .buf1_count = idx == 1,
.buf2_count = idx == 2, .buf2_count = idx == 2,

View File

@ -1820,6 +1820,8 @@ static CLOSURE_CALLBACK(journal_write_done)
if (do_discards) if (do_discards)
bch2_do_discards(c); bch2_do_discards(c);
closure_put(&c->cl);
} }
static void journal_write_endio(struct bio *bio) static void journal_write_endio(struct bio *bio)

View File

@ -874,7 +874,34 @@ static int journal_flush_done(struct journal *j, u64 seq_to_flush,
--type) --type)
if (journal_flush_pins_or_still_flushing(j, seq_to_flush, BIT(type))) { if (journal_flush_pins_or_still_flushing(j, seq_to_flush, BIT(type))) {
*did_work = true; *did_work = true;
return ret;
/*
* Question from Dan Carpenter, on the early return:
*
* If journal_flush_pins_or_still_flushing() returns
* true, then the flush hasn't complete and we must
* return 0; we want the outer closure_wait_event() in
* journal_flush_pins() to continue.
*
* The early return is there because we don't want to
* call journal_entry_close() until we've finished
* flushing all outstanding journal pins - otherwise
* seq_to_flush can be U64_MAX, and we'll close a bunch
* of journal entries and write tiny ones completely
* unnecessarily.
*
* Having the early return be in the loop where we loop
* over types is important, because flushing one journal
* pin can cause new journal pins to be added (even of
* the same type, btree node writes may generate more
* btree node writes, when updating the parent pointer
* has a full node and has to trigger a split/compact).
*
* This is part of our shutdown sequence, where order of
* flushing is important in order to make sure that it
* terminates...
*/
return 0;
} }
if (seq_to_flush > journal_cur_seq(j)) if (seq_to_flush > journal_cur_seq(j))

View File

@ -330,7 +330,7 @@ int bch2_move_extent(struct moving_context *ctxt,
{ {
struct btree_trans *trans = ctxt->trans; struct btree_trans *trans = ctxt->trans;
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
int ret = -ENOMEM; int ret = 0;
if (trace_io_move_enabled()) if (trace_io_move_enabled())
trace_io_move2(c, k, &io_opts, &data_opts); trace_io_move2(c, k, &io_opts, &data_opts);
@ -351,11 +351,10 @@ int bch2_move_extent(struct moving_context *ctxt,
struct moving_io *io = allocate_dropping_locks(trans, ret, struct moving_io *io = allocate_dropping_locks(trans, ret,
kzalloc(sizeof(struct moving_io), _gfp)); kzalloc(sizeof(struct moving_io), _gfp));
if (!io) if (!io && !ret)
goto err; ret = bch_err_throw(c, ENOMEM_move_extent);
if (ret) if (ret)
goto err_free; goto err;
INIT_LIST_HEAD(&io->io_list); INIT_LIST_HEAD(&io->io_list);
io->write.ctxt = ctxt; io->write.ctxt = ctxt;
@ -366,7 +365,7 @@ int bch2_move_extent(struct moving_context *ctxt,
ret = bch2_data_update_init(trans, iter, ctxt, &io->write, ctxt->wp, ret = bch2_data_update_init(trans, iter, ctxt, &io->write, ctxt->wp,
&io_opts, data_opts, iter->btree_id, k); &io_opts, data_opts, iter->btree_id, k);
if (ret) if (ret)
goto err_free; goto err;
io->write.op.end_io = move_write_done; io->write.op.end_io = move_write_done;
} else { } else {
@ -380,7 +379,7 @@ int bch2_move_extent(struct moving_context *ctxt,
ret = bch2_data_update_bios_init(&io->write, c, &io_opts); ret = bch2_data_update_bios_init(&io->write, c, &io_opts);
if (ret) if (ret)
goto err_free; goto err;
} }
io->write.rbio.bio.bi_end_io = move_read_endio; io->write.rbio.bio.bi_end_io = move_read_endio;
@ -423,9 +422,8 @@ int bch2_move_extent(struct moving_context *ctxt,
BCH_READ_last_fragment, BCH_READ_last_fragment,
data_opts.scrub ? data_opts.read_dev : -1); data_opts.scrub ? data_opts.read_dev : -1);
return 0; return 0;
err_free:
kfree(io);
err: err:
kfree(io);
if (bch2_err_matches(ret, EROFS) || if (bch2_err_matches(ret, EROFS) ||
bch2_err_matches(ret, BCH_ERR_transaction_restart)) bch2_err_matches(ret, BCH_ERR_transaction_restart))
return ret; return ret;
@ -795,50 +793,50 @@ out:
return ret; return ret;
} }
int __bch2_move_data(struct moving_context *ctxt, static int bch2_move_data(struct bch_fs *c,
struct bbpos start, struct bbpos start,
struct bbpos end, struct bbpos end,
move_pred_fn pred, void *arg) unsigned min_depth,
struct bch_ratelimit *rate,
struct bch_move_stats *stats,
struct write_point_specifier wp,
bool wait_on_copygc,
move_pred_fn pred, void *arg)
{ {
struct bch_fs *c = ctxt->trans->c;
enum btree_id id;
int ret = 0; int ret = 0;
for (id = start.btree; struct moving_context ctxt;
bch2_moving_ctxt_init(&ctxt, c, rate, stats, wp, wait_on_copygc);
for (enum btree_id id = start.btree;
id <= min_t(unsigned, end.btree, btree_id_nr_alive(c) - 1); id <= min_t(unsigned, end.btree, btree_id_nr_alive(c) - 1);
id++) { id++) {
ctxt->stats->pos = BBPOS(id, POS_MIN); ctxt.stats->pos = BBPOS(id, POS_MIN);
if (!btree_type_has_ptrs(id) || if (!bch2_btree_id_root(c, id)->b)
!bch2_btree_id_root(c, id)->b)
continue; continue;
ret = bch2_move_data_btree(ctxt, unsigned min_depth_this_btree = min_depth;
id == start.btree ? start.pos : POS_MIN,
id == end.btree ? end.pos : POS_MAX, if (!btree_type_has_ptrs(id))
pred, arg, id, 0); min_depth_this_btree = max(min_depth_this_btree, 1);
for (unsigned level = min_depth_this_btree;
level < BTREE_MAX_DEPTH;
level++) {
ret = bch2_move_data_btree(&ctxt,
id == start.btree ? start.pos : POS_MIN,
id == end.btree ? end.pos : POS_MAX,
pred, arg, id, level);
if (ret)
break;
}
if (ret) if (ret)
break; break;
} }
return ret;
}
int bch2_move_data(struct bch_fs *c,
struct bbpos start,
struct bbpos end,
struct bch_ratelimit *rate,
struct bch_move_stats *stats,
struct write_point_specifier wp,
bool wait_on_copygc,
move_pred_fn pred, void *arg)
{
struct moving_context ctxt;
bch2_moving_ctxt_init(&ctxt, c, rate, stats, wp, wait_on_copygc);
int ret = __bch2_move_data(&ctxt, start, end, pred, arg);
bch2_moving_ctxt_exit(&ctxt); bch2_moving_ctxt_exit(&ctxt);
return ret; return ret;
} }
@ -1206,14 +1204,6 @@ static bool migrate_pred(struct bch_fs *c, void *arg,
return data_opts->rewrite_ptrs != 0; return data_opts->rewrite_ptrs != 0;
} }
static bool rereplicate_btree_pred(struct bch_fs *c, void *arg,
struct btree *b,
struct bch_io_opts *io_opts,
struct data_update_opts *data_opts)
{
return rereplicate_pred(c, arg, b->c.btree_id, bkey_i_to_s_c(&b->key), io_opts, data_opts);
}
/* /*
* Ancient versions of bcachefs produced packed formats which could represent * Ancient versions of bcachefs produced packed formats which could represent
* keys that the in memory format cannot represent; this checks for those * keys that the in memory format cannot represent; this checks for those
@ -1293,15 +1283,6 @@ static bool drop_extra_replicas_pred(struct bch_fs *c, void *arg,
return data_opts->kill_ptrs != 0; return data_opts->kill_ptrs != 0;
} }
static bool drop_extra_replicas_btree_pred(struct bch_fs *c, void *arg,
struct btree *b,
struct bch_io_opts *io_opts,
struct data_update_opts *data_opts)
{
return drop_extra_replicas_pred(c, arg, b->c.btree_id, bkey_i_to_s_c(&b->key),
io_opts, data_opts);
}
static bool scrub_pred(struct bch_fs *c, void *_arg, static bool scrub_pred(struct bch_fs *c, void *_arg,
enum btree_id btree, struct bkey_s_c k, enum btree_id btree, struct bkey_s_c k,
struct bch_io_opts *io_opts, struct bch_io_opts *io_opts,
@ -1359,14 +1340,11 @@ int bch2_data_job(struct bch_fs *c,
case BCH_DATA_OP_rereplicate: case BCH_DATA_OP_rereplicate:
stats->data_type = BCH_DATA_journal; stats->data_type = BCH_DATA_journal;
ret = bch2_journal_flush_device_pins(&c->journal, -1); ret = bch2_journal_flush_device_pins(&c->journal, -1);
ret = bch2_move_btree(c, start, end, ret = bch2_move_data(c, start, end, 0, NULL, stats,
rereplicate_btree_pred, c, stats) ?: ret;
ret = bch2_move_data(c, start, end,
NULL,
stats,
writepoint_hashed((unsigned long) current), writepoint_hashed((unsigned long) current),
true, true,
rereplicate_pred, c) ?: ret; rereplicate_pred, c) ?: ret;
bch2_btree_interior_updates_flush(c);
ret = bch2_replicas_gc2(c) ?: ret; ret = bch2_replicas_gc2(c) ?: ret;
break; break;
case BCH_DATA_OP_migrate: case BCH_DATA_OP_migrate:
@ -1389,12 +1367,10 @@ int bch2_data_job(struct bch_fs *c,
ret = bch2_scan_old_btree_nodes(c, stats); ret = bch2_scan_old_btree_nodes(c, stats);
break; break;
case BCH_DATA_OP_drop_extra_replicas: case BCH_DATA_OP_drop_extra_replicas:
ret = bch2_move_btree(c, start, end, ret = bch2_move_data(c, start, end, 0, NULL, stats,
drop_extra_replicas_btree_pred, c, stats) ?: ret; writepoint_hashed((unsigned long) current),
ret = bch2_move_data(c, start, end, NULL, stats, true,
writepoint_hashed((unsigned long) current), drop_extra_replicas_pred, c) ?: ret;
true,
drop_extra_replicas_pred, c) ?: ret;
ret = bch2_replicas_gc2(c) ?: ret; ret = bch2_replicas_gc2(c) ?: ret;
break; break;
default: default:

View File

@ -128,18 +128,6 @@ struct bch_io_opts *bch2_move_get_io_opts(struct btree_trans *,
int bch2_move_data_btree(struct moving_context *, struct bpos, struct bpos, int bch2_move_data_btree(struct moving_context *, struct bpos, struct bpos,
move_pred_fn, void *, enum btree_id, unsigned); move_pred_fn, void *, enum btree_id, unsigned);
int __bch2_move_data(struct moving_context *,
struct bbpos,
struct bbpos,
move_pred_fn, void *);
int bch2_move_data(struct bch_fs *,
struct bbpos start,
struct bbpos end,
struct bch_ratelimit *,
struct bch_move_stats *,
struct write_point_specifier,
bool,
move_pred_fn, void *);
int bch2_move_data_phys(struct bch_fs *, unsigned, u64, u64, unsigned, int bch2_move_data_phys(struct bch_fs *, unsigned, u64, u64, unsigned,
struct bch_ratelimit *, struct bch_move_stats *, struct bch_ratelimit *, struct bch_move_stats *,

View File

@ -37,78 +37,79 @@ int bch2_btree_lost_data(struct bch_fs *c,
struct printbuf *msg, struct printbuf *msg,
enum btree_id btree) enum btree_id btree)
{ {
u64 b = BIT_ULL(btree);
int ret = 0; int ret = 0;
guard(mutex)(&c->sb_lock); guard(mutex)(&c->sb_lock);
bool write_sb = false;
struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext); struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
if (!(c->sb.btrees_lost_data & b)) { if (!(c->sb.btrees_lost_data & BIT_ULL(btree))) {
prt_printf(msg, "flagging btree "); prt_printf(msg, "flagging btree ");
bch2_btree_id_to_text(msg, btree); bch2_btree_id_to_text(msg, btree);
prt_printf(msg, " lost data\n"); prt_printf(msg, " lost data\n");
ext->btrees_lost_data |= cpu_to_le64(b); write_sb |= !__test_and_set_bit_le64(btree, &ext->btrees_lost_data);
} }
/* Once we have runtime self healing for topology errors we won't need this: */ /* Once we have runtime self healing for topology errors we won't need this: */
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0, &write_sb) ?: ret;
/* Btree node accounting will be off: */ /* Btree node accounting will be off: */
__set_bit_le64(BCH_FSCK_ERR_accounting_mismatch, ext->errors_silent); write_sb |= !__test_and_set_bit_le64(BCH_FSCK_ERR_accounting_mismatch, ext->errors_silent);
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_allocations, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_allocations, 0, &write_sb) ?: ret;
#ifdef CONFIG_BCACHEFS_DEBUG #ifdef CONFIG_BCACHEFS_DEBUG
/* /*
* These are much more minor, and don't need to be corrected right away, * These are much more minor, and don't need to be corrected right away,
* but in debug mode we want the next fsck run to be clean: * but in debug mode we want the next fsck run to be clean:
*/ */
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_lrus, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_lrus, 0, &write_sb) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_backpointers_to_extents, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_backpointers_to_extents, 0, &write_sb) ?: ret;
#endif #endif
switch (btree) { switch (btree) {
case BTREE_ID_alloc: case BTREE_ID_alloc:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0, &write_sb) ?: ret;
__set_bit_le64(BCH_FSCK_ERR_alloc_key_data_type_wrong, ext->errors_silent); write_sb |= !__test_and_set_bit_le64(BCH_FSCK_ERR_alloc_key_data_type_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_gen_wrong, ext->errors_silent); write_sb |= !__test_and_set_bit_le64(BCH_FSCK_ERR_alloc_key_gen_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_dirty_sectors_wrong, ext->errors_silent); write_sb |= !__test_and_set_bit_le64(BCH_FSCK_ERR_alloc_key_dirty_sectors_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_cached_sectors_wrong, ext->errors_silent); write_sb |= !__test_and_set_bit_le64(BCH_FSCK_ERR_alloc_key_cached_sectors_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_stripe_wrong, ext->errors_silent); write_sb |= !__test_and_set_bit_le64(BCH_FSCK_ERR_alloc_key_stripe_wrong, ext->errors_silent);
__set_bit_le64(BCH_FSCK_ERR_alloc_key_stripe_redundancy_wrong, ext->errors_silent); write_sb |= !__test_and_set_bit_le64(BCH_FSCK_ERR_alloc_key_stripe_redundancy_wrong, ext->errors_silent);
goto out; goto out;
case BTREE_ID_backpointers: case BTREE_ID_backpointers:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_btree_backpointers, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_btree_backpointers, 0, &write_sb) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_extents_to_backpointers, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_extents_to_backpointers, 0, &write_sb) ?: ret;
goto out; goto out;
case BTREE_ID_need_discard: case BTREE_ID_need_discard:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0, &write_sb) ?: ret;
goto out; goto out;
case BTREE_ID_freespace: case BTREE_ID_freespace:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0, &write_sb) ?: ret;
goto out; goto out;
case BTREE_ID_bucket_gens: case BTREE_ID_bucket_gens:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0, &write_sb) ?: ret;
goto out; goto out;
case BTREE_ID_lru: case BTREE_ID_lru:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_alloc_info, 0, &write_sb) ?: ret;
goto out; goto out;
case BTREE_ID_accounting: case BTREE_ID_accounting:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_allocations, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_allocations, 0, &write_sb) ?: ret;
goto out; goto out;
case BTREE_ID_snapshots: case BTREE_ID_snapshots:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_reconstruct_snapshots, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_reconstruct_snapshots, 0, &write_sb) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0, &write_sb) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_scan_for_btree_nodes, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_scan_for_btree_nodes, 0, &write_sb) ?: ret;
goto out; goto out;
default: default:
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_check_topology, 0, &write_sb) ?: ret;
ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_scan_for_btree_nodes, 0) ?: ret; ret = __bch2_run_explicit_recovery_pass(c, msg, BCH_RECOVERY_PASS_scan_for_btree_nodes, 0, &write_sb) ?: ret;
goto out; goto out;
} }
out: out:
bch2_write_super(c); if (write_sb)
bch2_write_super(c);
return ret; return ret;
} }

View File

@ -340,7 +340,8 @@ static bool recovery_pass_needs_set(struct bch_fs *c,
int __bch2_run_explicit_recovery_pass(struct bch_fs *c, int __bch2_run_explicit_recovery_pass(struct bch_fs *c,
struct printbuf *out, struct printbuf *out,
enum bch_recovery_pass pass, enum bch_recovery_pass pass,
enum bch_run_recovery_pass_flags flags) enum bch_run_recovery_pass_flags flags,
bool *write_sb)
{ {
struct bch_fs_recovery *r = &c->recovery; struct bch_fs_recovery *r = &c->recovery;
int ret = 0; int ret = 0;
@ -362,7 +363,8 @@ int __bch2_run_explicit_recovery_pass(struct bch_fs *c,
if (!(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); 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); *write_sb |= !__test_and_set_bit_le64(bch2_recovery_pass_to_stable(pass),
ext->recovery_passes_required);
} }
if (pass < BCH_RECOVERY_PASS_set_may_go_rw && if (pass < BCH_RECOVERY_PASS_set_may_go_rw &&
@ -408,14 +410,19 @@ int bch2_run_explicit_recovery_pass(struct bch_fs *c,
enum bch_recovery_pass pass, enum bch_recovery_pass pass,
enum bch_run_recovery_pass_flags flags) enum bch_run_recovery_pass_flags flags)
{ {
int ret = 0; /*
* With RUN_RECOVERY_PASS_ratelimit, recovery_pass_needs_set needs
* sb_lock
*/
if (!(flags & RUN_RECOVERY_PASS_ratelimit) &&
!recovery_pass_needs_set(c, pass, &flags))
return 0;
if (recovery_pass_needs_set(c, pass, &flags)) { guard(mutex)(&c->sb_lock);
guard(mutex)(&c->sb_lock); bool write_sb = false;
ret = __bch2_run_explicit_recovery_pass(c, out, pass, flags); int ret = __bch2_run_explicit_recovery_pass(c, out, pass, flags, &write_sb);
if (write_sb)
bch2_write_super(c); bch2_write_super(c);
}
return ret; return ret;
} }
@ -438,14 +445,13 @@ int bch2_require_recovery_pass(struct bch_fs *c,
return 0; return 0;
enum bch_run_recovery_pass_flags flags = 0; enum bch_run_recovery_pass_flags flags = 0;
int ret = 0;
if (recovery_pass_needs_set(c, pass, &flags)) { bool write_sb = false;
ret = __bch2_run_explicit_recovery_pass(c, out, pass, flags); int ret = __bch2_run_explicit_recovery_pass(c, out, pass, flags, &write_sb) ?:
bch_err_throw(c, recovery_pass_will_run);
if (write_sb)
bch2_write_super(c); bch2_write_super(c);
} return ret;
return ret ?: bch_err_throw(c, recovery_pass_will_run);
} }
int bch2_run_print_explicit_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pass) int bch2_run_print_explicit_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pass)
@ -459,8 +465,10 @@ int bch2_run_print_explicit_recovery_pass(struct bch_fs *c, enum bch_recovery_pa
bch2_log_msg_start(c, &buf); bch2_log_msg_start(c, &buf);
guard(mutex)(&c->sb_lock); guard(mutex)(&c->sb_lock);
bool write_sb = false;
int ret = __bch2_run_explicit_recovery_pass(c, &buf, pass, int ret = __bch2_run_explicit_recovery_pass(c, &buf, pass,
RUN_RECOVERY_PASS_nopersistent); RUN_RECOVERY_PASS_nopersistent,
&write_sb);
bch2_print_str(c, KERN_NOTICE, buf.buf); bch2_print_str(c, KERN_NOTICE, buf.buf);
return ret; return ret;

View File

@ -30,7 +30,8 @@ int bch2_run_print_explicit_recovery_pass(struct bch_fs *, enum bch_recovery_pas
int __bch2_run_explicit_recovery_pass(struct bch_fs *, struct printbuf *, int __bch2_run_explicit_recovery_pass(struct bch_fs *, struct printbuf *,
enum bch_recovery_pass, enum bch_recovery_pass,
enum bch_run_recovery_pass_flags); enum bch_run_recovery_pass_flags,
bool *);
int bch2_run_explicit_recovery_pass(struct bch_fs *, struct printbuf *, int bch2_run_explicit_recovery_pass(struct bch_fs *, struct printbuf *,
enum bch_recovery_pass, enum bch_recovery_pass,
enum bch_run_recovery_pass_flags); enum bch_run_recovery_pass_flags);

View File

@ -329,7 +329,6 @@ duplicate_entries:
out: out:
fsck_err: fsck_err:
bch2_trans_iter_exit(trans, dup_iter); bch2_trans_iter_exit(trans, dup_iter);
printbuf_exit(&buf);
if (free_snapshots_seen) if (free_snapshots_seen)
darray_exit(&s->ids); darray_exit(&s->ids);
return ret; return ret;

View File

@ -514,6 +514,10 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
if (ret) if (ret)
return ret; return ret;
ret = bch2_fs_mark_dirty(c);
if (ret)
return ret;
clear_bit(BCH_FS_clean_shutdown, &c->flags); clear_bit(BCH_FS_clean_shutdown, &c->flags);
scoped_guard(rcu) scoped_guard(rcu)
@ -537,10 +541,6 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
bch2_journal_space_available(&c->journal); bch2_journal_space_available(&c->journal);
} }
ret = bch2_fs_mark_dirty(c);
if (ret)
return ret;
/* /*
* Don't jump to our error path, and call bch2_fs_read_only(), unless we * Don't jump to our error path, and call bch2_fs_read_only(), unless we
* successfully marked the filesystem dirty * successfully marked the filesystem dirty

View File

@ -18,6 +18,7 @@
#include "btree_key_cache.h" #include "btree_key_cache.h"
#include "btree_update.h" #include "btree_update.h"
#include "btree_update_interior.h" #include "btree_update_interior.h"
#include "btree_write_buffer.h"
#include "btree_gc.h" #include "btree_gc.h"
#include "buckets.h" #include "buckets.h"
#include "clock.h" #include "clock.h"
@ -150,6 +151,7 @@ write_attribute(trigger_journal_flush);
write_attribute(trigger_journal_writes); write_attribute(trigger_journal_writes);
write_attribute(trigger_btree_cache_shrink); write_attribute(trigger_btree_cache_shrink);
write_attribute(trigger_btree_key_cache_shrink); write_attribute(trigger_btree_key_cache_shrink);
write_attribute(trigger_btree_write_buffer_flush);
write_attribute(trigger_btree_updates); write_attribute(trigger_btree_updates);
write_attribute(trigger_freelist_wakeup); write_attribute(trigger_freelist_wakeup);
write_attribute(trigger_recalc_capacity); write_attribute(trigger_recalc_capacity);
@ -539,6 +541,11 @@ STORE(bch2_fs)
c->btree_key_cache.shrink->scan_objects(c->btree_key_cache.shrink, &sc); c->btree_key_cache.shrink->scan_objects(c->btree_key_cache.shrink, &sc);
} }
if (attr == &sysfs_trigger_btree_write_buffer_flush)
bch2_trans_do(c,
(bch2_btree_write_buffer_flush_sync(trans),
bch2_trans_begin(trans)));
if (attr == &sysfs_trigger_gc) if (attr == &sysfs_trigger_gc)
bch2_gc_gens(c); bch2_gc_gens(c);
@ -709,6 +716,7 @@ struct attribute *bch2_fs_internal_files[] = {
&sysfs_trigger_journal_writes, &sysfs_trigger_journal_writes,
&sysfs_trigger_btree_cache_shrink, &sysfs_trigger_btree_cache_shrink,
&sysfs_trigger_btree_key_cache_shrink, &sysfs_trigger_btree_key_cache_shrink,
&sysfs_trigger_btree_write_buffer_flush,
&sysfs_trigger_btree_updates, &sysfs_trigger_btree_updates,
&sysfs_trigger_freelist_wakeup, &sysfs_trigger_freelist_wakeup,
&sysfs_trigger_recalc_capacity, &sysfs_trigger_recalc_capacity,

View File

@ -733,6 +733,13 @@ static inline bool test_bit_le64(size_t bit, __le64 *addr)
return (addr[bit / 64] & cpu_to_le64(BIT_ULL(bit % 64))) != 0; return (addr[bit / 64] & cpu_to_le64(BIT_ULL(bit % 64))) != 0;
} }
static inline bool __test_and_set_bit_le64(size_t bit, __le64 *addr)
{
bool ret = test_bit_le64(bit, addr);
__set_bit_le64(bit, addr);
return ret;
}
static inline void memcpy_swab(void *_dst, void *_src, size_t len) static inline void memcpy_swab(void *_dst, void *_src, size_t len)
{ {
u8 *dst = _dst + len; u8 *dst = _dst + len;

View File

@ -13,23 +13,25 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/sched/debug.h> #include <linux/sched/debug.h>
static inline void closure_put_after_sub_checks(int flags) static inline void closure_put_after_sub_checks(struct closure *cl, int flags)
{ {
int r = flags & CLOSURE_REMAINING_MASK; int r = flags & CLOSURE_REMAINING_MASK;
if (WARN(flags & CLOSURE_GUARD_MASK, if (WARN(flags & CLOSURE_GUARD_MASK,
"closure has guard bits set: %x (%u)", "closure %ps has guard bits set: %x (%u)",
cl->fn,
flags & CLOSURE_GUARD_MASK, (unsigned) __fls(r))) flags & CLOSURE_GUARD_MASK, (unsigned) __fls(r)))
r &= ~CLOSURE_GUARD_MASK; r &= ~CLOSURE_GUARD_MASK;
WARN(!r && (flags & ~CLOSURE_DESTRUCTOR), WARN(!r && (flags & ~CLOSURE_DESTRUCTOR),
"closure ref hit 0 with incorrect flags set: %x (%u)", "closure %ps ref hit 0 with incorrect flags set: %x (%u)",
cl->fn,
flags & ~CLOSURE_DESTRUCTOR, (unsigned) __fls(flags)); flags & ~CLOSURE_DESTRUCTOR, (unsigned) __fls(flags));
} }
static inline void closure_put_after_sub(struct closure *cl, int flags) static inline void closure_put_after_sub(struct closure *cl, int flags)
{ {
closure_put_after_sub_checks(flags); closure_put_after_sub_checks(cl, flags);
if (!(flags & CLOSURE_REMAINING_MASK)) { if (!(flags & CLOSURE_REMAINING_MASK)) {
smp_acquire__after_ctrl_dep(); smp_acquire__after_ctrl_dep();
@ -167,7 +169,7 @@ void __sched closure_return_sync(struct closure *cl)
unsigned flags = atomic_sub_return_release(1 + CLOSURE_RUNNING - CLOSURE_DESTRUCTOR, unsigned flags = atomic_sub_return_release(1 + CLOSURE_RUNNING - CLOSURE_DESTRUCTOR,
&cl->remaining); &cl->remaining);
closure_put_after_sub_checks(flags); closure_put_after_sub_checks(cl, flags);
if (unlikely(flags & CLOSURE_REMAINING_MASK)) { if (unlikely(flags & CLOSURE_REMAINING_MASK)) {
while (1) { while (1) {