Skip to content

Commit 2f3d93e

Browse files
Long Litytso
authored andcommitted
ext4: fix race in buffer_head read fault injection
When I enabled ext4 debug for fault injection testing, I encountered the following warning: EXT4-fs error (device sda): ext4_read_inode_bitmap:201: comm fsstress: Cannot read inode bitmap - block_group = 8, inode_bitmap = 1051 WARNING: CPU: 0 PID: 511 at fs/buffer.c:1181 mark_buffer_dirty+0x1b3/0x1d0 The root cause of the issue lies in the improper implementation of ext4's buffer_head read fault injection. The actual completion of buffer_head read and the buffer_head fault injection are not atomic, which can lead to the uptodate flag being cleared on normally used buffer_heads in race conditions. [CPU0] [CPU1] [CPU2] ext4_read_inode_bitmap ext4_read_bh() <bh read complete> ext4_read_inode_bitmap if (buffer_uptodate(bh)) return bh jbd2_journal_commit_transaction __jbd2_journal_refile_buffer __jbd2_journal_unfile_buffer __jbd2_journal_temp_unlink_buffer ext4_simulate_fail_bh() clear_buffer_uptodate mark_buffer_dirty <report warning> WARN_ON_ONCE(!buffer_uptodate(bh)) The best approach would be to perform fault injection in the IO completion callback function, rather than after IO completion. However, the IO completion callback function cannot get the fault injection code in sb. Fix it by passing the result of fault injection into the bh read function, we simulate faults within the bh read function itself. This requires adding an extra parameter to the bh read functions that need fault injection. Fixes: 46f870d ("ext4: simulate various I/O and checksum errors when reading metadata") Signed-off-by: Long Li <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent a908258 commit 2f3d93e

File tree

10 files changed

+29
-29
lines changed

10 files changed

+29
-29
lines changed

