Skip to content

Commit a2cb8a6

Browse files
author
Kent Overstreet
committed
bcachefs: Self healing on read IO error
This repurposes the promote path, which already knows how to call data_update() after a read: we now automatically rewrite bad data when we get a read error and then successfully retry from a different replica. Signed-off-by: Kent Overstreet <[email protected]>
1 parent b1d63b0 commit a2cb8a6

File tree

3 files changed

+53
-26
lines changed

3 files changed

+53
-26
lines changed

fs/bcachefs/extents.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ static void bch2_extent_crc_pack(union bch_extent_crc *,
3737
struct bch_extent_crc_unpacked,
3838
enum bch_extent_entry_type);
3939

40-
static struct bch_dev_io_failures *dev_io_failures(struct bch_io_failures *f,
41-
unsigned dev)
40+
struct bch_dev_io_failures *bch2_dev_io_failures(struct bch_io_failures *f,
41+
unsigned dev)
4242
{
4343
struct bch_dev_io_failures *i;
4444

@@ -52,7 +52,7 @@ static struct bch_dev_io_failures *dev_io_failures(struct bch_io_failures *f,
5252
void bch2_mark_io_failure(struct bch_io_failures *failed,
5353
struct extent_ptr_decoded *p)
5454
{
55-
struct bch_dev_io_failures *f = dev_io_failures(failed, p->ptr.dev);
55+
struct bch_dev_io_failures *f = bch2_dev_io_failures(failed, p->ptr.dev);
5656

5757
if (!f) {
5858
BUG_ON(failed->nr >= ARRAY_SIZE(failed->devs));
@@ -140,7 +140,7 @@ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k,
140140
if (p.ptr.cached && (!ca || dev_ptr_stale_rcu(ca, &p.ptr)))
141141
continue;
142142

143-
f = failed ? dev_io_failures(failed, p.ptr.dev) : NULL;
143+
f = failed ? bch2_dev_io_failures(failed, p.ptr.dev) : NULL;
144144
if (f)
145145
p.idx = f->nr_failed < f->nr_retries
146146
? f->idx

fs/bcachefs/extents.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ out: \
399399

400400
/* utility code common to all keys with pointers: */
401401

402+
struct bch_dev_io_failures *bch2_dev_io_failures(struct bch_io_failures *,
403+
unsigned);
402404
void bch2_mark_io_failure(struct bch_io_failures *,
403405
struct extent_ptr_decoded *);
404406
int bch2_bkey_pick_read_device(struct bch_fs *, struct bkey_s_c,

fs/bcachefs/io_read.c

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,24 @@ static const struct rhashtable_params bch_promote_params = {
9393
static inline int should_promote(struct bch_fs *c, struct bkey_s_c k,
9494
struct bpos pos,
9595
struct bch_io_opts opts,
96-
unsigned flags)
96+
unsigned flags,
97+
struct bch_io_failures *failed)
9798
{
98-
BUG_ON(!opts.promote_target);
99+
if (!failed) {
100+
BUG_ON(!opts.promote_target);
99101

100-
if (!(flags & BCH_READ_MAY_PROMOTE))
101-
return -BCH_ERR_nopromote_may_not;
102+
if (!(flags & BCH_READ_MAY_PROMOTE))
103+
return -BCH_ERR_nopromote_may_not;
102104

103-
if (bch2_bkey_has_target(c, k, opts.promote_target))
104-
return -BCH_ERR_nopromote_already_promoted;
105+
if (bch2_bkey_has_target(c, k, opts.promote_target))
106+
return -BCH_ERR_nopromote_already_promoted;
105107

106-
if (bkey_extent_is_unwritten(k))
107-
return -BCH_ERR_nopromote_unwritten;
108+
if (bkey_extent_is_unwritten(k))
109+
return -BCH_ERR_nopromote_unwritten;
108110

109-
if (bch2_target_congested(c, opts.promote_target))
110-
return -BCH_ERR_nopromote_congested;
111+
if (bch2_target_congested(c, opts.promote_target))
112+
return -BCH_ERR_nopromote_congested;
113+
}
111114

112115
if (rhashtable_lookup_fast(&c->promote_table, &pos,
113116
bch_promote_params))
@@ -164,7 +167,8 @@ static struct promote_op *__promote_alloc(struct btree_trans *trans,
164167
struct extent_ptr_decoded *pick,
165168
struct bch_io_opts opts,
166169
unsigned sectors,
167-
struct bch_read_bio **rbio)
170+
struct bch_read_bio **rbio,
171+
struct bch_io_failures *failed)
168172
{
169173
struct bch_fs *c = trans->c;
170174
struct promote_op *op = NULL;
@@ -217,14 +221,28 @@ static struct promote_op *__promote_alloc(struct btree_trans *trans,
217221
bio = &op->write.op.wbio.bio;
218222
bio_init(bio, NULL, bio->bi_inline_vecs, pages, 0);
219223

224+
struct data_update_opts update_opts = {};
225+
226+
if (!failed) {
227+
update_opts.target = opts.promote_target;
228+
update_opts.extra_replicas = 1;
229+
update_opts.write_flags = BCH_WRITE_ALLOC_NOWAIT|BCH_WRITE_CACHED;
230+
} else {
231+
update_opts.target = opts.foreground_target;
232+
233+
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
234+
unsigned i = 0;
235+
bkey_for_each_ptr(ptrs, ptr) {
236+
if (bch2_dev_io_failures(failed, ptr->dev))
237+
update_opts.rewrite_ptrs |= BIT(i);
238+
i++;
239+
}
240+
}
241+
220242
ret = bch2_data_update_init(trans, NULL, NULL, &op->write,
221243
writepoint_hashed((unsigned long) current),
222244
opts,
223-
(struct data_update_opts) {
224-
.target = opts.promote_target,
225-
.extra_replicas = 1,
226-
.write_flags = BCH_WRITE_ALLOC_NOWAIT|BCH_WRITE_CACHED,
227-
},
245+
update_opts,
228246
btree_id, k);
229247
/*
230248
* possible errors: -BCH_ERR_nocow_lock_blocked,
@@ -258,10 +276,17 @@ static struct promote_op *promote_alloc(struct btree_trans *trans,
258276
unsigned flags,
259277
struct bch_read_bio **rbio,
260278
bool *bounce,
261-
bool *read_full)
279+
bool *read_full,
280+
struct bch_io_failures *failed)
262281
{
263282
struct bch_fs *c = trans->c;
264-
bool promote_full = *read_full || READ_ONCE(c->promote_whole_extents);
283+
/*
284+
* if failed != NULL we're not actually doing a promote, we're
285+
* recovering from an io/checksum error
286+
*/
287+
bool promote_full = (failed ||
288+
*read_full ||
289+
READ_ONCE(c->promote_whole_extents));
265290
/* data might have to be decompressed in the write path: */
266291
unsigned sectors = promote_full
267292
? max(pick->crc.compressed_size, pick->crc.live_size)
@@ -272,15 +297,15 @@ static struct promote_op *promote_alloc(struct btree_trans *trans,
272297
struct promote_op *promote;
273298
int ret;
274299

275-
ret = should_promote(c, k, pos, opts, flags);
300+
ret = should_promote(c, k, pos, opts, flags, failed);
276301
if (ret)
277302
goto nopromote;
278303

279304
promote = __promote_alloc(trans,
280305
k.k->type == KEY_TYPE_reflink_v
281306
? BTREE_ID_reflink
282307
: BTREE_ID_extents,
283-
k, pos, pick, opts, sectors, rbio);
308+
k, pos, pick, opts, sectors, rbio, failed);
284309
ret = PTR_ERR_OR_ZERO(promote);
285310
if (ret)
286311
goto nopromote;
@@ -910,9 +935,9 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
910935
bounce = true;
911936
}
912937

913-
if (orig->opts.promote_target)
938+
if (orig->opts.promote_target)// || failed)
914939
promote = promote_alloc(trans, iter, k, &pick, orig->opts, flags,
915-
&rbio, &bounce, &read_full);
940+
&rbio, &bounce, &read_full, failed);
916941

917942
if (!read_full) {
918943
EBUG_ON(crc_is_compressed(pick.crc));

0 commit comments

Comments
 (0)