* Re: [PATCH] ext4: fix quota accounting WARN in bigalloc punch hole
From: Zhang Yi @ 2026-05-29 7:32 UTC (permalink / raw)
To: Qiliang Yuan, Theodore Ts'o, Andreas Dilger, Baokun Li,
Jan Kara, Ojaswin Mujoo, Ritesh Harjani (IBM)
Cc: linux-ext4, linux-kernel, Zijing Yin
In-Reply-To: <20260528-fix-ext4-bigalloc-punch-hole-quota-v2-v1-1-32871356273b@gmail.com>
Hi, Qiliang!
On 5/28/2026 6:21 PM, Qiliang Yuan wrote:
> When doing direct I/O write on a bigalloc filesystem, the allocated
> extent might not cover entire clusters at its boundaries, leaving
> delayed blocks in those boundary clusters. In ext4_es_insert_extent(),
> __revise_pending() inserts new pending reservations for those boundary
> clusters, and the return value (pending=true) was added to resv_used,
> causing ext4_da_update_reserve_space() to incorrectly release the
> quota reservations for those boundary clusters.
>
> Later when PUNCH_HOLE removes the DIO-allocated blocks, the
> extent removal path detects the pending reservation via
> ext4_is_pending() and calls ext4_rereserve_cluster(). This tries
> to reclaim quota from dq_dqb.dqb_curspace back to dqb_rsvspace,
> but since the quota was already incorrectly released, dqb_curspace
> is insufficient, triggering:
>
> WARNING at dquot_reclaim_space_nodirty+0x77c/0x8c0
Hmm, the analysis doesn't seem correct to me. Do you mean the
following case?
# Assume the cluster size is 16KB.
xfs_io -f -c "pwrite 12k 4k" /mnt/foo
xfs_io -d -c "pwrite 0 4k" /mnt/foo
xfs_io -c "fpunch 0 4k" /mnt/foo
During the direct I/O write, quota space will be added in
ext4_mb_new_blocks() because the EXT4_MB_DELALLOC_RESERVED flag is
not set. Therefore, in ext4_es_insert_extent(), we should release the
quota reservations, since this cluster has already been allocated.
Then, in the third operation (punch hole), it will reclaim the added
dqb_curspace. This should not cause an insufficiency.
Am I missing something?
>
> The subsequent delalloc writeback then fires a second WARN from
> dquot_claim_space_nodirty() for the same reason: dqb_rsvspace was
> depleted by the earlier incorrect release.
>
> __es_remove_extent() -> get_rsvd() already correctly excludes
> boundary clusters that still contain delayed blocks from resv_used.
> Adding pending to resv_used double-counts those boundary clusters,
> erroneously releasing reservations that are still needed.
>
> Remove the pending variable and the resv_used += pending addition.
That's not correct. Assume the following case.
# Assume the cluster size is 16KB.
xfs_io -f -c "pwrite 0 16k" /mnt/foo
xfs_io -c "sync_range -w 0 4k" /mnt/foo
Although only part of the cluster is written back, the cluster has been
allocated. Therefore, the quota needs to be claimed. However, since we
only wrote back a portion of a cluster, __es_remove_extent() will not
return the reserved clusters that need to be consumed (i.e., resv_used
is zero). Therefore, we need to determine whether a new pending
allocation has been created by checking the pending status, so that we
can correctly claim the quota.
Thanks,
Yi
>
> Fixes: c543e2429640 ("ext4: update delalloc data reserve spcae in ext4_es_insert_extent()")
> Reported-by: Zijing Yin <yzjaurora@gmail.com>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221570
> Signed-off-by: Qiliang Yuan <realwujing@gmail.com>
> ---
> fs/ext4/extents_status.c | 4 +---
> 1 file changed, 1 insertion(+), 3 deletions(-)
>
> diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
> index 6e4a191e82191..fefe0bb8ac4d1 100644
> --- a/fs/ext4/extents_status.c
> +++ b/fs/ext4/extents_status.c
> @@ -909,7 +909,7 @@ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
> struct extent_status newes;
> ext4_lblk_t end = lblk + len - 1;
> int err1 = 0, err2 = 0, err3 = 0;
> - int resv_used = 0, pending = 0;
> + int resv_used = 0;
> struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
> struct extent_status *es1 = NULL;
> struct extent_status *es2 = NULL;
> @@ -977,7 +977,6 @@ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
> __free_pending(pr);
> pr = NULL;
> }
> - pending = err3;
> }
> ext4_es_inc_seq(inode);
> error:
> @@ -998,7 +997,6 @@ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
> * any previously delayed allocated clusters instead of claim them
> * again.
> */
> - resv_used += pending;
> if (resv_used)
> ext4_da_update_reserve_space(inode, resv_used,
> delalloc_reserve_used);
>
> ---
> base-commit: eb3f4b7426cfd2b79d65b7d37155480b32259a11
> change-id: 20260528-fix-ext4-bigalloc-punch-hole-quota-v2-2adca315d1ba
>
> Best regards,
^ permalink raw reply
* Re: [PATCH] ext4: convert legacy ext4_debug() to standard pr_debug()
From: Baokun Li @ 2026-05-29 7:32 UTC (permalink / raw)
To: lirongqing
Cc: Theodore Ts'o, Jan Kara, Zhang Yi, Andreas Dilger,
Ojaswin Mujoo, Ritesh Harjani, linux-ext4, linux-kernel
In-Reply-To: <20260521074634.2697-1-lirongqing@baidu.com>
On 2026/5/21 15:46, lirongqing wrote:
> From: Li RongQing <lirongqing@baidu.com>
>
> The ext4 file system historically implemented its own debug logging macro
> ext4_debug() via EXT4FS_DEBUG conditional compilation. This legacy
> implementation suffers from two major drawbacks:
>
> 1. It makes two consecutive un-serialized printk() calls, which can
> lead to severe log interleaving and corruption under multi-core
> concurrent workloads.
> 2. It completely bypasses the standard modern kernel dynamic debug
> (CONFIG_DYNAMIC_DEBUG) infrastructure.
>
> Clean up the legacy implementation by leveraging pr_debug(). This squashes
> the multiple printk() calls into a single atomic execution, ensuring
> log integrity, while seamlessly hooking ext4 into the kernel's native
> dynamic debug framework.
>
> The redundant __FILE__ and __LINE__ macros are intentionally removed from
> the string format because the dynamic debug infrastructure can already
> append them automatically at runtime (via the '+fl' flags) if desired.
> This avoids redundancy and double-logging in modern production/debugging
> environments while keeping the macro clean and robust against dangling
> comma compiler errors.
>
> Signed-off-by: Li RongQing <lirongqing@baidu.com>
Thanks for cleaning this up.
> ---
> fs/ext4/ext4.h | 20 ++------------------
> 1 file changed, 2 insertions(+), 18 deletions(-)
>
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 94283a9..39e86ff 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -62,24 +62,8 @@
> */
> #define DOUBLE_CHECK__
>
> -/*
> - * Define EXT4FS_DEBUG to produce debug messages
> - */
> -#undef EXT4FS_DEBUG
> -
This looks like it's just disabling EXT4FS_DEBUG, but it's actually
the only toggle point — enabling debug required manually flipping this
to #define EXT4FS_DEBUG in the source.
Since balloc.c, ialloc.c, and page-io.c still have #ifdef EXT4FS_DEBUG
blocks that weren't cleaned up here, it would be cleaner to replace
those with CONFIG_EXT4_DEBUG so they fall under a single
Kconfig-controlled umbrella.
> -/*
> - * Debug code
> - */
> -#ifdef EXT4FS_DEBUG
> -#define ext4_debug(f, a...) \
> - do { \
> - printk(KERN_DEBUG "EXT4-fs DEBUG (%s, %d): %s:", \
> - __FILE__, __LINE__, __func__); \
> - printk(KERN_DEBUG f, ## a); \
> - } while (0)
> -#else
> -#define ext4_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
> -#endif
> +#define ext4_debug(fmt, ...) \
> + pr_debug("EXT4-fs DEBUG %s: " fmt, __func__, ##__VA_ARGS__)
Nit: an extra space between __func__, and ##__VA_ARGS__.
>
> /*
> * Turn on EXT_DEBUG to enable ext4_ext_show_path/leaf/move in extents.c
Also, could ext4_debug be moved next to ext_debug and placed under
#ifdef CONFIG_EXT4_DEBUG together, for a cleaner layout?
Cheers,
Baokun
^ permalink raw reply
* Re: [PATCH] ext4: Remove mention of PageWriteback
From: Baokun Li @ 2026-05-29 6:33 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Theodore Ts'o, Andreas Dilger, Jan Kara, Ojaswin Mujoo,
Ritesh Harjani (IBM), Zhang Yi, linux-ext4, linux-kernel
In-Reply-To: <20260526190805.341676-1-willy@infradead.org>
On 2026/5/27 03:08, Matthew Wilcox (Oracle) wrote:
> Update a comment to refer to the concept of writeback instead of the
> (now obsolete) detail of how it's implemented.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Looks good to me.
Reviewed-by: Baokun Li <libaokun@linux.alibaba.com>
> ---
> fs/ext4/page-io.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
> index dc82e7b57e75..bc674aa4a656 100644
> --- a/fs/ext4/page-io.c
> +++ b/fs/ext4/page-io.c
> @@ -168,7 +168,7 @@ static void ext4_release_io_end(ext4_io_end_t *io_end)
> * written. On IO failure, check if journal abort is needed. Note that
> * we are protected from truncate touching same part of extent tree by the
> * fact that truncate code waits for all DIO to finish (thus exclusion from
> - * direct IO is achieved) and also waits for PageWriteback bits. Thus we
> + * direct IO is achieved) and also waits for writeback to complete. Thus we
> * cannot get to ext4_ext_truncate() before all IOs overlapping that range are
> * completed (happens from ext4_free_ioend()).
> */
^ permalink raw reply
* Re: [PATCH] ext4: Remove mention of PageWriteback
From: Ojaswin Mujoo @ 2026-05-29 6:01 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Theodore Ts'o, Andreas Dilger, Baokun Li, Jan Kara,
Ritesh Harjani (IBM), Zhang Yi, linux-ext4, linux-kernel
In-Reply-To: <20260526190805.341676-1-willy@infradead.org>
On Tue, May 26, 2026 at 08:08:02PM +0100, Matthew Wilcox (Oracle) wrote:
> Update a comment to refer to the concept of writeback instead of the
> (now obsolete) detail of how it's implemented.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
> fs/ext4/page-io.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
> index dc82e7b57e75..bc674aa4a656 100644
> --- a/fs/ext4/page-io.c
> +++ b/fs/ext4/page-io.c
> @@ -168,7 +168,7 @@ static void ext4_release_io_end(ext4_io_end_t *io_end)
> * written. On IO failure, check if journal abort is needed. Note that
> * we are protected from truncate touching same part of extent tree by the
> * fact that truncate code waits for all DIO to finish (thus exclusion from
> - * direct IO is achieved) and also waits for PageWriteback bits. Thus we
> + * direct IO is achieved) and also waits for writeback to complete. Thus we
Looks good,
Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Regards,
Ojaswin
> * cannot get to ext4_ext_truncate() before all IOs overlapping that range are
> * completed (happens from ext4_free_ioend()).
> */
> --
> 2.47.3
>
^ permalink raw reply
* Re: [PATCH v6 06/11] fstests: verify f_fsid for cloned filesystems
From: Darrick J. Wong @ 2026-05-29 4:39 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
In-Reply-To: <e029755044a32de6ec2a0d3391f3ff0089fc3c30.1779939330.git.asj@kernel.org>
On Thu, May 28, 2026 at 12:05:37PM +0800, Anand Jain wrote:
> Verify that the cloned filesystem provides an f_fsid that is persistent
> across mount cycles, yet unique from the original filesystem's f_fsid.
Might want to add that last part to the test description itself, because
otherwise I don't know what 'verify' means.
> Signed-off-by: Anand Jain <asj@kernel.org>
> ---
> tests/generic/802 | 67 +++++++++++++++++++++++++++++++++++++++++++
> tests/generic/802.out | 7 +++++
> 2 files changed, 74 insertions(+)
> create mode 100644 tests/generic/802
> create mode 100644 tests/generic/802.out
>
> diff --git a/tests/generic/802 b/tests/generic/802
> new file mode 100644
> index 000000000000..653e74e11b53
> --- /dev/null
> +++ b/tests/generic/802
> @@ -0,0 +1,67 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2026 Anand Jain <asj@kernel.org>. All Rights Reserved.
> +#
> +# FS QA Test 802
> +# Verify f_fsid and s_uuid of cloned filesystems across mount cycle.
> +
> +. ./common/preamble
> +
> +_begin_fstest auto quick mount clone
> +
> +_require_test
> +_require_block_device $TEST_DEV
> +_require_loop
> +
> +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \
> + "btrfs: use on-disk uuid for s_uuid in temp_fsid mounts"
> +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \
> + "btrfs: derive f_fsid from on-disk fsuuid and dev_t"
_fixed_by_fs_commit?
> +
> +_cleanup()
> +{
> + cd /
> + rm -r -f $tmp.*
> + umount $mnt1 $mnt2 2>/dev/null
> + _loop_image_destroy "${devs[@]}" 2> /dev/null
> +}
> +
> +# Setup base loop device and its clone
> +devs=()
> +_loop_image_create_clone devs
> +mkdir -p $TEST_DIR/$seq
> +mnt1=$TEST_DIR/$seq/mnt1
> +mnt2=$TEST_DIR/$seq/mnt2
> +mkdir -p $mnt1
> +mkdir -p $mnt2
> +
> +# Mount both filesystems simultaneously using mandatory clone mount options
> +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
> + _fail "Failed to mount dev1"
> +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
> + _fail "Failed to mount dev2"
> +
> +# Capture baseline filesystem IDs for comparison
> +fsid_scratch=$(stat -f -c "%i" $mnt1)
> +fsid_clone=$(stat -f -c "%i" $mnt2)
> +
> +echo "**** fsid initially ****"
> +echo $fsid_scratch | sed -e "s/$fsid_scratch/FSID_SCRATCH/g"
> +echo $fsid_clone | sed -e "s/$fsid_clone/FSID_CLONE/g"
Why echo only to sed?
--D
> +
> +# Verify that the fsids remain stable after a mount cycle, even when the
> +# mount order is reversed.
> +echo "**** fsid after mount cycle ****"
> +_unmount $mnt1
> +_unmount $mnt2
> +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
> + _fail "Failed to mount dev2"
> +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
> + _fail "Failed to mount dev1"
> +
> +# Compare post mount-cycle values against the baseline
> +stat -f -c "%i" $mnt1 | sed -e "s/$fsid_scratch/FSID_SCRATCH/g"
> +stat -f -c "%i" $mnt2 | sed -e "s/$fsid_clone/FSID_CLONE/g"
> +
> +status=0
> +exit
> diff --git a/tests/generic/802.out b/tests/generic/802.out
> new file mode 100644
> index 000000000000..d1e008f122bb
> --- /dev/null
> +++ b/tests/generic/802.out
> @@ -0,0 +1,7 @@
> +QA output created by 802
> +**** fsid initially ****
> +FSID_SCRATCH
> +FSID_CLONE
> +**** fsid after mount cycle ****
> +FSID_SCRATCH
> +FSID_CLONE
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH v6 05/11] fstests: verify fanotify isolation on cloned filesystems
From: Darrick J. Wong @ 2026-05-29 4:36 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
In-Reply-To: <ef076b330a047d2f19ed48f5b7166f419433bb73.1779939330.git.asj@kernel.org>
On Thu, May 28, 2026 at 12:05:36PM +0800, Anand Jain wrote:
> Verify that fanotify events are correctly routed to the appropriate
> watcher when cloned filesystems are mounted.
> Helps verify kernel's event notification distinguishes between devices
> sharing the same FSID/UUID.
>
> Signed-off-by: Anand Jain <asj@kernel.org>
> ---
> tests/generic/801 | 135 ++++++++++++++++++++++++++++++++++++++++++
> tests/generic/801.out | 7 +++
> 2 files changed, 142 insertions(+)
> create mode 100644 tests/generic/801
> create mode 100644 tests/generic/801.out
>
> diff --git a/tests/generic/801 b/tests/generic/801
> new file mode 100644
> index 000000000000..3bfb87d41922
> --- /dev/null
> +++ b/tests/generic/801
> @@ -0,0 +1,135 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2026 Anand Jain <asj@kernel.org>. All Rights Reserved.
> +#
> +# FS QA Test 801
> +# Verify fanotify FID functionality on cloned filesystems by setting up
> +# watchers and making sure notifications are in the correct logs files.
> +
> +. ./common/preamble
> +
> +_begin_fstest auto quick mount clone
> +
> +_require_test
> +_require_block_device $TEST_DEV
> +_require_loop
> +_require_command "$FSNOTIFYWAIT_PROG" fsnotifywait
> +_require_unique_f_fsid
> +
> +_cleanup()
> +{
> + cd /
> + [[ -n $pid1 ]] && { kill -TERM "$pid1" 2> /dev/null; wait $pid1; }
> + [[ -n $pid2 ]] && { kill -TERM "$pid2" 2> /dev/null; wait $pid2; }
> +
> + if [ "$semanage_added" = "yes" ]; then
> + semanage permissive -d unconfined_t >/dev/null 2>&1 || true
> + fi
> +
> + umount $mnt1 $mnt2 2>/dev/null
> + _loop_image_destroy "${devs[@]}" 2> /dev/null
> + rm -r -f $tmp.*
> +}
> +
> +# Run fsnotifywait in unbuffered mode to watch filesystem-wide create events
> +monitor_fanotify()
> +{
> + local mmnt=$1
> + exec stdbuf -oL $FSNOTIFYWAIT_PROG -m -F -S -e create "$mmnt" 2>&1
I guess you need stdbuf to force fsnotifywait to run in linebuffered
mode even if you pipe/redirect it somewhere?
> +}
> +
> +# Transform f_fsid into the hi.lo format used in fanotify FID logs
> +fsid_to_fid_parts()
> +{
> + local fsid=$1
> + # Pad to 16 hex chars (64-bit), then split into two 32-bit halves
> + local padded=$(printf '%016x' "0x${fsid}")
> + local hi=$(printf '%x' "0x${padded:0:8}") # strips leading zeros
> + local lo=$(printf '%x' "0x${padded:8:8}") # strips leading zeros
> + echo "${hi}.${lo}"
> +}
> +
> +# Create base loop device and its clone
> +devs=()
> +_loop_image_create_clone devs
> +mkdir -p $TEST_DIR/$seq
> +mnt1=$TEST_DIR/$seq/mnt1
> +mnt2=$TEST_DIR/$seq/mnt2
> +mkdir -p $mnt1
> +mkdir -p $mnt2
> +
> +# Mount both base and clone filesystems using required clone mount options
> +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
> + _fail "Failed to mount dev1"
> +_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
> + _fail "Failed to mount dev2"
> +
> +# Fetch filesystem IDs to verify the kernel can differentiate between them
> +fsid1=$(stat -f -c "%i" $mnt1)
> +fsid2=$(stat -f -c "%i" $mnt2)
> +
> +log1=$tmp.fanotify1
> +log2=$tmp.fanotify2
> +
> +pid1=""
> +pid2=""
> +echo "Setup FID fanotify watchers on both mnt1 and mnt2"
> +
> +# Permit unconfined_t domains when SELinux is enforcing to prevent fanotify
> +# blockages
> +semanage_added="no"
> +if [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then
> + if ! semanage permissive -l | grep -q "unconfined_t"; then
> + semanage permissive -a unconfined_t >/dev/null 2>&1 && semanage_added="yes"
> + fi
> +fi
Is there a cleaner way to manage setting up and automatically undoing
this step?
There might not be, since iirc the suggestion to register cleanup
functions in a cleanups=() array and call them all in reverse order
didn't go anywhere.
> +
> +# Start asynchronous fanotify monitors
> +( monitor_fanotify "$mnt1" > "$log1" ) &
> +pid1=$!
> +( monitor_fanotify "$mnt2" > "$log2" ) &
> +pid2=$!
> +sleep 2
> +
> +echo "Trigger file creation on mnt1"
> +touch $mnt1/file_on_mnt1
> +sync
> +sleep 1
> +
> +echo "Trigger file creation on mnt2"
> +touch $mnt2/file_on_mnt2
> +sync
> +sleep 1
> +
> +echo "Verify fsid in the fanotify"
> +kill $pid1 $pid2
> +wait $pid1 $pid2 2>/dev/null
> +pid1=""
> +pid2=""
> +
> +e_fsid1=$(fsid_to_fid_parts "$fsid1")
> +e_fsid2=$(fsid_to_fid_parts "$fsid2")
> +
> +# Dump debug details to the full log
> +echo $fsid1 $e_fsid1 $fsid2 $e_fsid2 >> $seqres.full
> +cat $log1 >> $seqres.full
> +cat $log2 >> $seqres.full
> +
> +# Ensure monitor 1 only captured events belonging to mnt 1 and fsid 1
> +if grep -qF "$e_fsid1" "$log1" && ! grep -qF "$e_fsid2" "$log1"; then
> + echo "SUCCESS: mnt1 events found"
> +else
> + [ ! -s "$log1" ] && echo " - mnt1 received no events."
> + grep -qF "$e_fsid2" "$log1" && echo " - mnt1 received event from mnt2."
> +fi
> +
> +# Ensure monitor 2 only captured events belonging to mnt 2 and fsid 2
> +if grep -qF "$e_fsid2" "$log2" && ! grep -qF "$e_fsid1" "$log2"; then
> + echo "SUCCESS: mnt2 events found"
> +else
> + [ ! -s "$log2" ] && echo " - mnt2 received no events."
> + grep -qF "$e_fsid1" "$log2" && echo " - mnt2 received event from mnt1."
> +fi
> +
> +status=0
> +exit
> diff --git a/tests/generic/801.out b/tests/generic/801.out
> new file mode 100644
> index 000000000000..d7b318d9f27c
> --- /dev/null
> +++ b/tests/generic/801.out
> @@ -0,0 +1,7 @@
> +QA output created by 801
> +Setup FID fanotify watchers on both mnt1 and mnt2
> +Trigger file creation on mnt1
> +Trigger file creation on mnt2
> +Verify fsid in the fanotify
> +SUCCESS: mnt1 events found
> +SUCCESS: mnt2 events found
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH v6 04/11] fstests: add _require_unique_f_fsid() helper
From: Darrick J. Wong @ 2026-05-29 4:30 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
In-Reply-To: <983ed0f63318c930379ee74220f23aa558c16d51.1779939330.git.asj@kernel.org>
On Thu, May 28, 2026 at 12:05:35PM +0800, Anand Jain wrote:
> Add a helper to check if the target filesystem supports unique f_fsid
> tracking across cloned or snapshot instances.
>
> Certain filesystems like XFS, Btrfs, and F2FS ensure unique f_fsid
> identifiers per filesystem instance. However, Ext4 derives its f_fsid
> directly from its superblock UUID, which leads to identical f_fsid
> values on cloned images until the UUID is manually modified by userspace.
>
> Introduce _require_unique_f_fsid() to allow test cases requiring strict
> f_fsid uniqueness to skip gracefully on unsupported filesystems.
>
> Signed-off-by: Anand Jain <asj@kernel.org>
> ---
> common/rc | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/common/rc b/common/rc
> index 937f478963b4..5446552aed92 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -6314,6 +6314,27 @@ _require_fanotify_ioerrors()
> _notrun "$FSTYP does not support fanotify ioerrors"
> }
>
> +# Ext4 derives f_fsid from the superblock UUID, meaning clones share the
> +# same f_fsid until their UUIDs diverge. Conversely, XFS, Btrfs,
> +# and F2FS ensure f_fsid remains unique per filesystem instance (often by
> +# deriving it from the UUID and underlying block device.)
> +#
> +# Across all filesystems, a UUID collision causes libblkid tools to return
> +# non-deterministic device mappings. It is ultimately the responsibility
"device mappings", as in /dev/disk/by-id/$UUID ?
> +# of the userspace utility or use-case to enforce uniqueness when a clone
> +# diverges. For details, see mailing list thread discussions titled:
> +# "ext4: derive f_fsid from block device to avoid collisions".
How about providing a direct lore link?
--D
> +_require_unique_f_fsid()
> +{
> + # Skip the test if the filesystem does not enforce unique f_fsids
> + # natively. Checking this dynamically requires recreating a clone
> + # layout, so we use a static lookup based on FSTYP.
> + if [ "$FSTYP" == "ext4" ]; then
> + _notrun "Target filesystem ($FSTYP) does not guarantee unique f_fsid on clones."
> + fi
> +}
> +
> +
> # Computes a percentage of the available space in a filesystem and
> # returns that quantity in MB. The percentage must not contain a percent
> # sign ("%").
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH v6 03/11] fstests: add FSNOTIFYWAIT_PROG
From: Darrick J. Wong @ 2026-05-29 4:29 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
In-Reply-To: <f4dc9d5af00133834acf97e1c72232d2a9b34ac6.1779939330.git.asj@kernel.org>
On Thu, May 28, 2026 at 12:05:34PM +0800, Anand Jain wrote:
> Define `FSNOTIFYWAIT_PROG` for an upcoming test case that uses `fsnotifywait`.
>
> Signed-off-by: Anand Jain <asj@kernel.org>
Seems fine to me
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> ---
> common/config | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/common/config b/common/config
> index d5299d5b926f..5661fa0ec310 100644
> --- a/common/config
> +++ b/common/config
> @@ -242,6 +242,7 @@ export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical)
> export PARTED_PROG="$(type -P parted)"
> export XFS_PROPERTY_PROG="$(type -P xfs_property)"
> export FSCRYPTCTL_PROG="$(type -P fscryptctl)"
> +export FSNOTIFYWAIT_PROG="$(type -P fsnotifywait)"
>
> # udev wait functions.
> #
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH v6 02/11] fstests: add _clone_mount_option() helper
From: Darrick J. Wong @ 2026-05-29 4:28 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
In-Reply-To: <4346c80089c61c8f0d62ea696f9d73f2a9669297.1779939330.git.asj@kernel.org>
On Thu, May 28, 2026 at 12:05:33PM +0800, Anand Jain wrote:
> Adds _clone_mount_option() helper function to handle filesystem-specific
> requirements for mounting cloned devices. Abstract the need for -o nouuid
> on XFS.
>
> Signed-off-by: Anand Jain <asj@kernel.org>
> ---
> common/rc | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/common/rc b/common/rc
> index d7e3e0bdfb1e..937f478963b4 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -414,6 +414,23 @@ _scratch_mount_options()
> $SCRATCH_DEV $SCRATCH_MNT
> }
>
> +# Return filesystem-specific mount options required for mounting clone/snapshot
> +# devices.
> +_clone_mount_option()
> +{
> + local mount_opts=""
> +
> + case "$FSTYP" in
> + xfs)
> + # Allow mounting a duplicate filesystem on the same host
> + mount_opts="-o nouuid"
> + ;;
> + *)
> + esac
> +
> + echo $mount_opts
I probably would've just echo'd straight from inside the case statement,
but this otherwise looks ok,
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> +}
> +
> _supports_filetype()
> {
> local dir=$1
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH v6 01/11] fstests: add _loop_image_create_clone() helper
From: Darrick J. Wong @ 2026-05-29 4:27 UTC (permalink / raw)
To: Anand Jain
Cc: fstests, linux-btrfs, linux-ext4, linux-xfs, linux-f2fs-devel,
zlang, hch
In-Reply-To: <421c7cdd5aae27b99d04dddf08c5d9df79c2f790.1779939330.git.asj@kernel.org>
On Thu, May 28, 2026 at 12:05:32PM +0800, Anand Jain wrote:
> Introduce _loop_image_create_clone() and _loop_image_destroy() to mkfs an
> image file and clone it to another image file, and attach a loop device to
> them. And its destroy part.
>
> Signed-off-by: Anand Jain <asj@kernel.org>
> ---
> common/rc | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
>
> diff --git a/common/rc b/common/rc
> index 79189e7e6e94..d7e3e0bdfb1e 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -1520,6 +1520,69 @@ _scratch_resvblks()
> esac
> }
>
> +# Create a small loop image, run an optional tuning function ($2) on it,
> +# clone it, and attach both to loop devices, returned in ($1).
> +# Args:
> +# $1: Nameref to return the array of allocated loop devices [base, clone].
> +# $2: Optional callback function to tune the base filesystem before cloning.
> +_loop_image_create_clone()
> +{
> + local -n _ret=$1
That switch ^^ is very clever. I always wondered how one did indirect
variables in bash.
> + local pre_clone_tune_func="$2"
> + local img_file=$TEST_DIR/${seq}.img
> + local img_file_clone=$TEST_DIR/${seq}_clone.img
> + local size=$(_small_fs_size_mb 128) # Smallest possible
> + local loop_devs
> +
> + # Since we copy the block device image, we keep its size small.
> + _require_fs_space $TEST_DIR $((size * 1024))
> +
> + _create_file_sized $((size * 1024 * 1024)) $img_file ||
> + _fail "Failed: Create $img_file $size"
> +
> + loop_devs=$(_create_loop_device $img_file)
> + _ret=($loop_devs)
Should this check that a loopdev actually got created?
> + case $FSTYP in
> + xfs)
> + _mkfs_dev "-s size=4096" ${loop_devs[0]}
> + ;;
> + btrfs)
> + _mkfs_dev ${loop_devs[0]}
> + ;;
> + *)
> + _mkfs_dev ${loop_devs[0]}
> + ;;
> + esac
> +
> + # Only execute if the function argument is not empty
> + if [ -n "$pre_clone_tune_func" ]; then
> + $pre_clone_tune_func ${loop_devs[0]}
> + fi
> +
> + sync ${loop_devs[0]}
> + cp $img_file $img_file_clone
> +
> + loop_devs="$loop_devs $(_create_loop_device $img_file_clone)"
local lodev="$(_create_loop_device ...)"
test -z "$lodev" && _fail "second loopdev not created"
_ret+=("$lodev")
?
> +
> + _ret=($loop_devs)
> +}
> +
> +# Teardown loop devices and delete their underlying backing image files.
> +# Accepts a list of loop device paths (e.g., /dev/loop0 /dev/loop1).
> +_loop_image_destroy()
> +{
> + for d in "$@"; do
> + # Retrieve the path of the backing file
> + local f=$(losetup --noheadings --output BACK-FILE $d)
> +
> + # Detach the loop device from the backing file
> + _destroy_loop_device "$d"
> +
> + # Clean up the backing disk image file
> + [ -n "$f" ] && rm -f "$f"
> + done
> +}
>
> # Repair scratch filesystem. Returns 0 if the FS is good to go (either no
> # errors found or errors were fixed) and nonzero otherwise; also spits out
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH] ext4: validate dx count against limit in dx_csum
From: Andreas Dilger @ 2026-05-29 0:09 UTC (permalink / raw)
To: Artem Blagodarenko; +Cc: linux-ext4, Theodore Ts'o
In-Reply-To: <20260528160557.6956-1-ablagodarenko@thelustrecollective.com>
On May 28, 2026, at 10:05, Artem Blagodarenko <artem.blagodarenko@gmail.com> wrote:
>
> From: Artem Blagodarenko <artem.blagodarenko@gmail.com>
>
> Sashiko AI, during inspection of the "ext4: replace ext4_dir_entry
> with ext4_dir_entry_2" series, reported a missing bounds check on
> count against limit in DX block checksum verification and setup
> paths.
>
> The code validates that limit fits within block boundaries, but does
> not verify that count <= limit. A maliciously crafted filesystem image
> could therefore provide an artificially large count value.
>
> Since ext4_dx_csum() uses count to determine the checksum region
> size, this may result in an out-of-bounds access crossing page
> boundaries into unmapped memory, potentially leading to a crash during
> checksum verification.
>
> The same issue exists in ext4_dx_csum_set().
>
> The bug was not introduced by the patch under review, so the fix is sent
> separately.
>
> Signed-off-by: Artem Blagodarenko artem.blagodarenko@gmail.com
Reviewed-by: Andreas Dilger <adilger@dilger.ca <mailto:adilger@dilger.ca>>
> ---
> fs/ext4/namei.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index a316fc2ac41b..20b7bac69889 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -472,7 +472,8 @@ static int ext4_dx_csum_verify(struct inode *inode,
> }
> limit = le16_to_cpu(c->limit);
> count = le16_to_cpu(c->count);
> - if (count_offset + (limit * sizeof(struct dx_entry)) >
> + if (!count || count > limit ||
> + count_offset + (limit * sizeof(struct dx_entry)) >
> EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) {
> warn_no_space_for_csum(inode);
> return 0;
> @@ -501,7 +502,8 @@ static void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry_2 *diren
> }
> limit = le16_to_cpu(c->limit);
> count = le16_to_cpu(c->count);
> - if (count_offset + (limit * sizeof(struct dx_entry)) >
> + if (!count || count > limit ||
> + count_offset + (limit * sizeof(struct dx_entry)) >
> EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) {
> warn_no_space_for_csum(inode);
> return;
> --
> 2.43.7
>
Cheers, Andreas
^ permalink raw reply
* [PATCH v2 16/34] jbd2: Convert journal commit to bh_submit()
From: Matthew Wilcox (Oracle) @ 2026-05-28 17:31 UTC (permalink / raw)
To: Jan Kara
Cc: Matthew Wilcox (Oracle), Christian Brauner, Christoph Hellwig,
linux-fsdevel, Theodore Ts'o, linux-ext4
In-Reply-To: <20260528173150.1093780-1-willy@infradead.org>
Avoid an extra indirect function call by using bh_submit()
instead of submit_bh() in journal_submit_commit_record()
and jbd2_journal_commit_transaction(). These both use
journal_end_buffer_io_sync(), so it's more straightforward to do them
both at once.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Acked-by: Theodore Ts'o <tytso@mit.edu>
Cc: linux-ext4@vger.kernel.org
---
fs/jbd2/commit.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 8cf61e7185c4..4e91593d27e5 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -29,8 +29,10 @@
/*
* IO end handler for temporary buffer_heads handling writes to the journal.
*/
-static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+static void journal_end_buffer_io_sync(struct bio *bio)
{
+ struct buffer_head *bh;
+ bool uptodate = bio_endio_bh(bio, &bh);
struct buffer_head *orig_bh = bh->b_private;
BUFFER_TRACE(bh, "");
@@ -147,13 +149,12 @@ static int journal_submit_commit_record(journal_t *journal,
lock_buffer(bh);
clear_buffer_dirty(bh);
set_buffer_uptodate(bh);
- bh->b_end_io = journal_end_buffer_io_sync;
if (journal->j_flags & JBD2_BARRIER &&
!jbd2_has_feature_async_commit(journal))
write_flags |= REQ_PREFLUSH | REQ_FUA;
- submit_bh(write_flags, bh);
+ bh_submit(bh, write_flags, journal_end_buffer_io_sync);
*cbh = bh;
return 0;
}
@@ -751,9 +752,9 @@ void jbd2_journal_commit_transaction(journal_t *journal)
lock_buffer(bh);
clear_buffer_dirty(bh);
set_buffer_uptodate(bh);
- bh->b_end_io = journal_end_buffer_io_sync;
- submit_bh(REQ_OP_WRITE | JBD2_JOURNAL_REQ_FLAGS,
- bh);
+ bh_submit(bh,
+ REQ_OP_WRITE | JBD2_JOURNAL_REQ_FLAGS,
+ journal_end_buffer_io_sync);
}
cond_resched();
--
2.47.3
^ permalink raw reply related
* [PATCH v2 15/34] ext4: Convert ext4_commit_super() to bh_submit()
From: Matthew Wilcox (Oracle) @ 2026-05-28 17:31 UTC (permalink / raw)
To: Jan Kara
Cc: Matthew Wilcox (Oracle), Christian Brauner, Christoph Hellwig,
linux-fsdevel, Theodore Ts'o, linux-ext4
In-Reply-To: <20260528173150.1093780-1-willy@infradead.org>
Avoid an extra indirect function call and changing the buffer refcount
by using bh_submit() instead of submit_bh().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Acked-by: Theodore Ts'o <tytso@mit.edu>
Cc: linux-ext4@vger.kernel.org
---
fs/ext4/super.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index bc7faedcb8e4..7283108d7609 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -6316,12 +6316,10 @@ static int ext4_commit_super(struct super_block *sb)
clear_buffer_write_io_error(sbh);
set_buffer_uptodate(sbh);
}
- get_bh(sbh);
/* Clear potential dirty bit if it was journalled update */
clear_buffer_dirty(sbh);
- sbh->b_end_io = end_buffer_write_sync;
- submit_bh(REQ_OP_WRITE | REQ_SYNC |
- (test_opt(sb, BARRIER) ? REQ_FUA : 0), sbh);
+ bh_submit(sbh, REQ_OP_WRITE | REQ_SYNC |
+ (test_opt(sb, BARRIER) ? REQ_FUA : 0), bh_end_write);
wait_on_buffer(sbh);
if (buffer_write_io_error(sbh)) {
ext4_msg(sb, KERN_ERR, "I/O error while writing "
--
2.47.3
^ permalink raw reply related
* [PATCH v2 17/34] jbd2: Convert jbd2_write_superblock() to bh_submit()
From: Matthew Wilcox (Oracle) @ 2026-05-28 17:31 UTC (permalink / raw)
To: Jan Kara
Cc: Matthew Wilcox (Oracle), Christian Brauner, Christoph Hellwig,
linux-fsdevel, Theodore Ts'o, linux-ext4
In-Reply-To: <20260528173150.1093780-1-willy@infradead.org>
Avoid an extra indirect function call and changing the buffer refcount
by using bh_submit() instead of submit_bh().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Acked-by: Theodore Ts'o <tytso@mit.edu>
Cc: linux-ext4@vger.kernel.org
---
fs/jbd2/journal.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 4f397fcdb13c..2040af8c84cb 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -1820,9 +1820,7 @@ static int jbd2_write_superblock(journal_t *journal, blk_opf_t write_flags)
}
if (jbd2_journal_has_csum_v2or3(journal))
sb->s_checksum = jbd2_superblock_csum(sb);
- get_bh(bh);
- bh->b_end_io = end_buffer_write_sync;
- submit_bh(REQ_OP_WRITE | write_flags, bh);
+ bh_submit(bh, REQ_OP_WRITE | write_flags, bh_end_write);
wait_on_buffer(bh);
if (buffer_write_io_error(bh)) {
clear_buffer_write_io_error(bh);
--
2.47.3
^ permalink raw reply related
* [PATCH v2 14/34] ext4: Convert write_mmp_block_thawed() to bh_submit()
From: Matthew Wilcox (Oracle) @ 2026-05-28 17:31 UTC (permalink / raw)
To: Jan Kara
Cc: Matthew Wilcox (Oracle), Christian Brauner, Christoph Hellwig,
linux-fsdevel, linux-ext4
In-Reply-To: <20260528173150.1093780-1-willy@infradead.org>
Avoid an extra indirect function call and changing the buffer refcount
by using bh_submit() instead of submit_bh().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Cc: linux-ext4@vger.kernel.org
---
fs/ext4/mmp.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c
index 6f57c181ff77..7ce361484b38 100644
--- a/fs/ext4/mmp.c
+++ b/fs/ext4/mmp.c
@@ -46,9 +46,8 @@ static int write_mmp_block_thawed(struct super_block *sb,
ext4_mmp_csum_set(sb, mmp);
lock_buffer(bh);
- bh->b_end_io = end_buffer_write_sync;
- get_bh(bh);
- submit_bh(REQ_OP_WRITE | REQ_SYNC | REQ_META | REQ_PRIO, bh);
+ bh_submit(bh, REQ_OP_WRITE | REQ_SYNC | REQ_META | REQ_PRIO,
+ bh_end_write);
wait_on_buffer(bh);
if (unlikely(!buffer_uptodate(bh)))
return -EIO;
--
2.47.3
^ permalink raw reply related
* [PATCH v2 13/34] ext4: Convert ext4_fc_submit_bh() to bh_submit()
From: Matthew Wilcox (Oracle) @ 2026-05-28 17:31 UTC (permalink / raw)
To: Jan Kara
Cc: Matthew Wilcox (Oracle), Christian Brauner, Christoph Hellwig,
linux-fsdevel, linux-ext4
In-Reply-To: <20260528173150.1093780-1-willy@infradead.org>
Avoid an extra indirect function call by converting
ext4_end_buffer_io_sync() from bh_end_io_t to bio_end_io_t and
calling bh_submit().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Cc: linux-ext4@vger.kernel.org
---
fs/ext4/fast_commit.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
index b3c22636251d..5773b85e43cb 100644
--- a/fs/ext4/fast_commit.c
+++ b/fs/ext4/fast_commit.c
@@ -184,8 +184,11 @@
#include <trace/events/ext4.h>
static struct kmem_cache *ext4_fc_dentry_cachep;
-static void ext4_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+static void ext4_end_buffer_io_sync(struct bio *bio)
{
+ struct buffer_head *bh;
+ bool uptodate = bio_endio_bh(bio, &bh);
+
BUFFER_TRACE(bh, "");
if (uptodate) {
ext4_debug("%s: Block %lld up-to-date",
@@ -659,8 +662,7 @@ static void ext4_fc_submit_bh(struct super_block *sb, bool is_tail)
lock_buffer(bh);
set_buffer_dirty(bh);
set_buffer_uptodate(bh);
- bh->b_end_io = ext4_end_buffer_io_sync;
- submit_bh(REQ_OP_WRITE | write_flags, bh);
+ bh_submit(bh, REQ_OP_WRITE | write_flags, ext4_end_buffer_io_sync);
EXT4_SB(sb)->s_fc_bh = NULL;
}
--
2.47.3
^ permalink raw reply related
* [PATCH v2 12/34] ext4; Convert __ext4_read_bh() to bh_submit()
From: Matthew Wilcox (Oracle) @ 2026-05-28 17:31 UTC (permalink / raw)
To: Jan Kara
Cc: Matthew Wilcox (Oracle), Christian Brauner, Christoph Hellwig,
linux-fsdevel, linux-ext4
In-Reply-To: <20260528173150.1093780-1-willy@infradead.org>
Avoid an extra indirect function call and changing the buffer refcount
by converting ext4_end_bitmap_read() from bh_end_io_t to bio_end_io_t
and calling bh_submit().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Cc: linux-ext4@vger.kernel.org
---
fs/ext4/ext4.h | 10 +++++-----
fs/ext4/ialloc.c | 6 ++++--
fs/ext4/super.c | 12 ++++++------
3 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 94283a991e5c..6af11f0ff1c5 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2959,7 +2959,7 @@ extern unsigned long ext4_count_dirs(struct super_block *);
extern void ext4_mark_bitmap_end(int start_bit, int end_bit, char *bitmap);
extern int ext4_init_inode_table(struct super_block *sb,
ext4_group_t group, int barrier);
-extern void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate);
+void ext4_end_bitmap_read(struct bio *bio);
/* fast_commit.c */
int ext4_fc_info_show(struct seq_file *seq, void *v);
@@ -3184,10 +3184,10 @@ extern struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb,
sector_t block);
extern struct buffer_head *ext4_sb_bread_nofail(struct super_block *sb,
sector_t block);
-extern void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
- bh_end_io_t *end_io, bool simu_fail);
-extern int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
- bh_end_io_t *end_io, bool simu_fail);
+void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
+ bio_end_io_t end_io, bool simu_fail);
+int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
+ bio_end_io_t end_io, bool simu_fail);
extern int ext4_read_bh_lock(struct buffer_head *bh, blk_opf_t op_flags, bool wait);
extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
extern int ext4_seq_options_show(struct seq_file *seq, void *offset);
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 3fd8f0099852..9aacd629a683 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -66,14 +66,16 @@ void ext4_mark_bitmap_end(int start_bit, int end_bit, char *bitmap)
memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
}
-void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate)
+void ext4_end_bitmap_read(struct bio *bio)
{
+ struct buffer_head *bh;
+ bool uptodate = bio_endio_bh(bio, &bh);
+
if (uptodate) {
set_buffer_uptodate(bh);
set_bitmap_uptodate(bh);
}
unlock_buffer(bh);
- put_bh(bh);
}
static int ext4_validate_inode_bitmap(struct super_block *sb,
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 6a77db4d3124..bc7faedcb8e4 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -161,7 +161,7 @@ MODULE_ALIAS("ext3");
static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
- bh_end_io_t *end_io, bool simu_fail)
+ bio_end_io_t end_io, bool simu_fail)
{
if (simu_fail) {
clear_buffer_uptodate(bh);
@@ -176,13 +176,13 @@ static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
*/
clear_buffer_verified(bh);
- bh->b_end_io = end_io ? end_io : end_buffer_read_sync;
- get_bh(bh);
- submit_bh(REQ_OP_READ | op_flags, bh);
+ if (!end_io)
+ end_io = bh_end_read;
+ bh_submit(bh, REQ_OP_READ | op_flags, end_io);
}
void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
- bh_end_io_t *end_io, bool simu_fail)
+ bio_end_io_t end_io, bool simu_fail)
{
BUG_ON(!buffer_locked(bh));
@@ -194,7 +194,7 @@ void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
}
int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
- bh_end_io_t *end_io, bool simu_fail)
+ bio_end_io_t end_io, bool simu_fail)
{
BUG_ON(!buffer_locked(bh));
--
2.47.3
^ permalink raw reply related
* [PATCH v2] jbd2: Remove special jbd2 slabs
From: Matthew Wilcox (Oracle) @ 2026-05-28 17:14 UTC (permalink / raw)
To: Theodore Tso
Cc: Matthew Wilcox (Oracle), Jan Kara, linux-ext4, linux-fsdevel,
Mike Rapoport (Microsoft), Vlastimil Babka, Tal Zussman, Jan Kara
When jbd2 was originally written, kmalloc() would not guarantee memory
alignment for the requested objects. Since commit 59bb47985c1d in 2019,
kmalloc has guaranteed natural alignment for power-of-two allocations.
We can now remove the jbd2 special slabs and just use kmalloc() directly.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
---
v2: Rmove b_size parameter (Tal Zussman)
fs/jbd2/commit.c | 8 +--
fs/jbd2/journal.c | 125 ++----------------------------------------
fs/jbd2/transaction.c | 8 +--
include/linux/jbd2.h | 3 -
4 files changed, 13 insertions(+), 131 deletions(-)
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 4e91593d27e5..9ee573948dd8 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -514,10 +514,8 @@ void jbd2_journal_commit_transaction(journal_t *journal)
* leave undo-committed data.
*/
if (jh->b_committed_data) {
- struct buffer_head *bh = jh2bh(jh);
-
spin_lock(&jh->b_state_lock);
- jbd2_free(jh->b_committed_data, bh->b_size);
+ kfree(jh->b_committed_data);
jh->b_committed_data = NULL;
spin_unlock(&jh->b_state_lock);
}
@@ -978,7 +976,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
* its triggers if they exist, so we can clear that too.
*/
if (jh->b_committed_data) {
- jbd2_free(jh->b_committed_data, bh->b_size);
+ kfree(jh->b_committed_data);
jh->b_committed_data = NULL;
if (jh->b_frozen_data) {
jh->b_committed_data = jh->b_frozen_data;
@@ -986,7 +984,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
jh->b_frozen_triggers = NULL;
}
} else if (jh->b_frozen_data) {
- jbd2_free(jh->b_frozen_data, bh->b_size);
+ kfree(jh->b_frozen_data);
jh->b_frozen_data = NULL;
jh->b_frozen_triggers = NULL;
}
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 2040af8c84cb..1621b49de003 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -95,8 +95,6 @@ EXPORT_SYMBOL(jbd2_journal_release_jbd_inode);
EXPORT_SYMBOL(jbd2_journal_begin_ordered_truncate);
EXPORT_SYMBOL(jbd2_inode_cache);
-static int jbd2_journal_create_slab(size_t slab_size);
-
#ifdef CONFIG_JBD2_DEBUG
void __jbd2_debug(int level, const char *file, const char *func,
unsigned int line, const char *fmt, ...)
@@ -385,10 +383,10 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
goto escape_done;
spin_unlock(&jh_in->b_state_lock);
- tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS | __GFP_NOFAIL);
+ tmp = kmalloc(bh_in->b_size, GFP_NOFS | __GFP_NOFAIL);
spin_lock(&jh_in->b_state_lock);
if (jh_in->b_frozen_data) {
- jbd2_free(tmp, bh_in->b_size);
+ kfree(tmp);
goto copy_done;
}
@@ -2062,14 +2060,6 @@ EXPORT_SYMBOL(jbd2_journal_update_sb_errno);
int jbd2_journal_load(journal_t *journal)
{
int err;
- journal_superblock_t *sb = journal->j_superblock;
-
- /*
- * Create a slab for this blocksize
- */
- err = jbd2_journal_create_slab(be32_to_cpu(sb->s_blocksize));
- if (err)
- return err;
/* Let the recovery code check whether it needs to recover any
* data from the journal. */
@@ -2697,108 +2687,6 @@ size_t journal_tag_bytes(journal_t *journal)
return sz - sizeof(__u32);
}
-/*
- * JBD memory management
- *
- * These functions are used to allocate block-sized chunks of memory
- * used for making copies of buffer_head data. Very often it will be
- * page-sized chunks of data, but sometimes it will be in
- * sub-page-size chunks. (For example, 16k pages on Power systems
- * with a 4k block file system.) For blocks smaller than a page, we
- * use a SLAB allocator. There are slab caches for each block size,
- * which are allocated at mount time, if necessary, and we only free
- * (all of) the slab caches when/if the jbd2 module is unloaded. For
- * this reason we don't need to a mutex to protect access to
- * jbd2_slab[] allocating or releasing memory; only in
- * jbd2_journal_create_slab().
- */
-#define JBD2_MAX_SLABS 8
-static struct kmem_cache *jbd2_slab[JBD2_MAX_SLABS];
-
-static const char *jbd2_slab_names[JBD2_MAX_SLABS] = {
- "jbd2_1k", "jbd2_2k", "jbd2_4k", "jbd2_8k",
- "jbd2_16k", "jbd2_32k", "jbd2_64k", "jbd2_128k"
-};
-
-
-static void jbd2_journal_destroy_slabs(void)
-{
- int i;
-
- for (i = 0; i < JBD2_MAX_SLABS; i++) {
- kmem_cache_destroy(jbd2_slab[i]);
- jbd2_slab[i] = NULL;
- }
-}
-
-static int jbd2_journal_create_slab(size_t size)
-{
- static DEFINE_MUTEX(jbd2_slab_create_mutex);
- int i = order_base_2(size) - 10;
- size_t slab_size;
-
- if (size == PAGE_SIZE)
- return 0;
-
- if (i >= JBD2_MAX_SLABS)
- return -EINVAL;
-
- if (unlikely(i < 0))
- i = 0;
- mutex_lock(&jbd2_slab_create_mutex);
- if (jbd2_slab[i]) {
- mutex_unlock(&jbd2_slab_create_mutex);
- return 0; /* Already created */
- }
-
- slab_size = 1 << (i+10);
- jbd2_slab[i] = kmem_cache_create(jbd2_slab_names[i], slab_size,
- slab_size, 0, NULL);
- mutex_unlock(&jbd2_slab_create_mutex);
- if (!jbd2_slab[i]) {
- printk(KERN_EMERG "JBD2: no memory for jbd2_slab cache\n");
- return -ENOMEM;
- }
- return 0;
-}
-
-static struct kmem_cache *get_slab(size_t size)
-{
- int i = order_base_2(size) - 10;
-
- BUG_ON(i >= JBD2_MAX_SLABS);
- if (unlikely(i < 0))
- i = 0;
- BUG_ON(jbd2_slab[i] == NULL);
- return jbd2_slab[i];
-}
-
-void *jbd2_alloc(size_t size, gfp_t flags)
-{
- void *ptr;
-
- BUG_ON(size & (size-1)); /* Must be a power of 2 */
-
- if (size < PAGE_SIZE)
- ptr = kmem_cache_alloc(get_slab(size), flags);
- else
- ptr = (void *)__get_free_pages(flags, get_order(size));
-
- /* Check alignment; SLUB has gotten this wrong in the past,
- * and this can lead to user data corruption! */
- BUG_ON(((unsigned long) ptr) & (size-1));
-
- return ptr;
-}
-
-void jbd2_free(void *ptr, size_t size)
-{
- if (size < PAGE_SIZE)
- kmem_cache_free(get_slab(size), ptr);
- else
- free_pages((unsigned long)ptr, get_order(size));
-};
-
/*
* Journal_head storage management
*/
@@ -2972,15 +2860,15 @@ static void __journal_remove_journal_head(struct buffer_head *bh)
clear_buffer_jbd(bh);
}
-static void journal_release_journal_head(struct journal_head *jh, size_t b_size)
+static void journal_release_journal_head(struct journal_head *jh)
{
if (jh->b_frozen_data) {
printk(KERN_WARNING "%s: freeing b_frozen_data\n", __func__);
- jbd2_free(jh->b_frozen_data, b_size);
+ kfree(jh->b_frozen_data);
}
if (jh->b_committed_data) {
printk(KERN_WARNING "%s: freeing b_committed_data\n", __func__);
- jbd2_free(jh->b_committed_data, b_size);
+ kfree(jh->b_committed_data);
}
journal_free_journal_head(jh);
}
@@ -2999,7 +2887,7 @@ void jbd2_journal_put_journal_head(struct journal_head *jh)
if (!jh->b_jcount) {
__journal_remove_journal_head(bh);
jbd_unlock_bh_journal_head(bh);
- journal_release_journal_head(jh, bh->b_size);
+ journal_release_journal_head(jh);
__brelse(bh);
} else {
jbd_unlock_bh_journal_head(bh);
@@ -3141,7 +3029,6 @@ static void jbd2_journal_destroy_caches(void)
jbd2_journal_destroy_handle_cache();
jbd2_journal_destroy_inode_cache();
jbd2_journal_destroy_transaction_cache();
- jbd2_journal_destroy_slabs();
}
static int __init journal_init(void)
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 4885903bbd10..48ddb566d12d 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -1131,7 +1131,7 @@ do_get_write_access(handle_t *handle, struct journal_head *jh,
if (!frozen_buffer) {
JBUFFER_TRACE(jh, "allocate memory for buffer");
spin_unlock(&jh->b_state_lock);
- frozen_buffer = jbd2_alloc(jh2bh(jh)->b_size,
+ frozen_buffer = kmalloc(jh2bh(jh)->b_size,
GFP_NOFS | __GFP_NOFAIL);
goto repeat;
}
@@ -1159,7 +1159,7 @@ do_get_write_access(handle_t *handle, struct journal_head *jh,
out:
if (unlikely(frozen_buffer)) /* It's usually NULL */
- jbd2_free(frozen_buffer, bh->b_size);
+ kfree(frozen_buffer);
JBUFFER_TRACE(jh, "exit");
return error;
@@ -1424,7 +1424,7 @@ int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh)
repeat:
if (!jh->b_committed_data)
- committed_data = jbd2_alloc(jh2bh(jh)->b_size,
+ committed_data = kmalloc(jh2bh(jh)->b_size,
GFP_NOFS|__GFP_NOFAIL);
spin_lock(&jh->b_state_lock);
@@ -1445,7 +1445,7 @@ int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh)
out:
jbd2_journal_put_journal_head(jh);
if (unlikely(committed_data))
- jbd2_free(committed_data, bh->b_size);
+ kfree(committed_data);
return err;
}
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
index 7e785aa6d35d..b68561187e90 100644
--- a/include/linux/jbd2.h
+++ b/include/linux/jbd2.h
@@ -63,9 +63,6 @@ void __jbd2_debug(int level, const char *file, const char *func,
#define jbd2_debug(n, fmt, a...) no_printk(fmt, ##a)
#endif
-extern void *jbd2_alloc(size_t size, gfp_t flags);
-extern void jbd2_free(void *ptr, size_t size);
-
#define JBD2_MIN_JOURNAL_BLOCKS 1024
#define JBD2_DEFAULT_FAST_COMMIT_BLOCKS 256
--
2.47.3
^ permalink raw reply related
* [PATCH] ext4: validate dx count against limit in dx_csum
From: Artem Blagodarenko @ 2026-05-28 16:05 UTC (permalink / raw)
To: linux-ext4; +Cc: adilger.kernel, Artem Blagodarenko
From: Artem Blagodarenko <artem.blagodarenko@gmail.com>
Sashiko AI, during inspection of the "ext4: replace ext4_dir_entry
with ext4_dir_entry_2" series, reported a missing bounds check on
count against limit in DX block checksum verification and setup
paths.
The code validates that limit fits within block boundaries, but does
not verify that count <= limit. A maliciously crafted filesystem image
could therefore provide an artificially large count value.
Since ext4_dx_csum() uses count to determine the checksum region
size, this may result in an out-of-bounds access crossing page
boundaries into unmapped memory, potentially leading to a crash during
checksum verification.
The same issue exists in ext4_dx_csum_set().
The bug was not introduced by the patch under review, so the fix is sent
separately.
Signed-off-by: Artem Blagodarenko artem.blagodarenko@gmail.com
---
fs/ext4/namei.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index a316fc2ac41b..20b7bac69889 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -472,7 +472,8 @@ static int ext4_dx_csum_verify(struct inode *inode,
}
limit = le16_to_cpu(c->limit);
count = le16_to_cpu(c->count);
- if (count_offset + (limit * sizeof(struct dx_entry)) >
+ if (!count || count > limit ||
+ count_offset + (limit * sizeof(struct dx_entry)) >
EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) {
warn_no_space_for_csum(inode);
return 0;
@@ -501,7 +502,8 @@ static void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry_2 *diren
}
limit = le16_to_cpu(c->limit);
count = le16_to_cpu(c->count);
- if (count_offset + (limit * sizeof(struct dx_entry)) >
+ if (!count || count > limit ||
+ count_offset + (limit * sizeof(struct dx_entry)) >
EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) {
warn_no_space_for_csum(inode);
return;
--
2.43.7
^ permalink raw reply related
* Re: [PATCH v4 08/23] ext4: implement buffered write path using iomap
From: Darrick J. Wong @ 2026-05-28 15:40 UTC (permalink / raw)
To: Ojaswin Mujoo
Cc: Zhang Yi, linux-ext4, linux-fsdevel, linux-kernel, tytso,
adilger.kernel, libaokun, jack, ritesh.list, hch, yi.zhang,
yizhang089, yangerkun, yukuai
In-Reply-To: <ahXUBgoivo5CFl39@li-dc0c254c-257c-11b2-a85c-98b6c1322444.ibm.com>
On Tue, May 26, 2026 at 10:40:30PM +0530, Ojaswin Mujoo wrote:
> On Mon, May 11, 2026 at 03:23:28PM +0800, Zhang Yi wrote:
> > From: Zhang Yi <yi.zhang@huawei.com>
> >
> > Introduce two new iomap_ops instances for ext4 buffered writes:
> >
> > - ext4_iomap_buffered_da_write_ops: for delayed allocation mode, using
> > ext4_da_map_blocks() to map delalloc extents.
> > - ext4_iomap_buffered_write_ops: for non-delayed allocation mode, using
> > ext4_iomap_get_blocks() to directly allocate blocks.
> >
> > Also add ext4_iomap_valid() for the iomap infrastructure to check extent
> > validity.
> >
> > Key changes and considerations:
> >
> > - Unwritten extents for new blocks (dioread_nolock always on)
> > Since data=ordered mode is not used to prevent stale data exposure in
> > the non-delayed allocation path, new blocks are always allocated as
> > unwritten extents.
>
> Okay makes sense.
>
> >
> > - Short write and write failure handling
> > a. Delalloc path: On short write or failure, the stale delalloc range
> > must be dropped and its space reservation released. Otherwise, a
> > clean folio may cover leftover delalloc extents, causing
> > inaccurate space reservation accounting.
>
> Hmm, okay so in the usual buffer head path, seems like during a short
> write we still zero the new buffers we couldn't write and keep it dirty
> (folio_zero_new_buffers()). This way they are still written back and
> the delalloc reservations are used up.
>
> However in iomap we don't mark the range that we couldnt write as dirty
> so we need to make sure we clear up the stale delalloc mappings. Is this
> correct?
Yes, that's true of iomap's pagecache handling.
--D
> Regards,
> Ojaswin
>
> > b. Non-delalloc path: No cleanup of allocated blocks is needed on
> > short write.
> >
> > - Lock ordering reversal
> > The folio lock and transaction start ordering is reversed compared to
> > the buffer_head buffered write path. To handle this, the journal
> > handle must be stopped in iomap_begin() callbacks. The lock ordering
> > documentation in super.c has been updated accordingly.
> >
> > Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
> > ---
> > fs/ext4/ext4.h | 4 ++
> > fs/ext4/file.c | 20 +++++-
> > fs/ext4/inode.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++-
> > fs/ext4/super.c | 10 ++-
> > 4 files changed, 192 insertions(+), 7 deletions(-)
> >
> > diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> > index 1e27d73d7427..4832e7f7db82 100644
> > --- a/fs/ext4/ext4.h
> > +++ b/fs/ext4/ext4.h
> > @@ -3057,6 +3057,7 @@ int ext4_walk_page_buffers(handle_t *handle,
> > int do_journal_get_write_access(handle_t *handle, struct inode *inode,
> > struct buffer_head *bh);
> > void ext4_set_inode_mapping_order(struct inode *inode);
> > +int ext4_nonda_switch(struct super_block *sb);
> > #define FALL_BACK_TO_NONDELALLOC 1
> > #define CONVERT_INLINE_DATA 2
> >
> > @@ -3926,6 +3927,9 @@ static inline void ext4_clear_io_unwritten_flag(ext4_io_end_t *io_end)
> >
> > extern const struct iomap_ops ext4_iomap_ops;
> > extern const struct iomap_ops ext4_iomap_report_ops;
> > +extern const struct iomap_ops ext4_iomap_buffered_write_ops;
> > +extern const struct iomap_ops ext4_iomap_buffered_da_write_ops;
> > +extern const struct iomap_write_ops ext4_iomap_write_ops;
> >
> > static inline int ext4_buffer_uptodate(struct buffer_head *bh)
> > {
> > diff --git a/fs/ext4/file.c b/fs/ext4/file.c
> > index eb1a323962b1..7f9bfbbc4a4e 100644
> > --- a/fs/ext4/file.c
> > +++ b/fs/ext4/file.c
> > @@ -299,6 +299,21 @@ static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *from)
> > return count;
> > }
> >
> > +static ssize_t ext4_iomap_buffered_write(struct kiocb *iocb,
> > + struct iov_iter *from)
> > +{
> > + struct inode *inode = file_inode(iocb->ki_filp);
> > + const struct iomap_ops *iomap_ops;
> > +
> > + if (test_opt(inode->i_sb, DELALLOC) && !ext4_nonda_switch(inode->i_sb))
> > + iomap_ops = &ext4_iomap_buffered_da_write_ops;
> > + else
> > + iomap_ops = &ext4_iomap_buffered_write_ops;
> > +
> > + return iomap_file_buffered_write(iocb, from, iomap_ops,
> > + &ext4_iomap_write_ops, NULL);
> > +}
> > +
> > static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
> > struct iov_iter *from)
> > {
> > @@ -313,7 +328,10 @@ static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
> > if (ret <= 0)
> > goto out;
> >
> > - ret = generic_perform_write(iocb, from);
> > + if (ext4_inode_buffered_iomap(inode))
> > + ret = ext4_iomap_buffered_write(iocb, from);
> > + else
> > + ret = generic_perform_write(iocb, from);
> >
> > out:
> > inode_unlock(inode);
> > diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> > index 39577a6b65b9..1ae7d3f4a1c8 100644
> > --- a/fs/ext4/inode.c
> > +++ b/fs/ext4/inode.c
> > @@ -3097,7 +3097,7 @@ static int ext4_dax_writepages(struct address_space *mapping,
> > return ret;
> > }
> >
> > -static int ext4_nonda_switch(struct super_block *sb)
> > +int ext4_nonda_switch(struct super_block *sb)
> > {
> > s64 free_clusters, dirty_clusters;
> > struct ext4_sb_info *sbi = EXT4_SB(sb);
> > @@ -3467,6 +3467,15 @@ static bool ext4_inode_datasync_dirty(struct inode *inode)
> > return inode_state_read_once(inode) & I_DIRTY_DATASYNC;
> > }
> >
> > +static bool ext4_iomap_valid(struct inode *inode, const struct iomap *iomap)
> > +{
> > + return iomap->validity_cookie == READ_ONCE(EXT4_I(inode)->i_es_seq);
> > +}
> > +
> > +const struct iomap_write_ops ext4_iomap_write_ops = {
> > + .iomap_valid = ext4_iomap_valid,
> > +};
> > +
> > static void ext4_set_iomap(struct inode *inode, struct iomap *iomap,
> > struct ext4_map_blocks *map, loff_t offset,
> > loff_t length, unsigned int flags)
> > @@ -3501,6 +3510,8 @@ static void ext4_set_iomap(struct inode *inode, struct iomap *iomap,
> > !ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
> > iomap->flags |= IOMAP_F_MERGED;
> >
> > + iomap->validity_cookie = map->m_seq;
> > +
> > /*
> > * Flags passed to ext4_map_blocks() for direct I/O writes can result
> > * in m_flags having both EXT4_MAP_MAPPED and EXT4_MAP_UNWRITTEN bits
> > @@ -3908,8 +3919,12 @@ const struct iomap_ops ext4_iomap_report_ops = {
> > .iomap_begin = ext4_iomap_begin_report,
> > };
> >
> > +/* Map blocks */
> > +typedef int (ext4_get_blocks_t)(struct inode *, struct ext4_map_blocks *);
> > +
> > static int ext4_iomap_map_blocks(struct inode *inode, loff_t offset,
> > - loff_t length, struct ext4_map_blocks *map)
> > + loff_t length, ext4_get_blocks_t get_blocks,
> > + struct ext4_map_blocks *map)
> > {
> > u8 blkbits = inode->i_blkbits;
> >
> > @@ -3921,6 +3936,9 @@ static int ext4_iomap_map_blocks(struct inode *inode, loff_t offset,
> > map->m_len = min_t(loff_t, (offset + length - 1) >> blkbits,
> > EXT4_MAX_LOGICAL_BLOCK) - map->m_lblk + 1;
> >
> > + if (get_blocks)
> > + return get_blocks(inode, map);
> > +
> > return ext4_map_blocks(NULL, inode, map, 0);
> > }
> >
> > @@ -3938,7 +3956,7 @@ static int ext4_iomap_buffered_read_begin(struct inode *inode, loff_t offset,
> > if (WARN_ON_ONCE(ext4_has_inline_data(inode)))
> > return -ERANGE;
> >
> > - ret = ext4_iomap_map_blocks(inode, offset, length, &map);
> > + ret = ext4_iomap_map_blocks(inode, offset, length, NULL, &map);
> > if (ret < 0)
> > return ret;
> >
> > @@ -3946,6 +3964,147 @@ static int ext4_iomap_buffered_read_begin(struct inode *inode, loff_t offset,
> > return 0;
> > }
> >
> > +static int ext4_iomap_get_blocks(struct inode *inode,
> > + struct ext4_map_blocks *map)
> > +{
> > + loff_t i_size = i_size_read(inode);
> > + handle_t *handle;
> > + int ret;
> > +
> > + /*
> > + * Check if the blocks have already been allocated, this could
> > + * avoid initiating a new journal transaction and return the
> > + * mapping information directly.
> > + */
> > + if ((map->m_lblk + map->m_len) <=
> > + round_up(i_size, i_blocksize(inode)) >> inode->i_blkbits) {
> > + ret = ext4_map_blocks(NULL, inode, map, 0);
> > + if (ret < 0)
> > + return ret;
> > + if (map->m_flags & (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN |
> > + EXT4_MAP_DELAYED))
> > + return 0;
> > + }
> > +
> > + handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS,
> > + ext4_chunk_trans_blocks(inode, map->m_len));
> > + if (IS_ERR(handle))
> > + return PTR_ERR(handle);
> > +
> > + ret = ext4_map_blocks(handle, inode, map,
> > + EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT);
> > + /*
> > + * Stop handle here following the lock ordering of the folio lock
> > + * and the transaction start.
> > + */
> > + ext4_journal_stop(handle);
> > +
> > + return ret;
> > +}
> > +
> > +static int ext4_iomap_buffered_do_write_begin(struct inode *inode,
> > + loff_t offset, loff_t length, unsigned int flags,
> > + struct iomap *iomap, struct iomap *srcmap, bool delalloc)
> > +{
> > + int ret, retries = 0;
> > + struct ext4_map_blocks map;
> > + ext4_get_blocks_t *get_blocks;
> > +
> > + ret = ext4_emergency_state(inode->i_sb);
> > + if (unlikely(ret))
> > + return ret;
> > +
> > + /* Inline data and non-extent are not supported. */
> > + if (WARN_ON_ONCE(ext4_has_inline_data(inode)))
> > + return -ERANGE;
> > + if (WARN_ON_ONCE(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
> > + return -EINVAL;
> > + if (WARN_ON_ONCE(!(flags & IOMAP_WRITE)))
> > + return -EINVAL;
> > +
> > + if (delalloc)
> > + get_blocks = ext4_da_map_blocks;
> > + else
> > + get_blocks = ext4_iomap_get_blocks;
> > +retry:
> > + ret = ext4_iomap_map_blocks(inode, offset, length, get_blocks, &map);
> > + if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
> > + goto retry;
> > + if (ret < 0)
> > + return ret;
> > +
> > + ext4_set_iomap(inode, iomap, &map, offset, length, flags);
> > + return 0;
> > +}
> > +
> > +static int ext4_iomap_buffered_write_begin(struct inode *inode,
> > + loff_t offset, loff_t length, unsigned int flags,
> > + struct iomap *iomap, struct iomap *srcmap)
> > +{
> > + return ext4_iomap_buffered_do_write_begin(inode, offset, length, flags,
> > + iomap, srcmap, false);
> > +}
> > +
> > +static int ext4_iomap_buffered_da_write_begin(struct inode *inode,
> > + loff_t offset, loff_t length, unsigned int flags,
> > + struct iomap *iomap, struct iomap *srcmap)
> > +{
> > + return ext4_iomap_buffered_do_write_begin(inode, offset, length, flags,
> > + iomap, srcmap, true);
> > +}
> > +
> > +/*
> > + * On write failure, drop the stale delayed allocation range and release
> > + * its reserved space for both start and end blocks. Otherwise, we may
> > + * leave a range of delayed extents covered by a clean folio, which can
> > + * result in inaccurate space reservation accounting.
> > + */
> > +static void ext4_iomap_punch_delalloc(struct inode *inode, loff_t offset,
> > + loff_t length, struct iomap *iomap)
> > +{
> > + down_write(&EXT4_I(inode)->i_data_sem);
> > + ext4_es_remove_extent(inode, offset >> inode->i_blkbits,
> > + DIV_ROUND_UP_ULL(length, EXT4_BLOCK_SIZE(inode->i_sb)));
> > + up_write(&EXT4_I(inode)->i_data_sem);
> > +}
> > +
> > +static int ext4_iomap_buffered_da_write_end(struct inode *inode, loff_t offset,
> > + loff_t length, ssize_t written,
> > + unsigned int flags,
> > + struct iomap *iomap)
> > +{
> > + loff_t start_byte, end_byte;
> > +
> > + /* If we didn't reserve the blocks, we're not allowed to punch them. */
> > + if (iomap->type != IOMAP_DELALLOC || !(iomap->flags & IOMAP_F_NEW))
> > + return 0;
> > +
> > + /* Nothing to do if we've written the entire delalloc extent */
> > + start_byte = iomap_last_written_block(inode, offset, written);
> > + end_byte = round_up(offset + length, i_blocksize(inode));
> > + if (start_byte >= end_byte)
> > + return 0;
> > +
> > + filemap_invalidate_lock(inode->i_mapping);
> > + iomap_write_delalloc_release(inode, start_byte, end_byte, flags,
> > + iomap, ext4_iomap_punch_delalloc);
> > + filemap_invalidate_unlock(inode->i_mapping);
> > + return 0;
> > +}
> > +
> > +/*
> > + * Since we always allocate unwritten extents, there is no need for
> > + * iomap_end to clean up allocated blocks on a short write.
> > + */
> > +const struct iomap_ops ext4_iomap_buffered_write_ops = {
> > + .iomap_begin = ext4_iomap_buffered_write_begin,
> > +};
> > +
> > +const struct iomap_ops ext4_iomap_buffered_da_write_ops = {
> > + .iomap_begin = ext4_iomap_buffered_da_write_begin,
> > + .iomap_end = ext4_iomap_buffered_da_write_end,
> > +};
> > +
> > const struct iomap_ops ext4_iomap_buffered_read_ops = {
> > .iomap_begin = ext4_iomap_buffered_read_begin,
> > };
> > diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> > index 6a77db4d3124..9bc294b769db 100644
> > --- a/fs/ext4/super.c
> > +++ b/fs/ext4/super.c
> > @@ -104,9 +104,13 @@ static const struct fs_parameter_spec ext4_param_specs[];
> > * -> page lock -> i_data_sem (rw)
> > *
> > * buffered write path:
> > - * sb_start_write -> i_mutex -> mmap_lock
> > - * sb_start_write -> i_mutex -> transaction start -> page lock ->
> > - * i_data_sem (rw)
> > + * sb_start_write -> i_rwsem (w) -> mmap_lock
> > + * - buffer_head path:
> > + * sb_start_write -> i_rwsem (w) -> transaction start -> folio lock ->
> > + * i_data_sem (rw)
> > + * - iomap path:
> > + * sb_start_write -> i_rwsem (w) -> transaction start -> i_data_sem (rw)
> > + * sb_start_write -> i_rwsem (w) -> folio lock (not under an active handle)
> > *
> > * truncate:
> > * sb_start_write -> i_mutex -> invalidate_lock (w) -> i_mmap_rwsem (w) ->
> > --
> > 2.52.0
> >
>
^ permalink raw reply
* Re: [PATCH 17/34] jbd2: Convert jbd2_write_superblock() to bh_submit()
From: Theodore Tso @ 2026-05-28 15:37 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Jan Kara, Christian Brauner, Christoph Hellwig, linux-fsdevel,
linux-ext4
In-Reply-To: <20260525171931.4144395-18-willy@infradead.org>
On Mon, May 25, 2026 at 06:19:10PM +0100, Matthew Wilcox (Oracle) wrote:
> Avoid an extra indirect function call by using bh_submit() instead of
> submit_bh().
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> Cc: linux-ext4@vger.kernel.org
Acked-by: Theodore Ts'o <tytso@mit.edu>
^ permalink raw reply
* Re: [PATCH 16/34] jbd2: Convert journal commit to bh_submit()
From: Theodore Tso @ 2026-05-28 15:36 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Jan Kara, Christian Brauner, Christoph Hellwig, linux-fsdevel,
linux-ext4
In-Reply-To: <20260525171931.4144395-17-willy@infradead.org>
On Mon, May 25, 2026 at 06:19:09PM +0100, Matthew Wilcox (Oracle) wrote:
> Avoid an extra indirect function call by using bh_submit()
> instead of submit_bh() in journal_submit_commit_record()
> and jbd2_journal_commit_transaction(). These both use
> journal_end_buffer_io_sync(), so it's more straightforward to do them
> both at once.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> Cc: linux-ext4@vger.kernel.org
Acked-by: Theodore Ts'o <tytso@mit.edu>
^ permalink raw reply
* Re: [PATCH 15/34] ext4: Convert ext4_commit_super() to bh_submit()
From: Theodore Tso @ 2026-05-28 15:09 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Jan Kara, Christian Brauner, Christoph Hellwig, linux-fsdevel,
linux-ext4
In-Reply-To: <20260525171931.4144395-16-willy@infradead.org>
On Mon, May 25, 2026 at 06:19:08PM +0100, Matthew Wilcox (Oracle) wrote:
> Avoid an extra indirect function call by using bh_submit() instead of
> submit_bh().
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> Cc: linux-ext4@vger.kernel.org
Acked-by: Theodore Ts'o <tytso@mit.edu>
^ permalink raw reply
* (no subject)
From: Syzkaller syz-bot-8448545c66-w8mzv @ 2026-05-28 14:56 UTC (permalink / raw)
To: eburkov, jack, linux-ext4, linux-kernel, tytso
Content-Type: multipart/mixed; boundary="===============0636582995431990448=="
MIME-Version: 1.0
To: eburkov@ptsecurity.com, jack@suse.com, linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, tytso@mit.edu
From: "syzbot" <fuzzing.farm+b4c70f9f7908435cb4f7@ptsecurity.com>
Reply-To:
Subject: [syzbot] INFO: task hung in do_get_write_access
--===============0636582995431990448==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Hello,
syzbot found the following issue on:
HEAD commit: 100000000000 6.9.6
git tree: https://github.com/torvalds/linux.git 6.9.6
console output: https://None.appspot.com/x/log.txt?x=16a08000000000
kernel config: https://None.appspot.com/x/.config?x=31f7bb9bd056e52b
dashboard link: https://None.appspot.com/bug?extid=b4c70f9f7908435cb4f7
compiler: gcc (Debian 12.2.0-14+deb12u1) 12.2.0
Unfortunately, I don't have any reproducer for this issue yet.
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: fuzzing.farm+b4c70f9f7908435cb4f7@ptsecurity.com
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
INFO: task kworker/u8:2:32 blocked for more than 143 seconds.
Tainted: G O 6.9.6 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:kworker/u8:2 state:D stack:24272 pid:32 tgid:32 ppid:2 flags:0x00004000
Workqueue: writeback wb_workfn (flush-8:0)
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0xb9b/0x2d20 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0xea/0x380 kernel/sched/core.c:6838
io_schedule+0xca/0x150 kernel/sched/core.c:9044
bit_wait_io+0x1b/0xf0 kernel/sched/wait_bit.c:209
__wait_on_bit+0x63/0x170 kernel/sched/wait_bit.c:49
out_of_line_wait_on_bit+0xe1/0x110 kernel/sched/wait_bit.c:64
wait_on_bit_io include/linux/wait_bit.h:101 [inline]
do_get_write_access+0x91c/0x1270 fs/jbd2/transaction.c:1111
jbd2_journal_get_write_access+0x20a/0x2a0 fs/jbd2/transaction.c:1260
__ext4_journal_get_write_access+0x6e/0x3e0 fs/ext4/ext4_jbd2.c:239
ext4_mb_mark_context+0x1f3/0xe50 fs/ext4/mballoc.c:4005
ext4_mb_mark_diskspace_used+0x461/0x990 fs/ext4/mballoc.c:4122
ext4_mb_new_blocks+0x7fc/0x4cc0 fs/ext4/mballoc.c:6224
ext4_ext_map_blocks+0x1ced/0x6180 fs/ext4/extents.c:4317
ext4_map_blocks+0x654/0x1a20 fs/ext4/inode.c:623
mpage_map_one_extent fs/ext4/inode.c:2163 [inline]
mpage_map_and_submit_extent fs/ext4/inode.c:2216 [inline]
ext4_do_writepages+0x1a62/0x35e0 fs/ext4/inode.c:2679
ext4_writepages+0x34e/0x7a0 fs/ext4/inode.c:2768
do_writepages+0x1ca/0x890 mm/page-writeback.c:2612
__writeback_single_inode+0x157/0xee0 fs/fs-writeback.c:1650
writeback_sb_inodes+0x5fb/0x1170 fs/fs-writeback.c:1941
__writeback_inodes_wb+0x10d/0x300 fs/fs-writeback.c:2012
wb_writeback+0x7b3/0xae0 fs/fs-writeback.c:2119
wb_check_background_flush fs/fs-writeback.c:2189 [inline]
wb_do_writeback fs/fs-writeback.c:2277 [inline]
wb_workfn+0x8a1/0xf60 fs/fs-writeback.c:2304
process_one_work+0x93e/0x1ab0 kernel/workqueue.c:3267
process_scheduled_works kernel/workqueue.c:3348 [inline]
worker_thread+0x6b9/0xf60 kernel/workqueue.c:3429
kthread+0x2cc/0x3c0 kernel/kthread.c:388
ret_from_fork+0x56/0x90 arch/x86/kernel/process.c:147
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244
</TASK>
INFO: task kworker/u8:3:41 blocked for more than 143 seconds.
Tainted: G O 6.9.6 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:kworker/u8:3 state:D stack:24280 pid:41 tgid:41 ppid:2 flags:0x00004000
Workqueue: writeback wb_workfn (flush-8:0)
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0xb9b/0x2d20 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0xea/0x380 kernel/sched/core.c:6838
io_schedule+0xca/0x150 kernel/sched/core.c:9044
blk_mq_get_tag+0x5ec/0xb90 block/blk-mq-tag.c:187
__blk_mq_alloc_requests+0x7eb/0x1e40 block/blk-mq.c:499
blk_mq_get_new_requests block/blk-mq.c:2890 [inline]
blk_mq_submit_bio+0x12b1/0x1f90 block/blk-mq.c:2988
__submit_bio+0x89/0x250 block/blk-core.c:621
__submit_bio_noacct_mq block/blk-core.c:700 [inline]
submit_bio_noacct_nocheck+0x92f/0xcd0 block/blk-core.c:729
submit_bio_noacct+0x278/0xff0 block/blk-core.c:839
submit_bio+0xd5/0x480 block/blk-core.c:881
ext4_io_submit+0xac/0x150 fs/ext4/page-io.c:378
ext4_do_writepages+0xc8b/0x35e0 fs/ext4/inode.c:2636
ext4_writepages+0x34e/0x7a0 fs/ext4/inode.c:2768
do_writepages+0x1ca/0x890 mm/page-writeback.c:2612
__writeback_single_inode+0x157/0xee0 fs/fs-writeback.c:1650
writeback_sb_inodes+0x5fb/0x1170 fs/fs-writeback.c:1941
__writeback_inodes_wb+0x10d/0x300 fs/fs-writeback.c:2012
wb_writeback+0x7b3/0xae0 fs/fs-writeback.c:2119
wb_check_background_flush fs/fs-writeback.c:2189 [inline]
wb_do_writeback fs/fs-writeback.c:2277 [inline]
wb_workfn+0x8a1/0xf60 fs/fs-writeback.c:2304
process_one_work+0x93e/0x1ab0 kernel/workqueue.c:3267
process_scheduled_works kernel/workqueue.c:3348 [inline]
worker_thread+0x6b9/0xf60 kernel/workqueue.c:3429
kthread+0x2cc/0x3c0 kernel/kthread.c:388
ret_from_fork+0x56/0x90 arch/x86/kernel/process.c:147
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244
</TASK>
INFO: task jbd2/sda-8:58 blocked for more than 143 seconds.
Tainted: G O 6.9.6 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:jbd2/sda-8 state:D stack:26824 pid:58 tgid:58 ppid:2 flags:0x00004000
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0xb9b/0x2d20 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0xea/0x380 kernel/sched/core.c:6838
io_schedule+0xca/0x150 kernel/sched/core.c:9044
blk_mq_get_tag+0x5ec/0xb90 block/blk-mq-tag.c:187
__blk_mq_alloc_requests+0x7eb/0x1e40 block/blk-mq.c:499
blk_mq_get_new_requests block/blk-mq.c:2890 [inline]
blk_mq_submit_bio+0x12b1/0x1f90 block/blk-mq.c:2988
__submit_bio+0x89/0x250 block/blk-core.c:621
__submit_bio_noacct_mq block/blk-core.c:700 [inline]
submit_bio_noacct_nocheck+0x92f/0xcd0 block/blk-core.c:729
submit_bio_noacct+0x278/0xff0 block/blk-core.c:839
submit_bio+0xd5/0x480 block/blk-core.c:881
submit_bh_wbc+0x579/0x740 fs/buffer.c:2804
submit_bh+0x24/0x30 fs/buffer.c:2809
jbd2_journal_commit_transaction+0x2cd8/0x6640 fs/jbd2/commit.c:731
kjournald2+0x218/0x970 fs/jbd2/journal.c:201
kthread+0x2cc/0x3c0 kernel/kthread.c:388
ret_from_fork+0x56/0x90 arch/x86/kernel/process.c:147
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244
</TASK>
INFO: task systemd-journal:85 blocked for more than 143 seconds.
Tainted: G O 6.9.6 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:systemd-journal state:D stack:25008 pid:85 tgid:85 ppid:1 flags:0x00000004
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0xb9b/0x2d20 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0xea/0x380 kernel/sched/core.c:6838
io_schedule+0xca/0x150 kernel/sched/core.c:9044
bit_wait_io+0x1b/0xf0 kernel/sched/wait_bit.c:209
__wait_on_bit+0x63/0x170 kernel/sched/wait_bit.c:49
out_of_line_wait_on_bit+0xe1/0x110 kernel/sched/wait_bit.c:64
wait_on_bit_io include/linux/wait_bit.h:101 [inline]
do_get_write_access+0x91c/0x1270 fs/jbd2/transaction.c:1111
jbd2_journal_get_write_access+0x20a/0x2a0 fs/jbd2/transaction.c:1260
__ext4_journal_get_write_access+0x6e/0x3e0 fs/ext4/ext4_jbd2.c:239
ext4_reserve_inode_write+0x144/0x280 fs/ext4/inode.c:5728
__ext4_mark_inode_dirty+0x1b1/0x8b0 fs/ext4/inode.c:5902
ext4_dirty_inode+0xe6/0x140 fs/ext4/inode.c:5939
__mark_inode_dirty+0x1d5/0xda0 fs/fs-writeback.c:2477
generic_update_time+0xd4/0x100 fs/inode.c:1907
inode_update_time fs/inode.c:1920 [inline]
__file_update_time fs/inode.c:2109 [inline]
file_update_time+0x143/0x170 fs/inode.c:2139
ext4_page_mkwrite+0x38c/0x17d0 fs/ext4/inode.c:6057
do_page_mkwrite+0x195/0x3b0 mm/memory.c:3091
wp_page_shared mm/memory.c:3478 [inline]
do_wp_page+0x543/0x2a60 mm/memory.c:3628
handle_pte_fault mm/memory.c:5316 [inline]
__handle_mm_fault+0xcc8/0x26e0 mm/memory.c:5441
handle_mm_fault+0x455/0xad0 mm/memory.c:5606
do_user_addr_fault+0x504/0x1130 arch/x86/mm/fault.c:1331
handle_page_fault arch/x86/mm/fault.c:1474 [inline]
exc_page_fault+0x5e/0xe0 arch/x86/mm/fault.c:1532
_ZN2PT3LLD9Shellcode14HyperTransportL21splice_exc_page_faultEPNS2_7pt_regsEm+0x43/0x50 root/lld/kernel/linux/shellcode/driver/communication/page_fault_hook.cpp:28 [ptsb]
asm_exc_page_fault+0x27/0x30 arch/x86/include/asm/idtentry.h:623
RIP: 0033:0x7f3b9ba7e419
RSP: 002b:00007fffe6506800 EFLAGS: 00010246
RAX: 00007f3b997e7308 RBX: 000055d8e08bfb00 RCX: 00007f3b9baeb7e0
RDX: 00007f3b9baeb200 RSI: 00007fffe65067a8 RDI: 000055d8e08c6e40
RBP: 000055d8e08c6790 R08: 0000000000600ea8 R09: 000000000012c308
R10: 0000000000000002 R11: 0000000000223fe6 R12: 0000000000000024
R13: 00000000000324d0 R14: 0000000000000000 R15: 00007fffe6506820
</TASK>
INFO: task rs:main Q:Reg:141 blocked for more than 143 seconds.
Tainted: G O 6.9.6 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:rs:main Q:Reg state:D stack:26808 pid:141 tgid:136 ppid:1 flags:0x00000000
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0xb9b/0x2d20 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0xea/0x380 kernel/sched/core.c:6838
io_schedule+0xca/0x150 kernel/sched/core.c:9044
bit_wait_io+0x1b/0xf0 kernel/sched/wait_bit.c:209
__wait_on_bit+0x63/0x170 kernel/sched/wait_bit.c:49
out_of_line_wait_on_bit+0xe1/0x110 kernel/sched/wait_bit.c:64
wait_on_bit_io include/linux/wait_bit.h:101 [inline]
do_get_write_access+0x91c/0x1270 fs/jbd2/transaction.c:1111
jbd2_journal_get_write_access+0x20a/0x2a0 fs/jbd2/transaction.c:1260
__ext4_journal_get_write_access+0x6e/0x3e0 fs/ext4/ext4_jbd2.c:239
ext4_reserve_inode_write+0x144/0x280 fs/ext4/inode.c:5728
__ext4_mark_inode_dirty+0x1b1/0x8b0 fs/ext4/inode.c:5902
ext4_dirty_inode+0xe6/0x140 fs/ext4/inode.c:5939
__mark_inode_dirty+0x1d5/0xda0 fs/fs-writeback.c:2477
generic_update_time+0xd4/0x100 fs/inode.c:1907
inode_update_time fs/inode.c:1920 [inline]
__file_update_time fs/inode.c:2109 [inline]
file_modified_flags fs/inode.c:2180 [inline]
file_modified+0x1b7/0x1f0 fs/inode.c:2196
ext4_write_checks fs/ext4/file.c:279 [inline]
ext4_buffered_write_iter+0x107/0x3f0 fs/ext4/file.c:295
ext4_file_write_iter+0x390/0x1900 fs/ext4/file.c:698
call_write_iter include/linux/fs.h:2110 [inline]
new_sync_write fs/read_write.c:497 [inline]
vfs_write+0x786/0x1350 fs/read_write.c:590
_ZN2PT3LLD9ShellcodeL16splice_vfs_writeEP4filePKcmPx+0x57/0x200 root/lld/kernel/linux/shellcode/driver/plugins/filetracer.cpp:363 [ptsb]
ksys_write+0x155/0x290 fs/read_write.c:643
__do_sys_write fs/read_write.c:655 [inline]
__se_sys_write fs/read_write.c:652 [inline]
__x64_sys_write+0x77/0xb0 fs/read_write.c:652
_ZN2PT3LLD9ShellcodeL16SysSpliceGenericEPK7pt_regsPFlS4_ERKNS1_11syscall_dscE root/lld/kernel/linux/shellcode/driver/plugins/syscalls.cpp:395 [inline] [ptsb]
_ZN2PT3LLD9ShellcodeL24splice_syscall_x64_writeEPK7pt_regs+0x51/0x60 root/lld/kernel/linux/shellcode/driver/plugins/syscalls.cpp:587 [ptsb]
x64_sys_call+0x1e65/0x1f10 arch/x86/include/generated/asm/syscalls_64.h:2
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xbf/0x1d0 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fd394ee9fef
RSP: 002b:00007fd38fffe830 EFLAGS: 00000293 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000000000000088 RCX: 00007fd394ee9fef
RDX: 0000000000001000 RSI: 00007fd384024260 RDI: 0000000000000009
RBP: 0000000000001000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000293 R12: 00007fd384024260
R13: 0000000000000000 R14: 0000000000000088 R15: 00007fd384023fa0
</TASK>
INFO: task syz-executor:209 blocked for more than 143 seconds.
Tainted: G O 6.9.6 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz-executor state:D stack:26264 pid:209 tgid:209 ppid:203 flags:0x00000002
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0xb9b/0x2d20 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0xea/0x380 kernel/sched/core.c:6838
io_schedule+0xca/0x150 kernel/sched/core.c:9044
bit_wait_io+0x1b/0xf0 kernel/sched/wait_bit.c:209
__wait_on_bit+0x63/0x170 kernel/sched/wait_bit.c:49
out_of_line_wait_on_bit+0xe1/0x110 kernel/sched/wait_bit.c:64
wait_on_bit_io include/linux/wait_bit.h:101 [inline]
do_get_write_access+0x91c/0x1270 fs/jbd2/transaction.c:1111
jbd2_journal_get_write_access+0x20a/0x2a0 fs/jbd2/transaction.c:1260
__ext4_journal_get_write_access+0x6e/0x3e0 fs/ext4/ext4_jbd2.c:239
ext4_reserve_inode_write+0x144/0x280 fs/ext4/inode.c:5728
__ext4_mark_inode_dirty+0x1b1/0x8b0 fs/ext4/inode.c:5902
ext4_dirty_inode+0xe6/0x140 fs/ext4/inode.c:5939
__mark_inode_dirty+0x1d5/0xda0 fs/fs-writeback.c:2477
generic_update_time+0xd4/0x100 fs/inode.c:1907
inode_update_time fs/inode.c:1920 [inline]
__file_update_time fs/inode.c:2109 [inline]
file_update_time+0x143/0x170 fs/inode.c:2139
ext4_page_mkwrite+0x38c/0x17d0 fs/ext4/inode.c:6057
do_page_mkwrite+0x195/0x3b0 mm/memory.c:3091
do_shared_fault mm/memory.c:4966 [inline]
do_fault mm/memory.c:5028 [inline]
do_pte_missing mm/memory.c:3880 [inline]
handle_pte_fault mm/memory.c:5300 [inline]
__handle_mm_fault+0x12ef/0x26e0 mm/memory.c:5441
handle_mm_fault+0x455/0xad0 mm/memory.c:5606
do_user_addr_fault+0x396/0x1130 arch/x86/mm/fault.c:1382
handle_page_fault arch/x86/mm/fault.c:1474 [inline]
exc_page_fault+0x5e/0xe0 arch/x86/mm/fault.c:1532
_ZN2PT3LLD9Shellcode14HyperTransportL21splice_exc_page_faultEPNS2_7pt_regsEm+0x43/0x50 root/lld/kernel/linux/shellcode/driver/communication/page_fault_hook.cpp:28 [ptsb]
asm_exc_page_fault+0x27/0x30 arch/x86/include/asm/idtentry.h:623
RIP: 0033:0x7f34896debad
RSP: 002b:00007ffe4422bb08 EFLAGS: 00010287
RAX: 00007f3484d93000 RBX: 0000555559d98d00 RCX: 000000000000003f
RDX: 000000000000005d RSI: 0000555559d9af30 RDI: 00007f3484d93000
RBP: 0000555559d97dc0 R08: 0000000000000007 R09: 0000555559d97f10
R10: 311dc10bb6075acf R11: 0000000000000202 R12: 0000000000000001
R13: 0000000000000000 R14: 00007ffe4422bb20 R15: 0000000000000000
</TASK>
INFO: task syz-executor:218 blocked for more than 143 seconds.
Tainted: G O 6.9.6 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz-executor state:D stack:24672 pid:218 tgid:218 ppid:214 flags:0x00000000
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0xb9b/0x2d20 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0xea/0x380 kernel/sched/core.c:6838
io_schedule+0xca/0x150 kernel/sched/core.c:9044
bit_wait_io+0x1b/0xf0 kernel/sched/wait_bit.c:209
__wait_on_bit+0x63/0x170 kernel/sched/wait_bit.c:49
out_of_line_wait_on_bit+0xe1/0x110 kernel/sched/wait_bit.c:64
wait_on_bit_io include/linux/wait_bit.h:101 [inline]
do_get_write_access+0x91c/0x1270 fs/jbd2/transaction.c:1111
jbd2_journal_get_write_access+0x20a/0x2a0 fs/jbd2/transaction.c:1260
__ext4_journal_get_write_access+0x6e/0x3e0 fs/ext4/ext4_jbd2.c:239
__ext4_new_inode+0x1057/0x56f0 fs/ext4/ialloc.c:1088
ext4_mkdir+0x2ab/0xbf0 fs/ext4/namei.c:3013
vfs_mkdir+0x585/0x830 fs/namei.c:4123
do_mkdirat+0x327/0x3d0 fs/namei.c:4146
__do_sys_mkdirat fs/namei.c:4161 [inline]
__se_sys_mkdirat fs/namei.c:4159 [inline]
__x64_sys_mkdirat+0x11c/0x180 fs/namei.c:4159
_ZN2PT3LLD9ShellcodeL16SysSpliceGenericEPK7pt_regsPFlS4_ERKNS1_11syscall_dscE root/lld/kernel/linux/shellcode/driver/plugins/syscalls.cpp:395 [inline] [ptsb]
_ZN2PT3LLD9ShellcodeL26splice_syscall_x64_mkdiratEPK7pt_regs+0x51/0x60 root/lld/kernel/linux/shellcode/driver/plugins/syscalls.cpp:1679 [ptsb]
x64_sys_call+0x1dff/0x1f10 arch/x86/include/generated/asm/syscalls_64.h:259
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xbf/0x1d0 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f1ee7aab997
RSP: 002b:00007ffcd45a4bd8 EFLAGS: 00000206 ORIG_RAX: 0000000000000102
RAX: ffffffffffffffda RBX: 00007f1ee7b1fc1b RCX: 00007f1ee7aab997
RDX: 00000000000001ff RSI: 00007f1ee7b1fc1b RDI: 00000000ffffff9c
RBP: 00007f1ee7c65a38 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000206 R12: 000000000000000c
R13: 0000000000000003 R14: 0000000000000009 R15: 0000000000000000
</TASK>
INFO: task kworker/u8:4:232 blocked for more than 143 seconds.
Tainted: G O 6.9.6 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:kworker/u8:4 state:D stack:25208 pid:232 tgid:232 ppid:2 flags:0x00004000
Workqueue: writeback wb_workfn (flush-8:0)
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5409 [inline]
__schedule+0xb9b/0x2d20 kernel/sched/core.c:6746
__schedule_loop kernel/sched/core.c:6823 [inline]
schedule+0xea/0x380 kernel/sched/core.c:6838
io_schedule+0xca/0x150 kernel/sched/core.c:9044
blk_mq_get_tag+0x5ec/0xb90 block/blk-mq-tag.c:187
__blk_mq_alloc_requests+0x7eb/0x1e40 block/blk-mq.c:499
blk_mq_get_new_requests block/blk-mq.c:2890 [inline]
blk_mq_submit_bio+0x12b1/0x1f90 block/blk-mq.c:2988
__submit_bio+0x89/0x250 block/blk-core.c:621
__submit_bio_noacct_mq block/blk-core.c:700 [inline]
submit_bio_noacct_nocheck+0x92f/0xcd0 block/blk-core.c:729
submit_bio_noacct+0x278/0xff0 block/blk-core.c:839
submit_bio+0xd5/0x480 block/blk-core.c:881
ext4_io_submit fs/ext4/page-io.c:378 [inline]
io_submit_add_bh fs/ext4/page-io.c:419 [inline]
ext4_bio_write_folio+0x86c/0x13b0 fs/ext4/page-io.c:563
mpage_submit_folio+0x1a8/0x290 fs/ext4/inode.c:1869
mpage_process_page_bufs+0x3dc/0x8c0 fs/ext4/inode.c:1982
mpage_prepare_extent_to_map+0xd74/0x1420 fs/ext4/inode.c:2490
ext4_do_writepages+0xc72/0x35e0 fs/ext4/inode.c:2632
ext4_writepages+0x34e/0x7a0 fs/ext4/inode.c:2768
do_writepages+0x1ca/0x890 mm/page-writeback.c:2612
__writeback_single_inode+0x157/0xee0 fs/fs-writeback.c:1650
writeback_sb_inodes+0x5fb/0x1170 fs/fs-writeback.c:1941
__writeback_inodes_wb+0x10d/0x300 fs/fs-writeback.c:2012
wb_writeback+0x7b3/0xae0 fs/fs-writeback.c:2119
wb_check_old_data_flush fs/fs-writeback.c:2223 [inline]
wb_do_writeback fs/fs-writeback.c:2276 [inline]
wb_workfn+0xa46/0xf60 fs/fs-writeback.c:2304
process_one_work+0x93e/0x1ab0 kernel/workqueue.c:3267
process_scheduled_works kernel/workqueue.c:3348 [inline]
worker_thread+0x6b9/0xf60 kernel/workqueue.c:3429
kthread+0x2cc/0x3c0 kernel/kthread.c:388
ret_from_fork+0x56/0x90 arch/x86/kernel/process.c:147
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244
</TASK>
INFO: lockdep is turned off.
forming flush request for buffer 0xffffc900006d4000, size: 288776
forming flush request for buffer 0xffffc900006d4000, size: 1680
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
forming flush request for buffer 0xffffc900006d4000, size: 1672
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
--===============0636582995431990448==--
^ permalink raw reply
* Re: [PATCH] ext4: Use %pe to print PTR_ERR() in namei.c
From: Theodore Tso @ 2026-05-28 14:56 UTC (permalink / raw)
To: Abdellah Ouhbi; +Cc: linux-ext4, linux-kernel, skhan, me, linux-kernel-mentees
In-Reply-To: <20260424152245.142308-1-abdououhbi1@gmail.com>
On Fri, Apr 24, 2026 at 04:22:45PM +0100, Abdellah Ouhbi wrote:
> Fix coccicheck warning
> ./namei.c:150:25-32: WARNING: Consider using %pe to print PTR_ERR()
>
> Replace %ld with %pe and PTR_ERR(bh) with bh pointer.
> The %pe specifier automatically converts error pointers to
> human-readable error names instead of raw error codes.
>
> Signed-off-by: Abdellah Ouhbi <abdououhbi1@gmail.com>
I've folded the three patches you had sent into a single commit. For
this kind of cleanup, there's no reason to have separate patches for
each file.
Thanks,
- Ted
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox