Re: [PATCH 2/3] ext4: use READ_ONCE for lockless jinode reads
From: Jan Kara
Date: Mon Feb 02 2026 - 11:41:46 EST
On Fri 30-01-26 11:12:31, Li Chen wrote:
> ext4 journal commit callbacks access jbd2_inode fields such as
> i_transaction and i_dirty_start/end without holding journal->j_list_lock.
>
> Use READ_ONCE() for these reads to correct the concurrency assumptions.
>
> Suggested-by: Jan Kara <jack@xxxxxxxx>
> Signed-off-by: Li Chen <me@linux.beauty>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@xxxxxxx>
Honza
> ---
> fs/ext4/inode.c | 6 ++++--
> fs/ext4/super.c | 13 ++++++++-----
> 2 files changed, 12 insertions(+), 7 deletions(-)
>
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index d99296d7315f..2d451388e080 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -3033,11 +3033,13 @@ static int ext4_writepages(struct address_space *mapping,
>
> int ext4_normal_submit_inode_data_buffers(struct jbd2_inode *jinode)
> {
> + loff_t dirty_start = READ_ONCE(jinode->i_dirty_start);
> + loff_t dirty_end = READ_ONCE(jinode->i_dirty_end);
> struct writeback_control wbc = {
> .sync_mode = WB_SYNC_ALL,
> .nr_to_write = LONG_MAX,
> - .range_start = jinode->i_dirty_start,
> - .range_end = jinode->i_dirty_end,
> + .range_start = dirty_start,
> + .range_end = dirty_end,
> };
> struct mpage_da_data mpd = {
> .inode = jinode->i_vfs_inode,
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 5cf6c2b54bbb..acb2bc016fd4 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -521,6 +521,7 @@ static bool ext4_journalled_writepage_needs_redirty(struct jbd2_inode *jinode,
> {
> struct buffer_head *bh, *head;
> struct journal_head *jh;
> + transaction_t *trans = READ_ONCE(jinode->i_transaction);
>
> bh = head = folio_buffers(folio);
> do {
> @@ -539,7 +540,7 @@ static bool ext4_journalled_writepage_needs_redirty(struct jbd2_inode *jinode,
> */
> jh = bh2jh(bh);
> if (buffer_dirty(bh) ||
> - (jh && (jh->b_transaction != jinode->i_transaction ||
> + (jh && (jh->b_transaction != trans ||
> jh->b_next_transaction)))
> return true;
> } while ((bh = bh->b_this_page) != head);
> @@ -550,12 +551,14 @@ static bool ext4_journalled_writepage_needs_redirty(struct jbd2_inode *jinode,
> static int ext4_journalled_submit_inode_data_buffers(struct jbd2_inode *jinode)
> {
> struct address_space *mapping = jinode->i_vfs_inode->i_mapping;
> + loff_t dirty_start = READ_ONCE(jinode->i_dirty_start);
> + loff_t dirty_end = READ_ONCE(jinode->i_dirty_end);
> struct writeback_control wbc = {
> - .sync_mode = WB_SYNC_ALL,
> + .sync_mode = WB_SYNC_ALL,
> .nr_to_write = LONG_MAX,
> - .range_start = jinode->i_dirty_start,
> - .range_end = jinode->i_dirty_end,
> - };
> + .range_start = dirty_start,
> + .range_end = dirty_end,
> + };
> struct folio *folio = NULL;
> int error;
>
> --
> 2.52.0
--
Jan Kara <jack@xxxxxxxx>
SUSE Labs, CR