bcachefs: Rewrite btree nodes with errors

This patch adds self healing functionality for btree nodes - if we
notice a problem when reading a btree node, we just rewrite it.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2021-04-24 02:47:41 -04:00 committed by Kent Overstreet
parent 8058b532ac
commit bcd25dac53
3 changed files with 58 additions and 0 deletions

View File

@ -986,6 +986,7 @@ static void btree_node_read_work(struct work_struct *work)
struct bch_io_failures failed = { .nr = 0 };
char buf[200];
struct printbuf out;
bool saw_error = false;
bool can_retry;
goto start;
@ -1022,6 +1023,8 @@ static void btree_node_read_work(struct work_struct *work)
!bch2_btree_node_read_done(c, ca, b, can_retry))
break;
saw_error = true;
if (!can_retry) {
set_btree_node_read_error(b);
break;
@ -1031,6 +1034,10 @@ static void btree_node_read_work(struct work_struct *work)
bch2_time_stats_update(&c->times[BCH_TIME_btree_node_read],
rb->start_time);
bio_put(&rb->bio);
if (saw_error && !btree_node_read_error(b))
bch2_btree_node_rewrite_async(c, b);
clear_btree_node_read_in_flight(b);
wake_up_bit(&b->flags, BTREE_NODE_read_in_flight);
}

View File

@ -72,6 +72,7 @@ int bch2_btree_delete_range(struct bch_fs *, enum btree_id,
int bch2_btree_node_rewrite(struct bch_fs *c, struct btree_iter *,
__le64, unsigned);
void bch2_btree_node_rewrite_async(struct bch_fs *, struct btree *);
int bch2_btree_node_update_key(struct bch_fs *, struct btree_iter *,
struct btree *, struct bkey_i *);

View File

@ -1797,6 +1797,56 @@ int bch2_btree_node_rewrite(struct bch_fs *c, struct btree_iter *iter,
return ret;
}
struct async_btree_rewrite {
struct bch_fs *c;
struct work_struct work;
enum btree_id btree_id;
unsigned level;
struct bpos pos;
__le64 seq;
};
void async_btree_node_rewrite_work(struct work_struct *work)
{
struct async_btree_rewrite *a =
container_of(work, struct async_btree_rewrite, work);
struct bch_fs *c = a->c;
struct btree_trans trans;
struct btree_iter *iter;
bch2_trans_init(&trans, c, 0, 0);
iter = bch2_trans_get_node_iter(&trans, a->btree_id, a->pos,
BTREE_MAX_DEPTH, a->level, 0);
bch2_btree_node_rewrite(c, iter, a->seq, 0);
bch2_trans_iter_put(&trans, iter);
bch2_trans_exit(&trans);
percpu_ref_put(&c->writes);
kfree(a);
}
void bch2_btree_node_rewrite_async(struct bch_fs *c, struct btree *b)
{
struct async_btree_rewrite *a;
if (!percpu_ref_tryget(&c->writes))
return;
a = kmalloc(sizeof(*a), GFP_NOFS);
if (!a) {
percpu_ref_put(&c->writes);
return;
}
a->c = c;
a->btree_id = b->c.btree_id;
a->level = b->c.level;
a->pos = b->key.k.p;
a->seq = b->data->keys.seq;
INIT_WORK(&a->work, async_btree_node_rewrite_work);
queue_work(system_long_wq, &a->work);
}
static void __bch2_btree_node_update_key(struct bch_fs *c,
struct btree_update *as,
struct btree_iter *iter,