fs/ext4/balloc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,8 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group,
550550
trace_ext4_read_block_bitmap_load(sb, block_group, ignore_locked);
551551
ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO |
552552
(ignore_locked ? REQ_RAHEAD : 0),
553-
ext4_end_bitmap_read);
553+
ext4_end_bitmap_read,
554+
ext4_simulate_fail(sb, EXT4_SIM_BBITMAP_EIO));
554555
return bh;
555556
verify:
556557
err = ext4_validate_block_bitmap(sb, desc, block_group, bh);
@@ -577,7 +578,6 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
577578
if (!desc)
578579
return -EFSCORRUPTED;
579580
wait_on_buffer(bh);
580-
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_BBITMAP_EIO);
581581
if (!buffer_uptodate(bh)) {
582582
ext4_error_err(sb, EIO, "Cannot read block bitmap - "
583583
"block_group = %u, block_bitmap = %llu",

fs/ext4/ext4.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,14 +1865,6 @@ static inline bool ext4_simulate_fail(struct super_block *sb,
18651865
return false;
18661866
}
18671867

1868-
static inline void ext4_simulate_fail_bh(struct super_block *sb,
1869-
struct buffer_head *bh,
1870-
unsigned long code)
1871-
{
1872-
if (!IS_ERR(bh) && ext4_simulate_fail(sb, code))
1873-
clear_buffer_uptodate(bh);
1874-
}
1875-
18761868
/*
18771869
* Error number codes for s_{first,last}_error_errno
18781870
*
@@ -3100,9 +3092,9 @@ extern struct buffer_head *ext4_sb_bread(struct super_block *sb,
31003092
extern struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb,
31013093
sector_t block);
31023094
extern void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
3103-
bh_end_io_t *end_io);
3095+
bh_end_io_t *end_io, bool simu_fail);
31043096
extern int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
3105-
bh_end_io_t *end_io);
3097+
bh_end_io_t *end_io, bool simu_fail);
31063098
extern int ext4_read_bh_lock(struct buffer_head *bh, blk_opf_t op_flags, bool wait);
31073099
extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
31083100
extern int ext4_seq_options_show(struct seq_file *seq, void *offset);

fs/ext4/extents.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ __read_extent_tree_block(const char *function, unsigned int line,
568568

569569
if (!bh_uptodate_or_lock(bh)) {
570570
trace_ext4_ext_load_extent(inode, pblk, _RET_IP_);
571-
err = ext4_read_bh(bh, 0, NULL);
571+
err = ext4_read_bh(bh, 0, NULL, false);
572572
if (err < 0)
573573
goto errout;
574574
}

fs/ext4/ialloc.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,9 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
193193
* submit the buffer_head for reading
194194
*/
195195
trace_ext4_load_inode_bitmap(sb, block_group);
196-
ext4_read_bh(bh, REQ_META | REQ_PRIO, ext4_end_bitmap_read);
197-
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_IBITMAP_EIO);
196+
ext4_read_bh(bh, REQ_META | REQ_PRIO,
197+
ext4_end_bitmap_read,
198+
ext4_simulate_fail(sb, EXT4_SIM_IBITMAP_EIO));
198199
if (!buffer_uptodate(bh)) {
199200
put_bh(bh);
200201
ext4_error_err(sb, EIO, "Cannot read inode bitmap - "

fs/ext4/indirect.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ static Indirect *ext4_get_branch(struct inode *inode, int depth,
170170
}
171171

172172
if (!bh_uptodate_or_lock(bh)) {
173-
if (ext4_read_bh(bh, 0, NULL) < 0) {
173+
if (ext4_read_bh(bh, 0, NULL, false) < 0) {
174174
put_bh(bh);
175175
goto failure;
176176
}

fs/ext4/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4504,10 +4504,10 @@ static int __ext4_get_inode_loc(struct super_block *sb, unsigned long ino,
45044504
* Read the block from disk.
45054505
*/
45064506
trace_ext4_load_inode(sb, ino);
4507-
ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO, NULL);
4507+
ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO, NULL,
4508+
ext4_simulate_fail(sb, EXT4_SIM_INODE_EIO));
45084509
blk_finish_plug(&plug);
45094510
wait_on_buffer(bh);
4510-
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_INODE_EIO);
45114511
if (!buffer_uptodate(bh)) {
45124512
if (ret_block)
45134513
*ret_block = block;

fs/ext4/mmp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **bh,
9494
}
9595

9696
lock_buffer(*bh);
97-
ret = ext4_read_bh(*bh, REQ_META | REQ_PRIO, NULL);
97+
ret = ext4_read_bh(*bh, REQ_META | REQ_PRIO, NULL, false);
9898
if (ret)
9999
goto warn_exit;
100100

fs/ext4/move_extent.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ static int mext_page_mkuptodate(struct folio *folio, size_t from, size_t to)
213213
unlock_buffer(bh);
214214
continue;
215215
}
216-
ext4_read_bh_nowait(bh, 0, NULL);
216+
ext4_read_bh_nowait(bh, 0, NULL, false);
217217
nr++;
218218
} while (block++, (bh = bh->b_this_page) != head);
219219

fs/ext4/resize.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1300,7 +1300,7 @@ static struct buffer_head *ext4_get_bitmap(struct super_block *sb, __u64 block)
13001300
if (unlikely(!bh))
13011301
return NULL;
13021302
if (!bh_uptodate_or_lock(bh)) {
1303-
if (ext4_read_bh(bh, 0, NULL) < 0) {
1303+
if (ext4_read_bh(bh, 0, NULL, false) < 0) {
13041304
brelse(bh);
13051305
return NULL;
13061306
}

fs/ext4/super.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,14 @@ MODULE_ALIAS("ext3");
161161

162162

163163
static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
164-
bh_end_io_t *end_io)
164+
bh_end_io_t *end_io, bool simu_fail)
165165
{
166+
if (simu_fail) {
167+
clear_buffer_uptodate(bh);
168+
unlock_buffer(bh);
169+
return;
170+
}
171+
166172
/*
167173
* buffer's verified bit is no longer valid after reading from
168174
* disk again due to write out error, clear it to make sure we
@@ -176,18 +182,19 @@ static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
176182
}
177183

178184
void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
179-
bh_end_io_t *end_io)
185+
bh_end_io_t *end_io, bool simu_fail)
180186
{
181187
BUG_ON(!buffer_locked(bh));
182188

183189
if (ext4_buffer_uptodate(bh)) {
184190
unlock_buffer(bh);
185191
return;
186192
}
187-
__ext4_read_bh(bh, op_flags, end_io);
193+
__ext4_read_bh(bh, op_flags, end_io, simu_fail);
188194
}
189195

190-
int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags, bh_end_io_t *end_io)
196+
int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
197+
bh_end_io_t *end_io, bool simu_fail)
191198
{
192199
BUG_ON(!buffer_locked(bh));
193200

@@ -196,7 +203,7 @@ int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags, bh_end_io_t *end_io
196203
return 0;
197204
}
198205

199-
__ext4_read_bh(bh, op_flags, end_io);
206+
__ext4_read_bh(bh, op_flags, end_io, simu_fail);
200207

201208
wait_on_buffer(bh);
202209
if (buffer_uptodate(bh))
@@ -208,10 +215,10 @@ int ext4_read_bh_lock(struct buffer_head *bh, blk_opf_t op_flags, bool wait)
208215
{
209216
lock_buffer(bh);
210217
if (!wait) {
211-
ext4_read_bh_nowait(bh, op_flags, NULL);
218+
ext4_read_bh_nowait(bh, op_flags, NULL, false);
212219
return 0;
213220
}
214-
return ext4_read_bh(bh, op_flags, NULL);
221+
return ext4_read_bh(bh, op_flags, NULL, false);
215222
}
216223

217224
/*
@@ -266,7 +273,7 @@ void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block)
266273

267274
if (likely(bh)) {
268275
if (trylock_buffer(bh))
269-
ext4_read_bh_nowait(bh, REQ_RAHEAD, NULL);
276+
ext4_read_bh_nowait(bh, REQ_RAHEAD, NULL, false);
270277
brelse(bh);
271278
}
272279
}

0 commit comments

Comments
 (0)