Re: [PATCH 16/19] jbd2: report journal write errors with BH_Write_EIO

Jan Kara <[email protected]> Tue, 4 Aug 2026 10:54:37 +0200
Newsgroups org.kernel.vger.linux-ext4,dev.linux.lists.gfs2,dev.linux.lists.ocfs2-devel,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <ppnbgcljrjb5wqqvqiyh254wkunfutydykgbi7zheuf2mfdjqv@j6lngn5vywxj>
On Sat 01-08-26 18:01:00, Chao Shi wrote:
> The journal's own write completion handler,
> journal_end_buffer_io_sync(), reports a failed write by clearing
> BH_Uptodate, and the three places that wait for journal writes look for
> that.  This series is removing that convention: a buffer whose write failed
> still holds the data that was supposed to reach the disk, and saying it is
> not up to date makes callers rewrite, re-read or WARN over data that was
> never wrong.
> 
> Set BH_Write_EIO instead, with mark_buffer_write_io_error(), and test it in
> journal_wait_on_commit_record() and in the two commit-phase waits.
> 
> The two changes have to go together, because commit phase 4 waits on a
> mixed list: descriptor blocks are submitted with
> journal_end_buffer_io_sync(), while revoke blocks go through
> write_dirty_buffer() and land in bh_end_write().  bh_end_write() already
> sets BH_Write_EIO, so converting the consumer alone would keep working for
> revoke blocks and silently stop detecting failed descriptor writes.  With
> the handler converted, both halves of the list report the same way.
> 
> mark_buffer_write_io_error() is safe on all of these buffers.  The shadow
> buffers from jbd2_journal_write_metadata_buffer() have no folio and no
> associated mapping, so it does nothing beyond setting the flag.  Descriptor
> and commit blocks are ordinary buffers on the journal device, and marking
> the journal's mapping with the error is what write_dirty_buffer() already
> does for revoke blocks on the same device.
> 
> Signed-off-by: Chao Shi <[email protected]>
> ---
>  fs/jbd2/commit.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
> index 60273cddf434..8912786ebebb 100644
> --- a/fs/jbd2/commit.c
> +++ b/fs/jbd2/commit.c
> @@ -39,7 +39,7 @@ static void journal_end_buffer_io_sync(struct bio *bio)
>  	if (uptodate)
>  		set_buffer_uptodate(bh);
>  	else
> -		clear_buffer_uptodate(bh);
> +		mark_buffer_write_io_error(bh);

There's no need to set uptodate flag here at all (all callers set it before
submitting IO). So you can just simplify this to:

	if (!uptodate)
		mark_buffer_write_io_error(bh);

Also I'd rename the local variable from 'uptodate' to 'ioerr' or something
like that.

								Honza

>  	if (orig_bh) {
>  		clear_and_wake_up_bit(BH_Shadow, &orig_bh->b_state);
>  	}
> @@ -169,7 +169,7 @@ static int journal_wait_on_commit_record(journal_t *journal,
>  	clear_buffer_dirty(bh);
>  	wait_on_buffer(bh);
>  
> -	if (unlikely(!buffer_uptodate(bh)))
> +	if (unlikely(buffer_write_io_error(bh)))
>  		ret = -EIO;
>  	put_bh(bh);            /* One for getblk() */
>  
> @@ -840,7 +840,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
>  		wait_on_buffer(bh);
>  		cond_resched();
>  
> -		if (unlikely(!buffer_uptodate(bh)))
> +		if (unlikely(buffer_write_io_error(bh)))
>  			err = -EIO;
>  		jbd2_unfile_log_bh(bh);
>  		stats.run.rs_blocks_logged++;
> @@ -883,7 +883,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
>  		wait_on_buffer(bh);
>  		cond_resched();
>  
> -		if (unlikely(!buffer_uptodate(bh)))
> +		if (unlikely(buffer_write_io_error(bh)))
>  			err = -EIO;
>  
>  		BUFFER_TRACE(bh, "ph5: control buffer writeout done: unfile");
> -- 
> 2.43.0
> 
-- 
Jan Kara <[email protected]>
SUSE Labs, CR