Skip to content

Commit cff07e2

Browse files
author
Kent Overstreet
committed
bcachefs: Guard against overflowing LRU_TIME_BITS
LRUs only have 48 bits for the time field (i.e. LRU order); thus we need overflow checks and guards. Reported-by: [email protected] Signed-off-by: Kent Overstreet <[email protected]>
1 parent 1ba4421 commit cff07e2

File tree

6 files changed

+32
-12
lines changed

6 files changed

+32
-12
lines changed

fs/bcachefs/alloc_background.c

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,14 @@ int bch2_alloc_v4_invalid(struct bch_fs *c, struct bkey_s_c k,
259259
"invalid data type (got %u should be %u)",
260260
a.v->data_type, alloc_data_type(*a.v, a.v->data_type));
261261

262+
for (unsigned i = 0; i < 2; i++)
263+
bkey_fsck_err_on(a.v->io_time[i] > LRU_TIME_MAX,
264+
c, err,
265+
alloc_key_io_time_bad,
266+
"invalid io_time[%s]: %llu, max %llu",
267+
i == READ ? "read" : "write",
268+
a.v->io_time[i], LRU_TIME_MAX);
269+
262270
switch (a.v->data_type) {
263271
case BCH_DATA_free:
264272
case BCH_DATA_need_gc_gens:
@@ -757,8 +765,8 @@ int bch2_trigger_alloc(struct btree_trans *trans,
757765
alloc_data_type_set(new_a, new_a->data_type);
758766

759767
if (bch2_bucket_sectors_total(*new_a) > bch2_bucket_sectors_total(*old_a)) {
760-
new_a->io_time[READ] = max_t(u64, 1, atomic64_read(&c->io_clock[READ].now));
761-
new_a->io_time[WRITE]= max_t(u64, 1, atomic64_read(&c->io_clock[WRITE].now));
768+
new_a->io_time[READ] = bch2_current_io_time(c, READ);
769+
new_a->io_time[WRITE]= bch2_current_io_time(c, WRITE);
762770
SET_BCH_ALLOC_V4_NEED_INC_GEN(new_a, true);
763771
SET_BCH_ALLOC_V4_NEED_DISCARD(new_a, true);
764772
}
@@ -781,7 +789,7 @@ int bch2_trigger_alloc(struct btree_trans *trans,
781789

782790
if (new_a->data_type == BCH_DATA_cached &&
783791
!new_a->io_time[READ])
784-
new_a->io_time[READ] = max_t(u64, 1, atomic64_read(&c->io_clock[READ].now));
792+
new_a->io_time[READ] = bch2_current_io_time(c, READ);
785793

786794
u64 old_lru = alloc_lru_idx_read(*old_a);
787795
u64 new_lru = alloc_lru_idx_read(*new_a);
@@ -1579,7 +1587,7 @@ static int bch2_check_alloc_to_lru_ref(struct btree_trans *trans,
15791587
if (ret)
15801588
goto err;
15811589

1582-
a_mut->v.io_time[READ] = atomic64_read(&c->io_clock[READ].now);
1590+
a_mut->v.io_time[READ] = bch2_current_io_time(c, READ);
15831591
ret = bch2_trans_update(trans, alloc_iter,
15841592
&a_mut->k_i, BTREE_TRIGGER_norun);
15851593
if (ret)
@@ -1975,8 +1983,8 @@ static int invalidate_one_bucket(struct btree_trans *trans,
19751983
a->v.data_type = 0;
19761984
a->v.dirty_sectors = 0;
19771985
a->v.cached_sectors = 0;
1978-
a->v.io_time[READ] = atomic64_read(&c->io_clock[READ].now);
1979-
a->v.io_time[WRITE] = atomic64_read(&c->io_clock[WRITE].now);
1986+
a->v.io_time[READ] = bch2_current_io_time(c, READ);
1987+
a->v.io_time[WRITE] = bch2_current_io_time(c, WRITE);
19801988

19811989
ret = bch2_trans_commit(trans, NULL, NULL,
19821990
BCH_WATERMARK_btree|
@@ -2204,7 +2212,7 @@ int bch2_bucket_io_time_reset(struct btree_trans *trans, unsigned dev,
22042212
if (ret)
22052213
return ret;
22062214

2207-
now = atomic64_read(&c->io_clock[rw].now);
2215+
now = bch2_current_io_time(c, rw);
22082216
if (a->v.io_time[rw] == now)
22092217
goto out;
22102218

fs/bcachefs/alloc_background.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,13 @@ static inline u64 alloc_lru_idx_fragmentation(struct bch_alloc_v4 a,
141141
!bch2_bucket_sectors_fragmented(ca, a))
142142
return 0;
143143

144-
u64 d = bch2_bucket_sectors_dirty(a);
144+
/*
145+
* avoid overflowing LRU_TIME_BITS on a corrupted fs, when
146+
* bucket_sectors_dirty is (much) bigger than bucket_size
147+
*/
148+
u64 d = min(bch2_bucket_sectors_dirty(a),
149+
ca->mi.bucket_size);
150+
145151
return div_u64(d * (1ULL << 31), ca->mi.bucket_size);
146152
}
147153

fs/bcachefs/bcachefs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,11 @@ static inline s64 bch2_current_time(const struct bch_fs *c)
12141214
return timespec_to_bch2_time(c, now);
12151215
}
12161216

1217+
static inline u64 bch2_current_io_time(const struct bch_fs *c, int rw)
1218+
{
1219+
return max(1ULL, (u64) atomic64_read(&c->io_clock[rw].now) & LRU_TIME_MAX);
1220+
}
1221+
12171222
static inline struct stdio_redirect *bch2_fs_stdio_redirect(struct bch_fs *c)
12181223
{
12191224
struct stdio_redirect *stdio = c->stdio;

fs/bcachefs/bcachefs_format.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,9 @@ struct bch_lru {
476476

477477
#define LRU_ID_STRIPES (1U << 16)
478478

479+
#define LRU_TIME_BITS 48
480+
#define LRU_TIME_MAX ((1ULL << LRU_TIME_BITS) - 1)
481+
479482
/* Optional/variable size superblock sections: */
480483

481484
struct bch_sb_field {

fs/bcachefs/lru.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
#ifndef _BCACHEFS_LRU_H
33
#define _BCACHEFS_LRU_H
44

5-
#define LRU_TIME_BITS 48
6-
#define LRU_TIME_MAX ((1ULL << LRU_TIME_BITS) - 1)
7-
85
static inline u64 lru_pos_id(struct bpos pos)
96
{
107
return pos.inode >> LRU_TIME_BITS;

fs/bcachefs/sb-errors_format.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@
277277
x(alloc_key_stripe_sectors_wrong, 271) \
278278
x(accounting_mismatch, 272) \
279279
x(accounting_replicas_not_marked, 273) \
280-
x(invalid_btree_id, 274)
280+
x(invalid_btree_id, 274) \
281+
x(alloc_key_io_time_bad, 275)
281282

282283
enum bch_sb_error_id {
283284
#define x(t, n) BCH_FSCK_ERR_##t = n,

0 commit comments

Comments
 (0)