summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2021-04-24 02:47:41 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2021-05-19 15:33:45 -0400
commitfd8c4c1ff9df17d1ca5187052eae2d8c11f98fc1 (patch)
treea89a8cc028d89bc9e7864bcb92c775ee56cb3cf5 /fs
parent0ef3c07bd06a323682a2b6082e50060b8f661406 (diff)
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/bcachefs/btree_io.c7
-rw-r--r--fs/bcachefs/btree_update.h1
-rw-r--r--fs/bcachefs/btree_update_interior.c50
3 files changed, 58 insertions, 0 deletions
diff --git a/fs/bcachefs/btree_io.c b/fs/bcachefs/btree_io.c
index 2de31a6b9661..05418b51b8a5 100644
--- a/fs/bcachefs/btree_io.c
+++ b/fs/bcachefs/btree_io.c
@@ -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;
@@ -1023,6 +1024,8 @@ start:
!bch2_btree_node_read_done(c, ca, b, can_retry))
break;
+ saw_error = true;
+
if (!can_retry) {
set_btree_node_read_error(b);
break;
@@ -1032,6 +1035,10 @@ start:
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);
}
diff --git a/fs/bcachefs/btree_update.h b/fs/bcachefs/btree_update.h
index 0c7caa7e91a0..56131ac516ce 100644
--- a/fs/bcachefs/btree_update.h
+++ b/fs/bcachefs/btree_update.h
@@ -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 *);
diff --git a/fs/bcachefs/btree_update_interior.c b/fs/bcachefs/btree_update_interior.c
index 5c86e76f5079..029307df3e87 100644
--- a/fs/bcachefs/btree_update_interior.c
+++ b/fs/bcachefs/btree_update_interior.c
@@ -1797,6 +1797,56 @@ out:
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 = kmalloc(sizeof(*a), GFP_NOFS);
+
+ 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,