Skip to content

Commit 9432e90

Browse files
author
Kent Overstreet
committed
bcachefs: Check for invalid bucket from bucket_gen(), gc_bucket()
Turn more asserts into proper recoverable error paths. Reported-by: [email protected] Signed-off-by: Kent Overstreet <[email protected]>
1 parent 9c4acd1 commit 9432e90

File tree

8 files changed

+135
-47
lines changed

8 files changed

+135
-47
lines changed

fs/bcachefs/alloc_background.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ int bch2_trigger_alloc(struct btree_trans *trans,
741741
enum btree_iter_update_trigger_flags flags)
742742
{
743743
struct bch_fs *c = trans->c;
744+
struct printbuf buf = PRINTBUF;
744745
int ret = 0;
745746

746747
struct bch_dev *ca = bch2_dev_bucket_tryget(c, new.k->p);
@@ -860,8 +861,14 @@ int bch2_trigger_alloc(struct btree_trans *trans,
860861
}
861862

862863
percpu_down_read(&c->mark_lock);
863-
if (new_a->gen != old_a->gen)
864-
*bucket_gen(ca, new.k->p.offset) = new_a->gen;
864+
if (new_a->gen != old_a->gen) {
865+
u8 *gen = bucket_gen(ca, new.k->p.offset);
866+
if (unlikely(!gen)) {
867+
percpu_up_read(&c->mark_lock);
868+
goto invalid_bucket;
869+
}
870+
*gen = new_a->gen;
871+
}
865872

866873
bch2_dev_usage_update(c, ca, old_a, new_a, journal_seq, false);
867874
percpu_up_read(&c->mark_lock);
@@ -895,6 +902,11 @@ int bch2_trigger_alloc(struct btree_trans *trans,
895902

896903
percpu_down_read(&c->mark_lock);
897904
struct bucket *g = gc_bucket(ca, new.k->p.offset);
905+
if (unlikely(!g)) {
906+
percpu_up_read(&c->mark_lock);
907+
goto invalid_bucket;
908+
}
909+
g->gen_valid = 1;
898910

899911
bucket_lock(g);
900912

@@ -910,8 +922,14 @@ int bch2_trigger_alloc(struct btree_trans *trans,
910922
percpu_up_read(&c->mark_lock);
911923
}
912924
err:
925+
printbuf_exit(&buf);
913926
bch2_dev_put(ca);
914927
return ret;
928+
invalid_bucket:
929+
bch2_fs_inconsistent(c, "reference to invalid bucket\n %s",
930+
(bch2_bkey_val_to_text(&buf, c, new.s_c), buf.buf));
931+
ret = -EIO;
932+
goto err;
915933
}
916934

917935
/*

fs/bcachefs/btree_gc.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,9 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
874874
const struct bch_alloc_v4 *old;
875875
int ret;
876876

877+
if (!bucket_valid(ca, k.k->p.offset))
878+
return 0;
879+
877880
old = bch2_alloc_to_v4(k, &old_convert);
878881
gc = new = *old;
879882

@@ -1005,12 +1008,14 @@ static int bch2_gc_alloc_start(struct bch_fs *c)
10051008
continue;
10061009
}
10071010

1008-
struct bch_alloc_v4 a_convert;
1009-
const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k, &a_convert);
1011+
if (bucket_valid(ca, k.k->p.offset)) {
1012+
struct bch_alloc_v4 a_convert;
1013+
const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k, &a_convert);
10101014

1011-
struct bucket *g = gc_bucket(ca, k.k->p.offset);
1012-
g->gen_valid = 1;
1013-
g->gen = a->gen;
1015+
struct bucket *g = gc_bucket(ca, k.k->p.offset);
1016+
g->gen_valid = 1;
1017+
g->gen = a->gen;
1018+
}
10141019
0;
10151020
})));
10161021
bch2_dev_put(ca);

fs/bcachefs/buckets.c

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,17 @@ static int bch2_check_fix_ptr(struct btree_trans *trans,
488488
}
489489

490490
struct bucket *g = PTR_GC_BUCKET(ca, &p.ptr);
491+
if (!g) {
492+
if (fsck_err(c, ptr_to_invalid_device,
493+
"pointer to invalid bucket on device %u\n"
494+
"while marking %s",
495+
p.ptr.dev,
496+
(printbuf_reset(&buf),
497+
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
498+
*do_update = true;
499+
goto out;
500+
}
501+
491502
enum bch_data_type data_type = bch2_bkey_ptr_data_type(k, p, entry);
492503

493504
if (fsck_err_on(!g->gen_valid,
@@ -577,17 +588,17 @@ static int bch2_check_fix_ptr(struct btree_trans *trans,
577588
if (p.has_ec) {
578589
struct gc_stripe *m = genradix_ptr(&c->gc_stripes, p.ec.idx);
579590

580-
if (fsck_err_on(!m || !m->alive, c,
581-
ptr_to_missing_stripe,
591+
if (fsck_err_on(!m || !m->alive,
592+
c, ptr_to_missing_stripe,
582593
"pointer to nonexistent stripe %llu\n"
583594
"while marking %s",
584595
(u64) p.ec.idx,
585596
(printbuf_reset(&buf),
586597
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
587598
*do_update = true;
588599

589-
if (fsck_err_on(m && m->alive && !bch2_ptr_matches_stripe_m(m, p), c,
590-
ptr_to_incorrect_stripe,
600+
if (fsck_err_on(m && m->alive && !bch2_ptr_matches_stripe_m(m, p),
601+
c, ptr_to_incorrect_stripe,
591602
"pointer does not match stripe %llu\n"
592603
"while marking %s",
593604
(u64) p.ec.idx,
@@ -1004,6 +1015,7 @@ static int bch2_trigger_pointer(struct btree_trans *trans,
10041015
enum btree_iter_update_trigger_flags flags)
10051016
{
10061017
bool insert = !(flags & BTREE_TRIGGER_overwrite);
1018+
struct printbuf buf = PRINTBUF;
10071019
int ret = 0;
10081020

10091021
struct bch_fs *c = trans->c;
@@ -1036,6 +1048,13 @@ static int bch2_trigger_pointer(struct btree_trans *trans,
10361048
if (flags & BTREE_TRIGGER_gc) {
10371049
percpu_down_read(&c->mark_lock);
10381050
struct bucket *g = gc_bucket(ca, bucket.offset);
1051+
if (bch2_fs_inconsistent_on(!g, c, "reference to invalid bucket on device %u\n %s",
1052+
p.ptr.dev,
1053+
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
1054+
ret = -EIO;
1055+
goto err_unlock;
1056+
}
1057+
10391058
bucket_lock(g);
10401059
struct bch_alloc_v4 old = bucket_m_to_alloc(*g), new = old;
10411060
ret = __mark_pointer(trans, ca, k, &p.ptr, *sectors, bp.data_type, &new);
@@ -1044,10 +1063,12 @@ static int bch2_trigger_pointer(struct btree_trans *trans,
10441063
bch2_dev_usage_update(c, ca, &old, &new, 0, true);
10451064
}
10461065
bucket_unlock(g);
1066+
err_unlock:
10471067
percpu_up_read(&c->mark_lock);
10481068
}
10491069
err:
10501070
bch2_dev_put(ca);
1071+
printbuf_exit(&buf);
10511072
return ret;
10521073
}
10531074

@@ -1335,10 +1356,11 @@ static int bch2_mark_metadata_bucket(struct bch_fs *c, struct bch_dev *ca,
13351356
u64 b, enum bch_data_type data_type, unsigned sectors,
13361357
enum btree_iter_update_trigger_flags flags)
13371358
{
1338-
int ret = 0;
1339-
13401359
percpu_down_read(&c->mark_lock);
13411360
struct bucket *g = gc_bucket(ca, b);
1361+
if (bch2_fs_inconsistent_on(!g, c, "reference to invalid bucket on device %u when marking metadata type %s",
1362+
ca->dev_idx, bch2_data_type_str(data_type)))
1363+
goto err_unlock;
13421364

13431365
bucket_lock(g);
13441366
struct bch_alloc_v4 old = bucket_m_to_alloc(*g);
@@ -1347,29 +1369,27 @@ static int bch2_mark_metadata_bucket(struct bch_fs *c, struct bch_dev *ca,
13471369
g->data_type != data_type, c,
13481370
"different types of data in same bucket: %s, %s",
13491371
bch2_data_type_str(g->data_type),
1350-
bch2_data_type_str(data_type))) {
1351-
ret = -EIO;
1372+
bch2_data_type_str(data_type)))
13521373
goto err;
1353-
}
13541374

13551375
if (bch2_fs_inconsistent_on((u64) g->dirty_sectors + sectors > ca->mi.bucket_size, c,
13561376
"bucket %u:%llu gen %u data type %s sector count overflow: %u + %u > bucket size",
13571377
ca->dev_idx, b, g->gen,
13581378
bch2_data_type_str(g->data_type ?: data_type),
1359-
g->dirty_sectors, sectors)) {
1360-
ret = -EIO;
1379+
g->dirty_sectors, sectors))
13611380
goto err;
1362-
}
13631381

13641382
g->data_type = data_type;
13651383
g->dirty_sectors += sectors;
13661384
struct bch_alloc_v4 new = bucket_m_to_alloc(*g);
1385+
bch2_dev_usage_update(c, ca, &old, &new, 0, true);
1386+
percpu_up_read(&c->mark_lock);
1387+
return 0;
13671388
err:
13681389
bucket_unlock(g);
1369-
if (!ret)
1370-
bch2_dev_usage_update(c, ca, &old, &new, 0, true);
1390+
err_unlock:
13711391
percpu_up_read(&c->mark_lock);
1372-
return ret;
1392+
return -EIO;
13731393
}
13741394

13751395
int bch2_trans_mark_metadata_bucket(struct btree_trans *trans,

fs/bcachefs/buckets.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,22 @@ static inline int gen_after(u8 a, u8 b)
172172
return r > 0 ? r : 0;
173173
}
174174

175-
static inline u8 dev_ptr_stale_rcu(struct bch_dev *ca, const struct bch_extent_ptr *ptr)
175+
static inline int dev_ptr_stale_rcu(struct bch_dev *ca, const struct bch_extent_ptr *ptr)
176176
{
177-
return gen_after(*bucket_gen(ca, PTR_BUCKET_NR(ca, ptr)), ptr->gen);
177+
u8 *gen = bucket_gen(ca, PTR_BUCKET_NR(ca, ptr));
178+
if (!gen)
179+
return -1;
180+
return gen_after(*gen, ptr->gen);
178181
}
179182

180183
/**
181184
* dev_ptr_stale() - check if a pointer points into a bucket that has been
182185
* invalidated.
183186
*/
184-
static inline u8 dev_ptr_stale(struct bch_dev *ca, const struct bch_extent_ptr *ptr)
187+
static inline int dev_ptr_stale(struct bch_dev *ca, const struct bch_extent_ptr *ptr)
185188
{
186189
rcu_read_lock();
187-
u8 ret = dev_ptr_stale_rcu(ca, ptr);
190+
int ret = dev_ptr_stale_rcu(ca, ptr);
188191
rcu_read_unlock();
189192

190193
return ret;

fs/bcachefs/ec.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ static int mark_stripe_bucket(struct btree_trans *trans,
268268
{
269269
struct bch_fs *c = trans->c;
270270
const struct bch_extent_ptr *ptr = s.v->ptrs + ptr_idx;
271+
struct printbuf buf = PRINTBUF;
271272
int ret = 0;
272273

273274
struct bch_dev *ca = bch2_dev_tryget(c, ptr->dev);
@@ -289,6 +290,13 @@ static int mark_stripe_bucket(struct btree_trans *trans,
289290
if (flags & BTREE_TRIGGER_gc) {
290291
percpu_down_read(&c->mark_lock);
291292
struct bucket *g = gc_bucket(ca, bucket.offset);
293+
if (bch2_fs_inconsistent_on(!g, c, "reference to invalid bucket on device %u\n %s",
294+
ptr->dev,
295+
(bch2_bkey_val_to_text(&buf, c, s.s_c), buf.buf))) {
296+
ret = -EIO;
297+
goto err_unlock;
298+
}
299+
292300
bucket_lock(g);
293301
struct bch_alloc_v4 old = bucket_m_to_alloc(*g), new = old;
294302
ret = __mark_stripe_bucket(trans, ca, s, ptr_idx, deleting, bucket, &new, flags);
@@ -297,10 +305,12 @@ static int mark_stripe_bucket(struct btree_trans *trans,
297305
bch2_dev_usage_update(c, ca, &old, &new, 0, true);
298306
}
299307
bucket_unlock(g);
308+
err_unlock:
300309
percpu_up_read(&c->mark_lock);
301310
}
302311
err:
303312
bch2_dev_put(ca);
313+
printbuf_exit(&buf);
304314
return ret;
305315
}
306316

@@ -714,10 +724,12 @@ static void ec_block_endio(struct bio *bio)
714724
bch2_blk_status_to_str(bio->bi_status)))
715725
clear_bit(ec_bio->idx, ec_bio->buf->valid);
716726

717-
if (dev_ptr_stale(ca, ptr)) {
727+
int stale = dev_ptr_stale(ca, ptr);
728+
if (stale) {
718729
bch_err_ratelimited(ca->fs,
719-
"error %s stripe: stale pointer after io",
720-
bio_data_dir(bio) == READ ? "reading from" : "writing to");
730+
"error %s stripe: stale/invalid pointer (%i) after io",
731+
bio_data_dir(bio) == READ ? "reading from" : "writing to",
732+
stale);
721733
clear_bit(ec_bio->idx, ec_bio->buf->valid);
722734
}
723735

@@ -743,10 +755,12 @@ static void ec_block_io(struct bch_fs *c, struct ec_stripe_buf *buf,
743755
return;
744756
}
745757

746-
if (dev_ptr_stale(ca, ptr)) {
758+
int stale = dev_ptr_stale(ca, ptr);
759+
if (stale) {
747760
bch_err_ratelimited(c,
748-
"error %s stripe: stale pointer",
749-
rw == READ ? "reading from" : "writing to");
761+
"error %s stripe: stale pointer (%i)",
762+
rw == READ ? "reading from" : "writing to",
763+
stale);
750764
clear_bit(idx, buf->valid);
751765
return;
752766
}

fs/bcachefs/extents.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k,
137137

138138
struct bch_dev *ca = bch2_dev_rcu(c, p.ptr.dev);
139139

140-
if (p.ptr.cached && (!ca || dev_ptr_stale(ca, &p.ptr)))
140+
if (p.ptr.cached && (!ca || dev_ptr_stale_rcu(ca, &p.ptr)))
141141
continue;
142142

143143
f = failed ? dev_io_failures(failed, p.ptr.dev) : NULL;
@@ -999,7 +999,7 @@ bool bch2_extent_normalize(struct bch_fs *c, struct bkey_s k)
999999
bch2_bkey_drop_ptrs(k, ptr,
10001000
ptr->cached &&
10011001
(ca = bch2_dev_rcu(c, ptr->dev)) &&
1002-
dev_ptr_stale_rcu(ca, ptr));
1002+
dev_ptr_stale_rcu(ca, ptr) > 0);
10031003
rcu_read_unlock();
10041004

10051005
return bkey_deleted(k.k);
@@ -1024,8 +1024,11 @@ void bch2_extent_ptr_to_text(struct printbuf *out, struct bch_fs *c, const struc
10241024
prt_str(out, " cached");
10251025
if (ptr->unwritten)
10261026
prt_str(out, " unwritten");
1027-
if (bucket_valid(ca, b) && dev_ptr_stale_rcu(ca, ptr))
1027+
int stale = dev_ptr_stale_rcu(ca, ptr);
1028+
if (stale > 0)
10281029
prt_printf(out, " stale");
1030+
else if (stale)
1031+
prt_printf(out, " invalid");
10291032
}
10301033
rcu_read_unlock();
10311034
--out->atomic;

fs/bcachefs/io_read.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -777,18 +777,32 @@ static noinline void read_from_stale_dirty_pointer(struct btree_trans *trans,
777777
PTR_BUCKET_POS(ca, &ptr),
778778
BTREE_ITER_cached);
779779

780-
prt_printf(&buf, "Attempting to read from stale dirty pointer:\n");
781-
printbuf_indent_add(&buf, 2);
780+
u8 *gen = bucket_gen(ca, iter.pos.offset);
781+
if (gen) {
782782

783-
bch2_bkey_val_to_text(&buf, c, k);
784-
prt_newline(&buf);
783+
prt_printf(&buf, "Attempting to read from stale dirty pointer:\n");
784+
printbuf_indent_add(&buf, 2);
785785

786-
prt_printf(&buf, "memory gen: %u", *bucket_gen(ca, iter.pos.offset));
787-
788-
ret = lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_slot(&iter)));
789-
if (!ret) {
786+
bch2_bkey_val_to_text(&buf, c, k);
790787
prt_newline(&buf);
788+
789+
prt_printf(&buf, "memory gen: %u", *gen);
790+
791+
ret = lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_slot(&iter)));
792+
if (!ret) {
793+
prt_newline(&buf);
794+
bch2_bkey_val_to_text(&buf, c, k);
795+
}
796+
} else {
797+
prt_printf(&buf, "Attempting to read from invalid bucket %llu:%llu:\n",
798+
iter.pos.inode, iter.pos.offset);
799+
printbuf_indent_add(&buf, 2);
800+
801+
prt_printf(&buf, "first bucket %u nbuckets %llu\n",
802+
ca->mi.first_bucket, ca->mi.nbuckets);
803+
791804
bch2_bkey_val_to_text(&buf, c, k);
805+
prt_newline(&buf);
792806
}
793807

794808
bch2_fs_inconsistent(c, "%s", buf.buf);

0 commit comments

Comments
 (0)