* [PATCH fstests] overlay: test that syncfs and unmount persist all changes @ 2026-11-06 15:04 Josef Bacik 2026-11-07 0:31 ` [PATCH fstests v2] " Josef Bacik 0 siblings, 1 reply; 9+ messages in thread From: Josef Bacik @ 2026-11-06 15:04 UTC (permalink / raw) To: fstests; +Cc: Zorro Lang, Amir Goldstein, linux-unionfs overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) and unmount. With fsync=relaxed it only writes the upper inodes that were modified through the overlay, so it has to know about all of them. Modify an overlay in many ways: new files and directories, data and metadata copy up, stores through a shared mapping, sendfile, copy_range, an O_TMPFILE, links, renames, whiteouts, a file written before the inode caches are dropped. Then sync or unmount the overlay, shut down the base filesystem without flushing its log, cycle the mounts and check that nothing has changed. Do that with the default mount options, with every fsync mode that syncs and with redirect_dir=on,metacopy=on. Options that the kernel does not know are skipped. Signed-off-by: Josef Bacik <josef@toxicpanda.com> --- This is for "ovl: only sync what was written through the overlay" [1], Amir suggested a test like this in his review. It passes without the series on ext4 and xfs, there it skips fsync=relaxed and fsync=on, and it passes with the series. With the series and the function that marks the overlay inode dirty turned into a no-op it fails in the default, fsync=relaxed and metacopy rounds, the file data is gone. [1] https://lore.kernel.org/r/20261106-b4-ovl-containerized-sync-v4-0-6ddde0b7dd8a@toxicpanda.com tests/overlay/118 | 180 ++++++++++++++++++++++++++++++++++++++++++ tests/overlay/118.out | 2 + 2 files changed, 182 insertions(+) create mode 100755 tests/overlay/118 create mode 100644 tests/overlay/118.out diff --git a/tests/overlay/118 b/tests/overlay/118 new file mode 100755 index 00000000..796f6d7b --- /dev/null +++ b/tests/overlay/118 @@ -0,0 +1,180 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. +# +# FS QA Test 118 +# +# Test that syncfs(2) on an overlay, and unmounting one, persist everything +# that was modified through it. +# +# overlayfs used to sync the whole upper filesystem for that. With +# fsync=relaxed it only writes the upper inodes that were modified through this +# overlay, so it has to know about all of them. +# +# Modify the overlay in every way we can think of, sync it, shut down the base +# filesystem without flushing its log, cycle the mounts and check that nothing +# has changed. +# +. ./common/preamble +_begin_fstest auto quick shutdown copyup whiteout mmap redirect metacopy + +. ./common/attr + +_require_scratch +_require_scratch_shutdown +_require_metadata_journaling $SCRATCH_DEV +_require_xfs_io_command "syncfs" +_require_xfs_io_command "sendfile" +_require_xfs_io_command "copy_range" +_require_xfs_io_command "-T" +_require_xfs_io_command "flink" +_require_attrs +_require_chown +_require_mknod +# These never sync +_exclude_scratch_mount_option "volatile" "fsync=off" + +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER + +create_lower() +{ + local f + + # This leaves the base filesystem mounted + _scratch_mkfs >> $seqres.full 2>&1 + + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir + for f in append truncate mmap chmod chown chown_append xattr link \ + rename remove dir/append rmdir/file mvdir/file; do + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" $lowerdir/$f \ + >> $seqres.full + done + + # These were not written through the overlay, so it does not have to + # sync them. + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT +} + +modify() +{ + local m=$SCRATCH_MNT + + # New files and directories + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full + mkdir $m/newdir + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full + ln -s new $m/symlink + mkfifo $m/fifo + + # Copy up, then change the data + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full + $XFS_IO_PROG -c "truncate 4k" $m/truncate + + # Stores through a shared mapping, also after a syncfs, no msync + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full + + # The other ways to write data + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ + >> $seqres.full + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ + >> $seqres.full + + # Copy up, then change the metadata + chmod 640 $m/chmod + chown 1234:1234 $m/chown + $SETFATTR_PROG -n user.test -v value $m/xattr + + # With metacopy=on the first one only copies up the metadata + chown 1234:1234 $m/chown_append + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ + >> $seqres.full + + # Names + ln $m/link $m/link2 + mv $m/rename $m/renamed + # Without redirect_dir=on mv gets EXDEV and copies the directory + mv $m/mvdir $m/mvdir2 + rm $m/remove + rm -r $m/rmdir + + # An overlay inode that nobody holds on to + $XFS_IO_PROG -f -c "pwrite -S 0x68 0 1m" $m/evicted >> $seqres.full + echo 2 > /proc/sys/vm/drop_caches +} + +# No inode numbers, no timestamps, and no sizes and link counts of directories +snapshot() +{ + local f + + cd $SCRATCH_MNT + find . | LC_ALL=C sort | while read -r f; do + if [ -L "$f" ]; then + echo "$f $(stat -c '%F %u:%g' "$f") $(readlink "$f")" + elif [ -f "$f" ]; then + echo "$f $(stat -c '%F %a %u:%g %h %s' "$f")" \ + "$(md5sum < "$f")" + else + echo "$f $(stat -c '%F %a %u:%g' "$f")" + fi + done + _getfattr --absolute-names -n user.test xattr 2>&1 + cd - > /dev/null +} + +# <syncfs|umount> [mount options] +do_test() +{ + local how=$1 + shift + local label="$how${*:+ $*}" + + echo "=== $label" >> $seqres.full + + create_lower + if [ $# -eq 0 ]; then + _scratch_mount + elif ! _try_scratch_mount $* >> $seqres.full 2>&1; then + # Not every kernel knows every option + echo "cannot mount" >> $seqres.full + _unmount $OVL_BASE_SCRATCH_MNT + return + fi + + modify + snapshot > $tmp.before + + if [ $how = syncfs ]; then + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT + _scratch_shutdown | tee -a $seqres.full + _scratch_unmount + else + # Only the overlay, the base filesystem stays mounted + _unmount $SCRATCH_MNT + _scratch_shutdown | tee -a $seqres.full + _unmount $OVL_BASE_SCRATCH_MNT + fi + + _scratch_mount $* + snapshot > $tmp.after + cat $tmp.after >> $seqres.full + diff -u $tmp.before $tmp.after | sed -e "s/^/$label: /" + _scratch_unmount +} + +for how in syncfs umount; do + do_test $how + for mode in relaxed on strict; do + do_test $how -o fsync=$mode + done + do_test $how -o redirect_dir=on,metacopy=on +done + +# success, all done +echo "Silence is golden" +status=0 +exit diff --git a/tests/overlay/118.out b/tests/overlay/118.out new file mode 100644 index 00000000..3daed864 --- /dev/null +++ b/tests/overlay/118.out @@ -0,0 +1,2 @@ +QA output created by 118 +Silence is golden -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH fstests v2] overlay: test that syncfs and unmount persist all changes 2026-11-06 15:04 [PATCH fstests] overlay: test that syncfs and unmount persist all changes Josef Bacik @ 2026-11-07 0:31 ` Josef Bacik 2026-11-07 11:58 ` Amir Goldstein ` (2 more replies) 0 siblings, 3 replies; 9+ messages in thread From: Josef Bacik @ 2026-11-07 0:31 UTC (permalink / raw) To: fstests; +Cc: Zorro Lang, Amir Goldstein, Christian Brauner, linux-unionfs overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) and unmount. With fsync=relaxed it only writes the upper inodes that were modified through the overlay, so it has to know about all of them. Modify an overlay in many ways: new files and directories, data and metadata copy up, stores through a shared mapping, sendfile, copy_range, an O_TMPFILE, a short and a long symlink, hard links, renames, whiteouts, a file written before the inode caches are dropped. Then sync or unmount the overlay, shut down the base filesystem without flushing its log, cycle the mounts and check that nothing has changed. Do that with the default mount options, with every fsync mode that syncs and with redirect_dir=on,metacopy=on. Options that the kernel does not know are skipped. Signed-off-by: Josef Bacik <josef@toxicpanda.com> --- v1: https://lore.kernel.org/r/773faf2a275e0a51bd38cf68f492856c.josef@toxicpanda.com v1->v2: - Added a symlink that's too long to be stored in the inode. (Christian) This is for "ovl: only sync what was written through the overlay" [1], Amir suggested a test like this in his review. It passes without the series on ext4 and xfs, there it skips fsync=relaxed and fsync=on, and it passes with the series. With the series and the function that marks the overlay inode dirty turned into a no-op it fails in the default, fsync=relaxed and metacopy rounds, the file data is gone. [1] https://lore.kernel.org/r/20261106-b4-ovl-containerized-sync-v4-0-6ddde0b7dd8a@toxicpanda.com tests/overlay/118 | 182 ++++++++++++++++++++++++++++++++++++++++++ tests/overlay/118.out | 2 + 2 files changed, 184 insertions(+) create mode 100755 tests/overlay/118 create mode 100644 tests/overlay/118.out diff --git a/tests/overlay/118 b/tests/overlay/118 new file mode 100755 index 00000000..f9c98eff --- /dev/null +++ b/tests/overlay/118 @@ -0,0 +1,182 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. +# +# FS QA Test 118 +# +# Test that syncfs(2) on an overlay, and unmounting one, persist everything +# that was modified through it. +# +# overlayfs used to sync the whole upper filesystem for that. With +# fsync=relaxed it only writes the upper inodes that were modified through this +# overlay, so it has to know about all of them. +# +# Modify the overlay in every way we can think of, sync it, shut down the base +# filesystem without flushing its log, cycle the mounts and check that nothing +# has changed. +# +. ./common/preamble +_begin_fstest auto quick shutdown copyup whiteout mmap redirect metacopy + +. ./common/attr + +_require_scratch +_require_scratch_shutdown +_require_metadata_journaling $SCRATCH_DEV +_require_xfs_io_command "syncfs" +_require_xfs_io_command "sendfile" +_require_xfs_io_command "copy_range" +_require_xfs_io_command "-T" +_require_xfs_io_command "flink" +_require_attrs +_require_chown +_require_mknod +# These never sync +_exclude_scratch_mount_option "volatile" "fsync=off" + +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER + +create_lower() +{ + local f + + # This leaves the base filesystem mounted + _scratch_mkfs >> $seqres.full 2>&1 + + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir + for f in append truncate mmap chmod chown chown_append xattr link \ + rename remove dir/append rmdir/file mvdir/file; do + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" $lowerdir/$f \ + >> $seqres.full + done + + # These were not written through the overlay, so it does not have to + # sync them. + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT +} + +modify() +{ + local m=$SCRATCH_MNT + + # New files and directories + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full + mkdir $m/newdir + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full + ln -s new $m/symlink + # Too long to be stored in the inode + ln -s $(printf 'a%.0s' $(seq 1000)) $m/longsymlink + mkfifo $m/fifo + + # Copy up, then change the data + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full + $XFS_IO_PROG -c "truncate 4k" $m/truncate + + # Stores through a shared mapping, also after a syncfs, no msync + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full + + # The other ways to write data + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ + >> $seqres.full + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ + >> $seqres.full + + # Copy up, then change the metadata + chmod 640 $m/chmod + chown 1234:1234 $m/chown + $SETFATTR_PROG -n user.test -v value $m/xattr + + # With metacopy=on the first one only copies up the metadata + chown 1234:1234 $m/chown_append + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ + >> $seqres.full + + # Names + ln $m/link $m/link2 + mv $m/rename $m/renamed + # Without redirect_dir=on mv gets EXDEV and copies the directory + mv $m/mvdir $m/mvdir2 + rm $m/remove + rm -r $m/rmdir + + # An overlay inode that nobody holds on to + $XFS_IO_PROG -f -c "pwrite -S 0x68 0 1m" $m/evicted >> $seqres.full + echo 2 > /proc/sys/vm/drop_caches +} + +# No inode numbers, no timestamps, and no sizes and link counts of directories +snapshot() +{ + local f + + cd $SCRATCH_MNT + find . | LC_ALL=C sort | while read -r f; do + if [ -L "$f" ]; then + echo "$f $(stat -c '%F %u:%g' "$f") $(readlink "$f")" + elif [ -f "$f" ]; then + echo "$f $(stat -c '%F %a %u:%g %h %s' "$f")" \ + "$(md5sum < "$f")" + else + echo "$f $(stat -c '%F %a %u:%g' "$f")" + fi + done + _getfattr --absolute-names -n user.test xattr 2>&1 + cd - > /dev/null +} + +# <syncfs|umount> [mount options] +do_test() +{ + local how=$1 + shift + local label="$how${*:+ $*}" + + echo "=== $label" >> $seqres.full + + create_lower + if [ $# -eq 0 ]; then + _scratch_mount + elif ! _try_scratch_mount $* >> $seqres.full 2>&1; then + # Not every kernel knows every option + echo "cannot mount" >> $seqres.full + _unmount $OVL_BASE_SCRATCH_MNT + return + fi + + modify + snapshot > $tmp.before + + if [ $how = syncfs ]; then + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT + _scratch_shutdown | tee -a $seqres.full + _scratch_unmount + else + # Only the overlay, the base filesystem stays mounted + _unmount $SCRATCH_MNT + _scratch_shutdown | tee -a $seqres.full + _unmount $OVL_BASE_SCRATCH_MNT + fi + + _scratch_mount $* + snapshot > $tmp.after + cat $tmp.after >> $seqres.full + diff -u $tmp.before $tmp.after | sed -e "s/^/$label: /" + _scratch_unmount +} + +for how in syncfs umount; do + do_test $how + for mode in relaxed on strict; do + do_test $how -o fsync=$mode + done + do_test $how -o redirect_dir=on,metacopy=on +done + +# success, all done +echo "Silence is golden" +status=0 +exit diff --git a/tests/overlay/118.out b/tests/overlay/118.out new file mode 100644 index 00000000..3daed864 --- /dev/null +++ b/tests/overlay/118.out @@ -0,0 +1,2 @@ +QA output created by 118 +Silence is golden -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH fstests v2] overlay: test that syncfs and unmount persist all changes 2026-11-07 0:31 ` [PATCH fstests v2] " Josef Bacik @ 2026-11-07 11:58 ` Amir Goldstein 2026-11-07 12:06 ` Amir Goldstein 2026-11-09 22:52 ` [PATCH fstests v3] " Josef Bacik 2 siblings, 0 replies; 9+ messages in thread From: Amir Goldstein @ 2026-11-07 11:58 UTC (permalink / raw) To: Josef Bacik; +Cc: fstests, Zorro Lang, Christian Brauner, linux-unionfs On Sat, Nov 7, 2026 at 1:31 AM Josef Bacik <josef@toxicpanda.com> wrote: > > overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) > and unmount. With fsync=relaxed it only writes the upper inodes that were > modified through the overlay, so it has to know about all of them. > > Modify an overlay in many ways: new files and directories, data and > metadata copy up, stores through a shared mapping, sendfile, copy_range, > an O_TMPFILE, a short and a long symlink, hard links, renames, whiteouts, > a file written before the inode caches are dropped. Then sync or unmount > the overlay, shut down the base filesystem without flushing its log, cycle > the mounts and check that nothing has changed. > > Do that with the default mount options, with every fsync mode that syncs > and with redirect_dir=on,metacopy=on. Options that the kernel does not > know are skipped. > > Signed-off-by: Josef Bacik <josef@toxicpanda.com> > --- > v1: https://lore.kernel.org/r/773faf2a275e0a51bd38cf68f492856c.josef@toxicpanda.com > > v1->v2: > - Added a symlink that's too long to be stored in the inode. (Christian) > > This is for "ovl: only sync what was written through the overlay" [1], Amir > suggested a test like this in his review. > > It passes without the series on ext4 and xfs, there it skips fsync=relaxed > and fsync=on, and it passes with the series. With the series and the > function that marks the overlay inode dirty turned into a no-op it fails in > the default, fsync=relaxed and metacopy rounds, the file data is gone. > > [1] https://lore.kernel.org/r/20261106-b4-ovl-containerized-sync-v4-0-6ddde0b7dd8a@toxicpanda.com > > tests/overlay/118 | 182 ++++++++++++++++++++++++++++++++++++++++++ > tests/overlay/118.out | 2 + > 2 files changed, 184 insertions(+) > create mode 100755 tests/overlay/118 > create mode 100644 tests/overlay/118.out > > diff --git a/tests/overlay/118 b/tests/overlay/118 > new file mode 100755 > index 00000000..f9c98eff > --- /dev/null > +++ b/tests/overlay/118 Nice! Thanks. What's the deal with Claude's number allocations? There is an unused test number range 090..099. In tests/overlay it's just because I started the unionmount tests in offset 100, but I think that in fstests we often reallocate numbers for tests that were deleted. > @@ -0,0 +1,182 @@ > +#! /bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. > +# > +# FS QA Test 118 > +# > +# Test that syncfs(2) on an overlay, and unmounting one, persist everything > +# that was modified through it. > +# > +# overlayfs used to sync the whole upper filesystem for that. With > +# fsync=relaxed it only writes the upper inodes that were modified through this > +# overlay, so it has to know about all of them. > +# > +# Modify the overlay in every way we can think of, sync it, shut down the base > +# filesystem without flushing its log, cycle the mounts and check that nothing > +# has changed. > +# > +. ./common/preamble > +_begin_fstest auto quick shutdown copyup whiteout mmap redirect metacopy > + > +. ./common/attr > + > +_require_scratch > +_require_scratch_shutdown > +_require_metadata_journaling $SCRATCH_DEV > +_require_xfs_io_command "syncfs" > +_require_xfs_io_command "sendfile" > +_require_xfs_io_command "copy_range" > +_require_xfs_io_command "-T" > +_require_xfs_io_command "flink" > +_require_attrs > +_require_chown > +_require_mknod > +# These never sync > +_exclude_scratch_mount_option "volatile" "fsync=off" This seems wrong. When one specified default mount options for the entire ./run they are not expected to affect tests that set those options explicitly I think the right thing to do is that this test always explicitly requests fsync= and where you did not provide fsync= provide fsync=auto instead. Also, please add: _require_scratch_overlay_features fsync But it doesn't work without my fix: https://lore.kernel.org/fstests/20261106155832.3867017-1-amir73il@gmail.com/ Yes this test could technically run on older kernels and skip the unsupported options but I don't think it's important that it would. > + > +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER > + > +create_lower() > +{ > + local f > + > + # This leaves the base filesystem mounted > + _scratch_mkfs >> $seqres.full 2>&1 > + > + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir > + for f in append truncate mmap chmod chown chown_append xattr link \ > + rename remove dir/append rmdir/file mvdir/file; do > + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" $lowerdir/$f \ > + >> $seqres.full > + done > + > + # These were not written through the overlay, so it does not have to > + # sync them. > + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT > +} > + > +modify() > +{ > + local m=$SCRATCH_MNT > + > + # New files and directories > + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full > + mkdir $m/newdir > + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full > + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full > + ln -s new $m/symlink > + # Too long to be stored in the inode > + ln -s $(printf 'a%.0s' $(seq 1000)) $m/longsymlink > + mkfifo $m/fifo > + > + # Copy up, then change the data > + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full > + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full > + $XFS_IO_PROG -c "truncate 4k" $m/truncate > + > + # Stores through a shared mapping, also after a syncfs, no msync > + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ > + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full > + > + # The other ways to write data > + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ > + >> $seqres.full > + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range > + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ > + >> $seqres.full > + > + # Copy up, then change the metadata > + chmod 640 $m/chmod > + chown 1234:1234 $m/chown > + $SETFATTR_PROG -n user.test -v value $m/xattr > + > + # With metacopy=on the first one only copies up the metadata > + chown 1234:1234 $m/chown_append > + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ > + >> $seqres.full > + > + # Names > + ln $m/link $m/link2 > + mv $m/rename $m/renamed > + # Without redirect_dir=on mv gets EXDEV and copies the directory > + mv $m/mvdir $m/mvdir2 > + rm $m/remove > + rm -r $m/rmdir > + > + # An overlay inode that nobody holds on to > + $XFS_IO_PROG -f -c "pwrite -S 0x68 0 1m" $m/evicted >> $seqres.full > + echo 2 > /proc/sys/vm/drop_caches > +} > + > +# No inode numbers, no timestamps, and no sizes and link counts of directories > +snapshot() > +{ > + local f > + > + cd $SCRATCH_MNT > + find . | LC_ALL=C sort | while read -r f; do > + if [ -L "$f" ]; then > + echo "$f $(stat -c '%F %u:%g' "$f") $(readlink "$f")" > + elif [ -f "$f" ]; then > + echo "$f $(stat -c '%F %a %u:%g %h %s' "$f")" \ > + "$(md5sum < "$f")" > + else > + echo "$f $(stat -c '%F %a %u:%g' "$f")" > + fi > + done > + _getfattr --absolute-names -n user.test xattr 2>&1 > + cd - > /dev/null > +} > + > +# <syncfs|umount> [mount options] > +do_test() > +{ > + local how=$1 > + shift > + local label="$how${*:+ $*}" > + > + echo "=== $label" >> $seqres.full Please echo label also to stdout and include it in golden output so if something fails it is easier to see right away in the diff which mode had failed. > + > + create_lower > + if [ $# -eq 0 ]; then > + _scratch_mount > + elif ! _try_scratch_mount $* >> $seqres.full 2>&1; then > + # Not every kernel knows every option > + echo "cannot mount" >> $seqres.full > + _unmount $OVL_BASE_SCRATCH_MNT > + return > + fi > + > + modify > + snapshot > $tmp.before > + > + if [ $how = syncfs ]; then > + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT > + _scratch_shutdown | tee -a $seqres.full > + _scratch_unmount > + else > + # Only the overlay, the base filesystem stays mounted > + _unmount $SCRATCH_MNT > + _scratch_shutdown | tee -a $seqres.full > + _unmount $OVL_BASE_SCRATCH_MNT > + fi > + > + _scratch_mount $* > + snapshot > $tmp.after > + cat $tmp.after >> $seqres.full > + diff -u $tmp.before $tmp.after | sed -e "s/^/$label: /" > + _scratch_unmount > +} > + > +for how in syncfs umount; do > + do_test $how I think adding auto to modes instead is sufficient and avoids the default fsync=off issue > + for mode in relaxed on strict; do > + do_test $how -o fsync=$mode > + done > + do_test $how -o redirect_dir=on,metacopy=on This makes for odd test coverage. No coverage for metacopy=on,fsync=relaxed (assuming default is off) No coverage for redirect_dir=off,fsync=relaxed (assuming default is on) I guess there is no escape from another X2 dimension of meta in on off and do_test $how -o fsync=$mode,redirect_dir=$meta,metacopy=$meta Thanks, Amir. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH fstests v2] overlay: test that syncfs and unmount persist all changes 2026-11-07 0:31 ` [PATCH fstests v2] " Josef Bacik 2026-11-07 11:58 ` Amir Goldstein @ 2026-11-07 12:06 ` Amir Goldstein 2026-11-09 22:52 ` [PATCH fstests v3] " Josef Bacik 2 siblings, 0 replies; 9+ messages in thread From: Amir Goldstein @ 2026-11-07 12:06 UTC (permalink / raw) To: Josef Bacik; +Cc: fstests, Zorro Lang, Christian Brauner, linux-unionfs On Sat, Nov 7, 2026 at 1:31 AM Josef Bacik <josef@toxicpanda.com> wrote: > > overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) > and unmount. With fsync=relaxed it only writes the upper inodes that were > modified through the overlay, so it has to know about all of them. > > Modify an overlay in many ways: new files and directories, data and > metadata copy up, stores through a shared mapping, sendfile, copy_range, > an O_TMPFILE, a short and a long symlink, hard links, renames, whiteouts, > a file written before the inode caches are dropped. Then sync or unmount > the overlay, shut down the base filesystem without flushing its log, cycle > the mounts and check that nothing has changed. > > Do that with the default mount options, with every fsync mode that syncs > and with redirect_dir=on,metacopy=on. Options that the kernel does not > know are skipped. > > Signed-off-by: Josef Bacik <josef@toxicpanda.com> > --- > v1: https://lore.kernel.org/r/773faf2a275e0a51bd38cf68f492856c.josef@toxicpanda.com > > v1->v2: > - Added a symlink that's too long to be stored in the inode. (Christian) > > This is for "ovl: only sync what was written through the overlay" [1], Amir > suggested a test like this in his review. > > It passes without the series on ext4 and xfs, there it skips fsync=relaxed > and fsync=on, and it passes with the series. With the series and the > function that marks the overlay inode dirty turned into a no-op it fails in > the default, fsync=relaxed and metacopy rounds, the file data is gone. > > [1] https://lore.kernel.org/r/20261106-b4-ovl-containerized-sync-v4-0-6ddde0b7dd8a@toxicpanda.com > > tests/overlay/118 | 182 ++++++++++++++++++++++++++++++++++++++++++ > tests/overlay/118.out | 2 + > 2 files changed, 184 insertions(+) > create mode 100755 tests/overlay/118 > create mode 100644 tests/overlay/118.out > > diff --git a/tests/overlay/118 b/tests/overlay/118 > new file mode 100755 > index 00000000..f9c98eff > --- /dev/null > +++ b/tests/overlay/118 > @@ -0,0 +1,182 @@ > +#! /bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. > +# > +# FS QA Test 118 > +# > +# Test that syncfs(2) on an overlay, and unmounting one, persist everything > +# that was modified through it. > +# > +# overlayfs used to sync the whole upper filesystem for that. With > +# fsync=relaxed it only writes the upper inodes that were modified through this > +# overlay, so it has to know about all of them. > +# > +# Modify the overlay in every way we can think of, sync it, shut down the base > +# filesystem without flushing its log, cycle the mounts and check that nothing > +# has changed. > +# > +. ./common/preamble > +_begin_fstest auto quick shutdown copyup whiteout mmap redirect metacopy > + > +. ./common/attr > + > +_require_scratch > +_require_scratch_shutdown > +_require_metadata_journaling $SCRATCH_DEV > +_require_xfs_io_command "syncfs" > +_require_xfs_io_command "sendfile" > +_require_xfs_io_command "copy_range" > +_require_xfs_io_command "-T" > +_require_xfs_io_command "flink" > +_require_attrs > +_require_chown > +_require_mknod > +# These never sync > +_exclude_scratch_mount_option "volatile" "fsync=off" > + > +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER > + > +create_lower() > +{ > + local f > + > + # This leaves the base filesystem mounted > + _scratch_mkfs >> $seqres.full 2>&1 > + > + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir > + for f in append truncate mmap chmod chown chown_append xattr link \ > + rename remove dir/append rmdir/file mvdir/file; do > + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" $lowerdir/$f \ > + >> $seqres.full > + done > + > + # These were not written through the overlay, so it does not have to > + # sync them. > + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT > +} > + > +modify() > +{ > + local m=$SCRATCH_MNT > + > + # New files and directories > + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full > + mkdir $m/newdir > + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full > + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full > + ln -s new $m/symlink > + # Too long to be stored in the inode > + ln -s $(printf 'a%.0s' $(seq 1000)) $m/longsymlink > + mkfifo $m/fifo > + > + # Copy up, then change the data > + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full > + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full > + $XFS_IO_PROG -c "truncate 4k" $m/truncate > + > + # Stores through a shared mapping, also after a syncfs, no msync > + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ > + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full Sashiko is right to note that this syncfs add an explicit fsync that could hide unmount non-persist bugs? > + > + # The other ways to write data > + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ > + >> $seqres.full > + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range > + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ > + >> $seqres.full > + > + # Copy up, then change the metadata > + chmod 640 $m/chmod > + chown 1234:1234 $m/chown > + $SETFATTR_PROG -n user.test -v value $m/xattr > + > + # With metacopy=on the first one only copies up the metadata > + chown 1234:1234 $m/chown_append > + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ > + >> $seqres.full > + > + # Names > + ln $m/link $m/link2 > + mv $m/rename $m/renamed > + # Without redirect_dir=on mv gets EXDEV and copies the directory > + mv $m/mvdir $m/mvdir2 > + rm $m/remove > + rm -r $m/rmdir > + > + # An overlay inode that nobody holds on to > + $XFS_IO_PROG -f -c "pwrite -S 0x68 0 1m" $m/evicted >> $seqres.full > + echo 2 > /proc/sys/vm/drop_caches Sashiko is right to claim that this drop caches is meaningless with the snapshot() that follows? > +} > + > +# No inode numbers, no timestamps, and no sizes and link counts of directories > +snapshot() > +{ > + local f > + > + cd $SCRATCH_MNT > + find . | LC_ALL=C sort | while read -r f; do > + if [ -L "$f" ]; then > + echo "$f $(stat -c '%F %u:%g' "$f") $(readlink "$f")" > + elif [ -f "$f" ]; then > + echo "$f $(stat -c '%F %a %u:%g %h %s' "$f")" \ > + "$(md5sum < "$f")" > + else > + echo "$f $(stat -c '%F %a %u:%g' "$f")" > + fi > + done > + _getfattr --absolute-names -n user.test xattr 2>&1 > + cd - > /dev/null > +} > + > +# <syncfs|umount> [mount options] > +do_test() > +{ > + local how=$1 > + shift > + local label="$how${*:+ $*}" > + > + echo "=== $label" >> $seqres.full > + > + create_lower > + if [ $# -eq 0 ]; then > + _scratch_mount > + elif ! _try_scratch_mount $* >> $seqres.full 2>&1; then > + # Not every kernel knows every option > + echo "cannot mount" >> $seqres.full > + _unmount $OVL_BASE_SCRATCH_MNT > + return > + fi > + > + modify > + snapshot > $tmp.before > + > + if [ $how = syncfs ]; then > + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT > + _scratch_shutdown | tee -a $seqres.full > + _scratch_unmount > + else > + # Only the overlay, the base filesystem stays mounted > + _unmount $SCRATCH_MNT > + _scratch_shutdown | tee -a $seqres.full > + _unmount $OVL_BASE_SCRATCH_MNT > + fi > + > + _scratch_mount $* > + snapshot > $tmp.after > + cat $tmp.after >> $seqres.full > + diff -u $tmp.before $tmp.after | sed -e "s/^/$label: /" > + _scratch_unmount > +} > + > +for how in syncfs umount; do > + do_test $how > + for mode in relaxed on strict; do > + do_test $how -o fsync=$mode > + done > + do_test $how -o redirect_dir=on,metacopy=on > +done > + > +# success, all done > +echo "Silence is golden" > +status=0 > +exit > diff --git a/tests/overlay/118.out b/tests/overlay/118.out > new file mode 100644 > index 00000000..3daed864 > --- /dev/null > +++ b/tests/overlay/118.out > @@ -0,0 +1,2 @@ > +QA output created by 118 > +Silence is golden > -- > 2.55.0 > ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH fstests v3] overlay: test that syncfs and unmount persist all changes 2026-11-07 0:31 ` [PATCH fstests v2] " Josef Bacik 2026-11-07 11:58 ` Amir Goldstein 2026-11-07 12:06 ` Amir Goldstein @ 2026-11-09 22:52 ` Josef Bacik 2026-11-10 10:42 ` Amir Goldstein 2026-11-10 16:27 ` [PATCH fstests v4] " Josef Bacik 2 siblings, 2 replies; 9+ messages in thread From: Josef Bacik @ 2026-11-09 22:52 UTC (permalink / raw) To: fstests; +Cc: Zorro Lang, Amir Goldstein, Christian Brauner, linux-unionfs overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) and unmount. With fsync=relaxed it only writes the upper inodes that were modified through the overlay, so it has to know about all of them. Modify an overlay in many ways: new files and directories, data and metadata copy up, stores through a shared mapping, sendfile, copy_range, an O_TMPFILE, a short and a long symlink, hard links, renames, whiteouts. Do the same to files that were synced before, so that nothing but the modification itself tells overlayfs about them. Then drop the caches, sync or unmount the overlay, shut down the base filesystem without flushing its log, cycle the mounts and check that nothing has changed. Do that with every fsync mode that syncs, with and without redirect_dir and metacopy. The base filesystem has to have a journal, so what gets lost if overlayfs misses a modification is data in the page cache. Changes of metadata are in the log, which overlayfs commits in any case. Signed-off-by: Josef Bacik <josef@toxicpanda.com> --- v1: https://lore.kernel.org/r/773faf2a275e0a51bd38cf68f492856c.josef@toxicpanda.com v2: https://lore.kernel.org/r/946b46fbf1c555a47a8a9304f2ee31af.josef@toxicpanda.com v2->v3: - It's overlay/090 now. (Amir) - Every round passes fsync=, with fsync=auto in place of the default, and _exclude_scratch_mount_option is gone. (Amir) - Added _require_scratch_overlay_features fsync redirect_dir metacopy. That needs [2], and the test doesn't run on older kernels any more. (Amir) - The rounds are in the golden output. (Amir) - Every fsync mode runs with redirect_dir and metacopy off and on, that's 16 rounds. It takes 70s here, so it's not in quick any more. (Amir) - The mmap step with the syncfs in it comes first, it used to write everything that came before it. (Sashiko, Amir) - The caches are dropped after the snapshot, for all files. (Sashiko, Amir) - The overlay is mounted with noatime. The snapshot reads every file, the atime update marks the overlay inode dirty, and that hid any modification that overlayfs had missed. - Added files that are synced before they are modified. Creating a file and copying it up mark the overlay inode dirty, and that hid any later modification that overlayfs had missed. - Added fallocate, and a copy_range that isn't aligned so xfs can't reflink it. This is for "ovl: only sync what was written through the overlay" [1]. It passes on ext4 and xfs with the series, and with only the first patch of it, which adds the mount options and still syncs the whole upper file system. I ran it on four kernels that I broke on purpose. All of them fail, only in the fsync=auto and fsync=relaxed rounds: - The function that marks the overlay inode dirty does nothing: the file data is gone. - Only ovl_copyattr() doesn't mark the inode: the pwrite, the sendfile and the copy_range to the files that were synced before are gone. v2 passes on this one. - A dirty overlay inode isn't kept after the last iput(): the file data is gone. v2 only notices in its unmount rounds. - Nothing is written at unmount: the unmount rounds lose the file data. [1] https://lore.kernel.org/r/20261107-b4-ovl-containerized-sync-v5-0-d34b4ae57fdf@toxicpanda.com [2] https://lore.kernel.org/r/20261106155832.3867017-1-amir73il@gmail.com tests/overlay/090 | 214 ++++++++++++++++++++++++++++++++++++++++++ tests/overlay/090.out | 17 ++++ 2 files changed, 231 insertions(+) create mode 100755 tests/overlay/090 create mode 100644 tests/overlay/090.out diff --git a/tests/overlay/090 b/tests/overlay/090 new file mode 100755 index 00000000..7f6e58f4 --- /dev/null +++ b/tests/overlay/090 @@ -0,0 +1,214 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. +# +# FS QA Test 090 +# +# Test that syncfs(2) on an overlay, and unmounting one, persist everything +# that was modified through it. +# +# overlayfs used to sync the whole upper filesystem for that. With +# fsync=relaxed it only writes the upper inodes that were modified through this +# overlay, so it has to know about all of them. +# +# Modify the overlay in every way we can think of, sync it, shut down the base +# filesystem without flushing its log, cycle the mounts and check that nothing +# has changed. +# +. ./common/preamble +_begin_fstest auto shutdown copyup whiteout mmap redirect metacopy + +. ./common/attr + +_require_scratch +_require_scratch_shutdown +_require_metadata_journaling $SCRATCH_DEV +_require_scratch_overlay_features fsync redirect_dir metacopy +_require_xfs_io_command "syncfs" +_require_xfs_io_command "sendfile" +_require_xfs_io_command "copy_range" +_require_xfs_io_command "falloc" +_require_xfs_io_command "-T" +_require_xfs_io_command "flink" +_require_attrs +_require_chown +_require_mknod + +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER + +create_lower() +{ + local f + + # This leaves the base filesystem mounted + _scratch_mkfs >> $seqres.full 2>&1 + + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir + for f in append truncate mmap chmod chown chown_append xattr link \ + rename remove dir/append rmdir/file mvdir/file; do + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" $lowerdir/$f \ + >> $seqres.full + done + + # These were not written through the overlay, so it does not have to + # sync them. + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT +} + +# Creating a file and copying it up are modifications. overlayfs remembers them +# until the file is synced, and a later modification that it misses does not +# show. So make files that are in the upper layer and have been synced. +prepare() +{ + local m=$SCRATCH_MNT + local f + + mkdir $m/clean + for f in pwrite truncate fallocate mmap sendfile copy_range chmod \ + chown xattr link rename remove; do + $XFS_IO_PROG -f -c "pwrite -S 0x70 0 256k" $m/clean/$f \ + >> $seqres.full + done + $XFS_IO_PROG -c "syncfs" $m +} + +modify() +{ + local m=$SCRATCH_MNT + + # Stores through a shared mapping, also after a syncfs, no msync. This + # has to be the first one, the syncfs must not write anything else. + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full + + # The files that overlayfs has nothing to remember about + $XFS_IO_PROG -c "pwrite -S 0x71 64k 64k" $m/clean/pwrite >> $seqres.full + $XFS_IO_PROG -c "truncate 4k" $m/clean/truncate + $XFS_IO_PROG -c "falloc 256k 64k" $m/clean/fallocate + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x72 64k 64k" \ + $m/clean/mmap >> $seqres.full + $XFS_IO_PROG -c "sendfile -i $m/link 0 64k" $m/clean/sendfile \ + >> $seqres.full + # Not aligned, so that the data is copied and not shared + $XFS_IO_PROG -c "copy_range -s 1 -d 1 -l 64k $m/link" \ + $m/clean/copy_range + chmod 640 $m/clean/chmod + chown 1234:1234 $m/clean/chown + $SETFATTR_PROG -n user.test -v value $m/clean/xattr + ln $m/clean/link $m/clean/link2 + mv $m/clean/rename $m/clean/renamed + rm $m/clean/remove + + # New files and directories + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full + mkdir $m/newdir + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full + ln -s new $m/symlink + # Too long to be stored in the inode + ln -s $(printf 'a%.0s' $(seq 1000)) $m/longsymlink + mkfifo $m/fifo + + # Copy up, then change the data + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full + $XFS_IO_PROG -c "truncate 4k" $m/truncate + + # The other ways to write data + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ + >> $seqres.full + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ + >> $seqres.full + + # Copy up, then change the metadata + chmod 640 $m/chmod + chown 1234:1234 $m/chown + $SETFATTR_PROG -n user.test -v value $m/xattr + + # With metacopy=on the first one only copies up the metadata + chown 1234:1234 $m/chown_append + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ + >> $seqres.full + + # Names + ln $m/link $m/link2 + mv $m/rename $m/renamed + # Without redirect_dir=on mv gets EXDEV and copies the directory + mv $m/mvdir $m/mvdir2 + rm $m/remove + rm -r $m/rmdir +} + +# No inode numbers, no timestamps, and no sizes and link counts of directories +snapshot() +{ + local f + + cd $SCRATCH_MNT + find . | LC_ALL=C sort | while read -r f; do + if [ -L "$f" ]; then + echo "$f $(stat -c '%F %u:%g' "$f") $(readlink "$f")" + elif [ -f "$f" ]; then + echo "$f $(stat -c '%F %a %u:%g %h %s' "$f")" \ + "$(md5sum < "$f")" + else + echo "$f $(stat -c '%F %a %u:%g' "$f")" + fi + done + _getfattr --absolute-names -n user.test xattr clean/xattr 2>&1 + cd - > /dev/null +} + +# <syncfs|umount> <mount options> +do_test() +{ + local how=$1 + local opts=$2 + + echo "=== $how $opts" | tee -a $seqres.full + + create_lower + + # An atime update is a modification too. With atime the snapshot would + # make overlayfs write every file that it reads, also one that + # overlayfs has lost track of. + _scratch_mount -o noatime,$opts + + prepare + modify + snapshot > $tmp.before + + # Nobody holds on to the overlay inodes when they are synced + echo 2 > /proc/sys/vm/drop_caches + + if [ $how = syncfs ]; then + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT + _scratch_shutdown | tee -a $seqres.full + _scratch_unmount + else + # Only the overlay, the base filesystem stays mounted + _unmount $SCRATCH_MNT + _scratch_shutdown | tee -a $seqres.full + _unmount $OVL_BASE_SCRATCH_MNT + fi + + _scratch_mount -o noatime,$opts + snapshot > $tmp.after + cat $tmp.after >> $seqres.full + diff -u $tmp.before $tmp.after + _scratch_unmount +} + +for how in syncfs umount; do + for mode in auto relaxed on strict; do + for meta in off on; do + do_test $how \ + fsync=$mode,redirect_dir=$meta,metacopy=$meta + done + done +done + +# success, all done +status=0 +exit diff --git a/tests/overlay/090.out b/tests/overlay/090.out new file mode 100644 index 00000000..d6b46515 --- /dev/null +++ b/tests/overlay/090.out @@ -0,0 +1,17 @@ +QA output created by 090 +=== syncfs fsync=auto,redirect_dir=off,metacopy=off +=== syncfs fsync=auto,redirect_dir=on,metacopy=on +=== syncfs fsync=relaxed,redirect_dir=off,metacopy=off +=== syncfs fsync=relaxed,redirect_dir=on,metacopy=on +=== syncfs fsync=on,redirect_dir=off,metacopy=off +=== syncfs fsync=on,redirect_dir=on,metacopy=on +=== syncfs fsync=strict,redirect_dir=off,metacopy=off +=== syncfs fsync=strict,redirect_dir=on,metacopy=on +=== umount fsync=auto,redirect_dir=off,metacopy=off +=== umount fsync=auto,redirect_dir=on,metacopy=on +=== umount fsync=relaxed,redirect_dir=off,metacopy=off +=== umount fsync=relaxed,redirect_dir=on,metacopy=on +=== umount fsync=on,redirect_dir=off,metacopy=off +=== umount fsync=on,redirect_dir=on,metacopy=on +=== umount fsync=strict,redirect_dir=off,metacopy=off +=== umount fsync=strict,redirect_dir=on,metacopy=on -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH fstests v3] overlay: test that syncfs and unmount persist all changes 2026-11-09 22:52 ` [PATCH fstests v3] " Josef Bacik @ 2026-11-10 10:42 ` Amir Goldstein 2026-11-10 12:46 ` Zorro Lang 2026-11-10 16:27 ` [PATCH fstests v4] " Josef Bacik 1 sibling, 1 reply; 9+ messages in thread From: Amir Goldstein @ 2026-11-10 10:42 UTC (permalink / raw) To: Josef Bacik; +Cc: fstests, Zorro Lang, Christian Brauner, linux-unionfs On Mon, Nov 9, 2026 at 11:52 PM Josef Bacik <josef@toxicpanda.com> wrote: > > overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) > and unmount. With fsync=relaxed it only writes the upper inodes that were > modified through the overlay, so it has to know about all of them. > > Modify an overlay in many ways: new files and directories, data and > metadata copy up, stores through a shared mapping, sendfile, copy_range, > an O_TMPFILE, a short and a long symlink, hard links, renames, whiteouts. > Do the same to files that were synced before, so that nothing but the > modification itself tells overlayfs about them. Then drop the caches, sync > or unmount the overlay, shut down the base filesystem without flushing its > log, cycle the mounts and check that nothing has changed. > > Do that with every fsync mode that syncs, with and without redirect_dir and > metacopy. > > The base filesystem has to have a journal, so what gets lost if overlayfs > misses a modification is data in the page cache. Changes of metadata are > in the log, which overlayfs commits in any case. > > Signed-off-by: Josef Bacik <josef@toxicpanda.com> > --- > v1: https://lore.kernel.org/r/773faf2a275e0a51bd38cf68f492856c.josef@toxicpanda.com > v2: https://lore.kernel.org/r/946b46fbf1c555a47a8a9304f2ee31af.josef@toxicpanda.com > > v2->v3: > - It's overlay/090 now. (Amir) Sorry to nag. Zorro renumbers test conflicts anyway before merge, but I have a staged 090 test for bugfix already staged on ovl-fixes: https://lore.kernel.org/linux-unionfs/20261109134006.2555438-1-giuseppe@scrivano.org/ so please take 091 to ease my own workflow (apply and test your patch). > - Every round passes fsync=, with fsync=auto in place of the default, and > _exclude_scratch_mount_option is gone. (Amir) > - Added _require_scratch_overlay_features fsync redirect_dir metacopy. That > needs [2], and the test doesn't run on older kernels any more. (Amir) > - The rounds are in the golden output. (Amir) > - Every fsync mode runs with redirect_dir and metacopy off and on, that's 16 > rounds. It takes 70s here, so it's not in quick any more. (Amir) Damn, this sucks. This is a simple test is should by all means be quick. Some suggestion (pick any): 1. Test only relaxed and on, which your patch set add auto is always one of them and strict is not related to syncfs/umount at all 2. md5sum all the file at one invocation 3. find . -printf '%p %y %m %U:%G %n %s %l\n' in place of many stat invocations 2 and 3 are quick suggestions from Opus 5.5 based on guess work I am sure that your models with access to the test run can do much better no need to overdo and no need to pay penalty for test readability but 70 seconds is way off the mark for a test like this > - The mmap step with the syncfs in it comes first, it used to write > everything that came before it. (Sashiko, Amir) > - The caches are dropped after the snapshot, for all files. (Sashiko, Amir) > - The overlay is mounted with noatime. The snapshot reads every file, the > atime update marks the overlay inode dirty, and that hid any modification > that overlayfs had missed. > - Added files that are synced before they are modified. Creating a file and > copying it up mark the overlay inode dirty, and that hid any later > modification that overlayfs had missed. > - Added fallocate, and a copy_range that isn't aligned so xfs can't reflink > it. Oh my, so many "hid modification that missed" I guess I need to trust the AI test/review process which found them ;) Thanks, Amir. > > This is for "ovl: only sync what was written through the overlay" [1]. > > It passes on ext4 and xfs with the series, and with only the first patch of > it, which adds the mount options and still syncs the whole upper file > system. > > I ran it on four kernels that I broke on purpose. All of them fail, only in > the fsync=auto and fsync=relaxed rounds: > > - The function that marks the overlay inode dirty does nothing: the file > data is gone. > - Only ovl_copyattr() doesn't mark the inode: the pwrite, the sendfile and > the copy_range to the files that were synced before are gone. v2 passes on > this one. > - A dirty overlay inode isn't kept after the last iput(): the file data is > gone. v2 only notices in its unmount rounds. > - Nothing is written at unmount: the unmount rounds lose the file data. > > [1] https://lore.kernel.org/r/20261107-b4-ovl-containerized-sync-v5-0-d34b4ae57fdf@toxicpanda.com > [2] https://lore.kernel.org/r/20261106155832.3867017-1-amir73il@gmail.com > > tests/overlay/090 | 214 ++++++++++++++++++++++++++++++++++++++++++ > tests/overlay/090.out | 17 ++++ > 2 files changed, 231 insertions(+) > create mode 100755 tests/overlay/090 > create mode 100644 tests/overlay/090.out > > diff --git a/tests/overlay/090 b/tests/overlay/090 > new file mode 100755 > index 00000000..7f6e58f4 > --- /dev/null > +++ b/tests/overlay/090 > @@ -0,0 +1,214 @@ > +#! /bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. > +# > +# FS QA Test 090 > +# > +# Test that syncfs(2) on an overlay, and unmounting one, persist everything > +# that was modified through it. > +# > +# overlayfs used to sync the whole upper filesystem for that. With > +# fsync=relaxed it only writes the upper inodes that were modified through this > +# overlay, so it has to know about all of them. > +# > +# Modify the overlay in every way we can think of, sync it, shut down the base > +# filesystem without flushing its log, cycle the mounts and check that nothing > +# has changed. > +# > +. ./common/preamble > +_begin_fstest auto shutdown copyup whiteout mmap redirect metacopy > + > +. ./common/attr > + > +_require_scratch > +_require_scratch_shutdown > +_require_metadata_journaling $SCRATCH_DEV > +_require_scratch_overlay_features fsync redirect_dir metacopy > +_require_xfs_io_command "syncfs" > +_require_xfs_io_command "sendfile" > +_require_xfs_io_command "copy_range" > +_require_xfs_io_command "falloc" > +_require_xfs_io_command "-T" > +_require_xfs_io_command "flink" > +_require_attrs > +_require_chown > +_require_mknod > + > +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER > + > +create_lower() > +{ > + local f > + > + # This leaves the base filesystem mounted > + _scratch_mkfs >> $seqres.full 2>&1 > + > + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir > + for f in append truncate mmap chmod chown chown_append xattr link \ > + rename remove dir/append rmdir/file mvdir/file; do > + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" $lowerdir/$f \ > + >> $seqres.full > + done > + > + # These were not written through the overlay, so it does not have to > + # sync them. > + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT > +} > + > +# Creating a file and copying it up are modifications. overlayfs remembers them > +# until the file is synced, and a later modification that it misses does not > +# show. So make files that are in the upper layer and have been synced. > +prepare() > +{ > + local m=$SCRATCH_MNT > + local f > + > + mkdir $m/clean > + for f in pwrite truncate fallocate mmap sendfile copy_range chmod \ > + chown xattr link rename remove; do > + $XFS_IO_PROG -f -c "pwrite -S 0x70 0 256k" $m/clean/$f \ > + >> $seqres.full > + done > + $XFS_IO_PROG -c "syncfs" $m > +} > + > +modify() > +{ > + local m=$SCRATCH_MNT > + > + # Stores through a shared mapping, also after a syncfs, no msync. This > + # has to be the first one, the syncfs must not write anything else. > + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ > + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full > + > + # The files that overlayfs has nothing to remember about > + $XFS_IO_PROG -c "pwrite -S 0x71 64k 64k" $m/clean/pwrite >> $seqres.full > + $XFS_IO_PROG -c "truncate 4k" $m/clean/truncate > + $XFS_IO_PROG -c "falloc 256k 64k" $m/clean/fallocate > + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x72 64k 64k" \ > + $m/clean/mmap >> $seqres.full > + $XFS_IO_PROG -c "sendfile -i $m/link 0 64k" $m/clean/sendfile \ > + >> $seqres.full > + # Not aligned, so that the data is copied and not shared > + $XFS_IO_PROG -c "copy_range -s 1 -d 1 -l 64k $m/link" \ > + $m/clean/copy_range > + chmod 640 $m/clean/chmod > + chown 1234:1234 $m/clean/chown > + $SETFATTR_PROG -n user.test -v value $m/clean/xattr > + ln $m/clean/link $m/clean/link2 > + mv $m/clean/rename $m/clean/renamed > + rm $m/clean/remove > + > + # New files and directories > + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full > + mkdir $m/newdir > + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full > + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full > + ln -s new $m/symlink > + # Too long to be stored in the inode > + ln -s $(printf 'a%.0s' $(seq 1000)) $m/longsymlink > + mkfifo $m/fifo > + > + # Copy up, then change the data > + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full > + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full > + $XFS_IO_PROG -c "truncate 4k" $m/truncate > + > + # The other ways to write data > + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ > + >> $seqres.full > + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range > + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ > + >> $seqres.full > + > + # Copy up, then change the metadata > + chmod 640 $m/chmod > + chown 1234:1234 $m/chown > + $SETFATTR_PROG -n user.test -v value $m/xattr > + > + # With metacopy=on the first one only copies up the metadata > + chown 1234:1234 $m/chown_append > + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ > + >> $seqres.full > + > + # Names > + ln $m/link $m/link2 > + mv $m/rename $m/renamed > + # Without redirect_dir=on mv gets EXDEV and copies the directory > + mv $m/mvdir $m/mvdir2 > + rm $m/remove > + rm -r $m/rmdir > +} > + > +# No inode numbers, no timestamps, and no sizes and link counts of directories > +snapshot() > +{ > + local f > + > + cd $SCRATCH_MNT > + find . | LC_ALL=C sort | while read -r f; do > + if [ -L "$f" ]; then > + echo "$f $(stat -c '%F %u:%g' "$f") $(readlink "$f")" > + elif [ -f "$f" ]; then > + echo "$f $(stat -c '%F %a %u:%g %h %s' "$f")" \ > + "$(md5sum < "$f")" > + else > + echo "$f $(stat -c '%F %a %u:%g' "$f")" > + fi > + done > + _getfattr --absolute-names -n user.test xattr clean/xattr 2>&1 > + cd - > /dev/null > +} > + > +# <syncfs|umount> <mount options> > +do_test() > +{ > + local how=$1 > + local opts=$2 > + > + echo "=== $how $opts" | tee -a $seqres.full > + > + create_lower > + > + # An atime update is a modification too. With atime the snapshot would > + # make overlayfs write every file that it reads, also one that > + # overlayfs has lost track of. > + _scratch_mount -o noatime,$opts > + > + prepare > + modify > + snapshot > $tmp.before > + > + # Nobody holds on to the overlay inodes when they are synced > + echo 2 > /proc/sys/vm/drop_caches > + > + if [ $how = syncfs ]; then > + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT > + _scratch_shutdown | tee -a $seqres.full > + _scratch_unmount > + else > + # Only the overlay, the base filesystem stays mounted > + _unmount $SCRATCH_MNT > + _scratch_shutdown | tee -a $seqres.full > + _unmount $OVL_BASE_SCRATCH_MNT > + fi > + > + _scratch_mount -o noatime,$opts > + snapshot > $tmp.after > + cat $tmp.after >> $seqres.full > + diff -u $tmp.before $tmp.after > + _scratch_unmount > +} > + > +for how in syncfs umount; do > + for mode in auto relaxed on strict; do > + for meta in off on; do > + do_test $how \ > + fsync=$mode,redirect_dir=$meta,metacopy=$meta > + done > + done > +done > + > +# success, all done > +status=0 > +exit > diff --git a/tests/overlay/090.out b/tests/overlay/090.out > new file mode 100644 > index 00000000..d6b46515 > --- /dev/null > +++ b/tests/overlay/090.out > @@ -0,0 +1,17 @@ > +QA output created by 090 > +=== syncfs fsync=auto,redirect_dir=off,metacopy=off > +=== syncfs fsync=auto,redirect_dir=on,metacopy=on > +=== syncfs fsync=relaxed,redirect_dir=off,metacopy=off > +=== syncfs fsync=relaxed,redirect_dir=on,metacopy=on > +=== syncfs fsync=on,redirect_dir=off,metacopy=off > +=== syncfs fsync=on,redirect_dir=on,metacopy=on > +=== syncfs fsync=strict,redirect_dir=off,metacopy=off > +=== syncfs fsync=strict,redirect_dir=on,metacopy=on > +=== umount fsync=auto,redirect_dir=off,metacopy=off > +=== umount fsync=auto,redirect_dir=on,metacopy=on > +=== umount fsync=relaxed,redirect_dir=off,metacopy=off > +=== umount fsync=relaxed,redirect_dir=on,metacopy=on > +=== umount fsync=on,redirect_dir=off,metacopy=off > +=== umount fsync=on,redirect_dir=on,metacopy=on > +=== umount fsync=strict,redirect_dir=off,metacopy=off > +=== umount fsync=strict,redirect_dir=on,metacopy=on > -- > 2.55.0 > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH fstests v3] overlay: test that syncfs and unmount persist all changes 2026-11-10 10:42 ` Amir Goldstein @ 2026-11-10 12:46 ` Zorro Lang 0 siblings, 0 replies; 9+ messages in thread From: Zorro Lang @ 2026-11-10 12:46 UTC (permalink / raw) To: Amir Goldstein; +Cc: Josef Bacik, fstests, Christian Brauner, linux-unionfs On Tue, Nov 10, 2026 at 11:42:57AM +0100, Amir Goldstein wrote: > On Mon, Nov 9, 2026 at 11:52 PM Josef Bacik <josef@toxicpanda.com> wrote: > > > > overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) > > and unmount. With fsync=relaxed it only writes the upper inodes that were > > modified through the overlay, so it has to know about all of them. > > > > Modify an overlay in many ways: new files and directories, data and > > metadata copy up, stores through a shared mapping, sendfile, copy_range, > > an O_TMPFILE, a short and a long symlink, hard links, renames, whiteouts. > > Do the same to files that were synced before, so that nothing but the > > modification itself tells overlayfs about them. Then drop the caches, sync > > or unmount the overlay, shut down the base filesystem without flushing its > > log, cycle the mounts and check that nothing has changed. > > > > Do that with every fsync mode that syncs, with and without redirect_dir and > > metacopy. > > > > The base filesystem has to have a journal, so what gets lost if overlayfs > > misses a modification is data in the page cache. Changes of metadata are > > in the log, which overlayfs commits in any case. > > > > Signed-off-by: Josef Bacik <josef@toxicpanda.com> > > --- > > v1: https://lore.kernel.org/r/773faf2a275e0a51bd38cf68f492856c.josef@toxicpanda.com > > v2: https://lore.kernel.org/r/946b46fbf1c555a47a8a9304f2ee31af.josef@toxicpanda.com > > > > v2->v3: > > - It's overlay/090 now. (Amir) > > Sorry to nag. Zorro renumbers test conflicts anyway before merge, > but I have a staged 090 test for bugfix already staged on ovl-fixes: > https://lore.kernel.org/linux-unionfs/20261109134006.2555438-1-giuseppe@scrivano.org/ > so please take 091 to ease my own workflow (apply and test your patch). Hi Amir, Thanks to the developers of each filesystem for helping review the patches. No need to worry about the case number:) If you're concerned about conflicts, feel free to use 999 or any higher 4-digit number. Although having a large dummy number makes merging much easier than dealing with a conflicting one, I'll handle both cases during the merge anyway. If you have a specific number range in mind, just let me know when you send a patch. Thanks, Zorro > > > - Every round passes fsync=, with fsync=auto in place of the default, and > > _exclude_scratch_mount_option is gone. (Amir) > > - Added _require_scratch_overlay_features fsync redirect_dir metacopy. That > > needs [2], and the test doesn't run on older kernels any more. (Amir) > > - The rounds are in the golden output. (Amir) > > - Every fsync mode runs with redirect_dir and metacopy off and on, that's 16 > > rounds. It takes 70s here, so it's not in quick any more. (Amir) > > Damn, this sucks. This is a simple test is should by all means be quick. > Some suggestion (pick any): > 1. Test only relaxed and on, which your patch set add > auto is always one of them and strict is not related to syncfs/umount at all > 2. md5sum all the file at one invocation > 3. find . -printf '%p %y %m %U:%G %n %s %l\n' in place of many stat invocations > > 2 and 3 are quick suggestions from Opus 5.5 based on guess work > I am sure that your models with access to the test run can do much better > no need to overdo and no need to pay penalty for test readability > but 70 seconds is way off the mark for a test like this > > > - The mmap step with the syncfs in it comes first, it used to write > > everything that came before it. (Sashiko, Amir) > > - The caches are dropped after the snapshot, for all files. (Sashiko, Amir) > > - The overlay is mounted with noatime. The snapshot reads every file, the > > atime update marks the overlay inode dirty, and that hid any modification > > that overlayfs had missed. > > - Added files that are synced before they are modified. Creating a file and > > copying it up mark the overlay inode dirty, and that hid any later > > modification that overlayfs had missed. > > - Added fallocate, and a copy_range that isn't aligned so xfs can't reflink > > it. > > Oh my, so many "hid modification that missed" > I guess I need to trust the AI test/review process which found them ;) > > Thanks, > Amir. > > > > > This is for "ovl: only sync what was written through the overlay" [1]. > > > > It passes on ext4 and xfs with the series, and with only the first patch of > > it, which adds the mount options and still syncs the whole upper file > > system. > > > > I ran it on four kernels that I broke on purpose. All of them fail, only in > > the fsync=auto and fsync=relaxed rounds: > > > > - The function that marks the overlay inode dirty does nothing: the file > > data is gone. > > - Only ovl_copyattr() doesn't mark the inode: the pwrite, the sendfile and > > the copy_range to the files that were synced before are gone. v2 passes on > > this one. > > - A dirty overlay inode isn't kept after the last iput(): the file data is > > gone. v2 only notices in its unmount rounds. > > - Nothing is written at unmount: the unmount rounds lose the file data. > > > > [1] https://lore.kernel.org/r/20261107-b4-ovl-containerized-sync-v5-0-d34b4ae57fdf@toxicpanda.com > > [2] https://lore.kernel.org/r/20261106155832.3867017-1-amir73il@gmail.com > > > > tests/overlay/090 | 214 ++++++++++++++++++++++++++++++++++++++++++ > > tests/overlay/090.out | 17 ++++ > > 2 files changed, 231 insertions(+) > > create mode 100755 tests/overlay/090 > > create mode 100644 tests/overlay/090.out > > > > diff --git a/tests/overlay/090 b/tests/overlay/090 > > new file mode 100755 > > index 00000000..7f6e58f4 > > --- /dev/null > > +++ b/tests/overlay/090 > > @@ -0,0 +1,214 @@ > > +#! /bin/bash > > +# SPDX-License-Identifier: GPL-2.0 > > +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. > > +# > > +# FS QA Test 090 > > +# > > +# Test that syncfs(2) on an overlay, and unmounting one, persist everything > > +# that was modified through it. > > +# > > +# overlayfs used to sync the whole upper filesystem for that. With > > +# fsync=relaxed it only writes the upper inodes that were modified through this > > +# overlay, so it has to know about all of them. > > +# > > +# Modify the overlay in every way we can think of, sync it, shut down the base > > +# filesystem without flushing its log, cycle the mounts and check that nothing > > +# has changed. > > +# > > +. ./common/preamble > > +_begin_fstest auto shutdown copyup whiteout mmap redirect metacopy > > + > > +. ./common/attr > > + > > +_require_scratch > > +_require_scratch_shutdown > > +_require_metadata_journaling $SCRATCH_DEV > > +_require_scratch_overlay_features fsync redirect_dir metacopy > > +_require_xfs_io_command "syncfs" > > +_require_xfs_io_command "sendfile" > > +_require_xfs_io_command "copy_range" > > +_require_xfs_io_command "falloc" > > +_require_xfs_io_command "-T" > > +_require_xfs_io_command "flink" > > +_require_attrs > > +_require_chown > > +_require_mknod > > + > > +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER > > + > > +create_lower() > > +{ > > + local f > > + > > + # This leaves the base filesystem mounted > > + _scratch_mkfs >> $seqres.full 2>&1 > > + > > + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir > > + for f in append truncate mmap chmod chown chown_append xattr link \ > > + rename remove dir/append rmdir/file mvdir/file; do > > + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" $lowerdir/$f \ > > + >> $seqres.full > > + done > > + > > + # These were not written through the overlay, so it does not have to > > + # sync them. > > + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT > > +} > > + > > +# Creating a file and copying it up are modifications. overlayfs remembers them > > +# until the file is synced, and a later modification that it misses does not > > +# show. So make files that are in the upper layer and have been synced. > > +prepare() > > +{ > > + local m=$SCRATCH_MNT > > + local f > > + > > + mkdir $m/clean > > + for f in pwrite truncate fallocate mmap sendfile copy_range chmod \ > > + chown xattr link rename remove; do > > + $XFS_IO_PROG -f -c "pwrite -S 0x70 0 256k" $m/clean/$f \ > > + >> $seqres.full > > + done > > + $XFS_IO_PROG -c "syncfs" $m > > +} > > + > > +modify() > > +{ > > + local m=$SCRATCH_MNT > > + > > + # Stores through a shared mapping, also after a syncfs, no msync. This > > + # has to be the first one, the syncfs must not write anything else. > > + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ > > + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full > > + > > + # The files that overlayfs has nothing to remember about > > + $XFS_IO_PROG -c "pwrite -S 0x71 64k 64k" $m/clean/pwrite >> $seqres.full > > + $XFS_IO_PROG -c "truncate 4k" $m/clean/truncate > > + $XFS_IO_PROG -c "falloc 256k 64k" $m/clean/fallocate > > + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x72 64k 64k" \ > > + $m/clean/mmap >> $seqres.full > > + $XFS_IO_PROG -c "sendfile -i $m/link 0 64k" $m/clean/sendfile \ > > + >> $seqres.full > > + # Not aligned, so that the data is copied and not shared > > + $XFS_IO_PROG -c "copy_range -s 1 -d 1 -l 64k $m/link" \ > > + $m/clean/copy_range > > + chmod 640 $m/clean/chmod > > + chown 1234:1234 $m/clean/chown > > + $SETFATTR_PROG -n user.test -v value $m/clean/xattr > > + ln $m/clean/link $m/clean/link2 > > + mv $m/clean/rename $m/clean/renamed > > + rm $m/clean/remove > > + > > + # New files and directories > > + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full > > + mkdir $m/newdir > > + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full > > + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full > > + ln -s new $m/symlink > > + # Too long to be stored in the inode > > + ln -s $(printf 'a%.0s' $(seq 1000)) $m/longsymlink > > + mkfifo $m/fifo > > + > > + # Copy up, then change the data > > + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full > > + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full > > + $XFS_IO_PROG -c "truncate 4k" $m/truncate > > + > > + # The other ways to write data > > + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ > > + >> $seqres.full > > + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range > > + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ > > + >> $seqres.full > > + > > + # Copy up, then change the metadata > > + chmod 640 $m/chmod > > + chown 1234:1234 $m/chown > > + $SETFATTR_PROG -n user.test -v value $m/xattr > > + > > + # With metacopy=on the first one only copies up the metadata > > + chown 1234:1234 $m/chown_append > > + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ > > + >> $seqres.full > > + > > + # Names > > + ln $m/link $m/link2 > > + mv $m/rename $m/renamed > > + # Without redirect_dir=on mv gets EXDEV and copies the directory > > + mv $m/mvdir $m/mvdir2 > > + rm $m/remove > > + rm -r $m/rmdir > > +} > > + > > +# No inode numbers, no timestamps, and no sizes and link counts of directories > > +snapshot() > > +{ > > + local f > > + > > + cd $SCRATCH_MNT > > + find . | LC_ALL=C sort | while read -r f; do > > + if [ -L "$f" ]; then > > + echo "$f $(stat -c '%F %u:%g' "$f") $(readlink "$f")" > > + elif [ -f "$f" ]; then > > + echo "$f $(stat -c '%F %a %u:%g %h %s' "$f")" \ > > + "$(md5sum < "$f")" > > + else > > + echo "$f $(stat -c '%F %a %u:%g' "$f")" > > + fi > > + done > > + _getfattr --absolute-names -n user.test xattr clean/xattr 2>&1 > > + cd - > /dev/null > > +} > > + > > +# <syncfs|umount> <mount options> > > +do_test() > > +{ > > + local how=$1 > > + local opts=$2 > > + > > + echo "=== $how $opts" | tee -a $seqres.full > > + > > + create_lower > > + > > + # An atime update is a modification too. With atime the snapshot would > > + # make overlayfs write every file that it reads, also one that > > + # overlayfs has lost track of. > > + _scratch_mount -o noatime,$opts > > + > > + prepare > > + modify > > + snapshot > $tmp.before > > + > > + # Nobody holds on to the overlay inodes when they are synced > > + echo 2 > /proc/sys/vm/drop_caches > > + > > + if [ $how = syncfs ]; then > > + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT > > + _scratch_shutdown | tee -a $seqres.full > > + _scratch_unmount > > + else > > + # Only the overlay, the base filesystem stays mounted > > + _unmount $SCRATCH_MNT > > + _scratch_shutdown | tee -a $seqres.full > > + _unmount $OVL_BASE_SCRATCH_MNT > > + fi > > + > > + _scratch_mount -o noatime,$opts > > + snapshot > $tmp.after > > + cat $tmp.after >> $seqres.full > > + diff -u $tmp.before $tmp.after > > + _scratch_unmount > > +} > > + > > +for how in syncfs umount; do > > + for mode in auto relaxed on strict; do > > + for meta in off on; do > > + do_test $how \ > > + fsync=$mode,redirect_dir=$meta,metacopy=$meta > > + done > > + done > > +done > > + > > +# success, all done > > +status=0 > > +exit > > diff --git a/tests/overlay/090.out b/tests/overlay/090.out > > new file mode 100644 > > index 00000000..d6b46515 > > --- /dev/null > > +++ b/tests/overlay/090.out > > @@ -0,0 +1,17 @@ > > +QA output created by 090 > > +=== syncfs fsync=auto,redirect_dir=off,metacopy=off > > +=== syncfs fsync=auto,redirect_dir=on,metacopy=on > > +=== syncfs fsync=relaxed,redirect_dir=off,metacopy=off > > +=== syncfs fsync=relaxed,redirect_dir=on,metacopy=on > > +=== syncfs fsync=on,redirect_dir=off,metacopy=off > > +=== syncfs fsync=on,redirect_dir=on,metacopy=on > > +=== syncfs fsync=strict,redirect_dir=off,metacopy=off > > +=== syncfs fsync=strict,redirect_dir=on,metacopy=on > > +=== umount fsync=auto,redirect_dir=off,metacopy=off > > +=== umount fsync=auto,redirect_dir=on,metacopy=on > > +=== umount fsync=relaxed,redirect_dir=off,metacopy=off > > +=== umount fsync=relaxed,redirect_dir=on,metacopy=on > > +=== umount fsync=on,redirect_dir=off,metacopy=off > > +=== umount fsync=on,redirect_dir=on,metacopy=on > > +=== umount fsync=strict,redirect_dir=off,metacopy=off > > +=== umount fsync=strict,redirect_dir=on,metacopy=on > > -- > > 2.55.0 > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH fstests v4] overlay: test that syncfs and unmount persist all changes 2026-11-09 22:52 ` [PATCH fstests v3] " Josef Bacik 2026-11-10 10:42 ` Amir Goldstein @ 2026-11-10 16:27 ` Josef Bacik 2026-11-11 10:36 ` Amir Goldstein 1 sibling, 1 reply; 9+ messages in thread From: Josef Bacik @ 2026-11-10 16:27 UTC (permalink / raw) To: fstests; +Cc: Zorro Lang, Amir Goldstein, Christian Brauner, linux-unionfs overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) and unmount. With fsync=relaxed it only writes the upper inodes that were modified through the overlay, so it has to know about all of them. Modify an overlay in many ways: new files and directories, data and metadata copy up, stores through a shared mapping, sendfile, copy_range, an O_TMPFILE, a short and a long symlink, hard links, renames, whiteouts. Do the same to files that were synced before, so that nothing but the modification itself tells overlayfs about them. Then drop the caches, sync or unmount the overlay, shut down the base filesystem without flushing its log, cycle the mounts and check that nothing has changed. Do that with fsync=relaxed and with fsync=on, which still syncs the whole upper filesystem, with and without redirect_dir and metacopy. The base filesystem has to have a journal, so what gets lost if overlayfs misses a modification is data in the page cache. Changes of metadata are in the log, which overlayfs commits in any case. Signed-off-by: Josef Bacik <josef@toxicpanda.com> --- v1: https://lore.kernel.org/r/773faf2a275e0a51bd38cf68f492856c.josef@toxicpanda.com v2: https://lore.kernel.org/r/946b46fbf1c555a47a8a9304f2ee31af.josef@toxicpanda.com v3: https://lore.kernel.org/r/94833333cf2c5dd487022cb290174393.josef@toxicpanda.com v3->v4: - It's overlay/091 now. (Amir) - It takes 17-18s instead of 64-67s and is back in quick. (Amir) - Only fsync=relaxed and fsync=on, that's 8 rounds instead of 16. - The snapshot is one find -printf and one md5sum instead of a stat and an md5sum per file. - One xfs_io writes all the files of the lower layer, and another one all the files that are synced before they are modified. Those times are from ./check in a KVM guest with 4 CPUs, on a kernel with KASAN, lockdep, UBSAN and DEBUG_OBJECTS. A round takes 1.8s there, a third of that is the 40 or so commands that modify the overlay. With the fsync=auto rounds gone nothing goes through the code that turns auto into relaxed. In v3 those rounds never showed anything that the relaxed rounds didn't. This is for "ovl: only sync what was written through the overlay" [1]. It needs [2]. It passes on ext4 and xfs with the series, and with only the first patch of it, which adds the mount options and still syncs the whole upper file system. On the four kernels that I broke on purpose for v3 it loses exactly the same files as v3 in the fsync=relaxed rounds, and nothing in the fsync=on rounds. [1] https://lore.kernel.org/r/20261110-b4-ovl-containerized-sync-v6-0-43d1be16a4d6@toxicpanda.com [2] https://lore.kernel.org/r/20261106155832.3867017-1-amir73il@gmail.com tests/overlay/091 | 204 ++++++++++++++++++++++++++++++++++++++++++ tests/overlay/091.out | 9 ++ 2 files changed, 213 insertions(+) create mode 100755 tests/overlay/091 create mode 100644 tests/overlay/091.out diff --git a/tests/overlay/091 b/tests/overlay/091 new file mode 100755 index 00000000..260978e3 --- /dev/null +++ b/tests/overlay/091 @@ -0,0 +1,204 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. +# +# FS QA Test 091 +# +# Test that syncfs(2) on an overlay, and unmounting one, persist everything +# that was modified through it. +# +# overlayfs used to sync the whole upper filesystem for that. With +# fsync=relaxed it only writes the upper inodes that were modified through this +# overlay, so it has to know about all of them. +# +# Modify the overlay in every way we can think of, sync it, shut down the base +# filesystem without flushing its log, cycle the mounts and check that nothing +# has changed. +# +. ./common/preamble +_begin_fstest auto quick shutdown copyup whiteout mmap redirect metacopy + +. ./common/attr + +_require_scratch +_require_scratch_shutdown +_require_metadata_journaling $SCRATCH_DEV +_require_scratch_overlay_features fsync redirect_dir metacopy +_require_xfs_io_command "syncfs" +_require_xfs_io_command "sendfile" +_require_xfs_io_command "copy_range" +_require_xfs_io_command "falloc" +_require_xfs_io_command "-T" +_require_xfs_io_command "flink" +_require_attrs +_require_chown +_require_mknod + +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER + +create_lower() +{ + # This leaves the base filesystem mounted + _scratch_mkfs >> $seqres.full 2>&1 + + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir + cd $lowerdir + # xfs_io runs the command on every file + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" \ + append truncate mmap chmod chown chown_append xattr link \ + rename remove dir/append rmdir/file mvdir/file >> $seqres.full + cd - > /dev/null + + # These were not written through the overlay, so it does not have to + # sync them. + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT +} + +# Creating a file and copying it up are modifications. overlayfs remembers them +# until the file is synced, and a later modification that it misses does not +# show. So make files that are in the upper layer and have been synced. +prepare() +{ + local m=$SCRATCH_MNT + + mkdir $m/clean + cd $m/clean + # xfs_io runs the command on every file + $XFS_IO_PROG -f -c "pwrite -S 0x70 0 256k" \ + pwrite truncate fallocate mmap sendfile copy_range chmod \ + chown xattr link rename remove >> $seqres.full + cd - > /dev/null + $XFS_IO_PROG -c "syncfs" $m +} + +modify() +{ + local m=$SCRATCH_MNT + + # Stores through a shared mapping, also after a syncfs, no msync. This + # has to be the first one, the syncfs must not write anything else. + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full + + # The files that overlayfs has nothing to remember about + $XFS_IO_PROG -c "pwrite -S 0x71 64k 64k" $m/clean/pwrite >> $seqres.full + $XFS_IO_PROG -c "truncate 4k" $m/clean/truncate + $XFS_IO_PROG -c "falloc 256k 64k" $m/clean/fallocate + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x72 64k 64k" \ + $m/clean/mmap >> $seqres.full + $XFS_IO_PROG -c "sendfile -i $m/link 0 64k" $m/clean/sendfile \ + >> $seqres.full + # Not aligned, so that the data is copied and not shared + $XFS_IO_PROG -c "copy_range -s 1 -d 1 -l 64k $m/link" \ + $m/clean/copy_range + chmod 640 $m/clean/chmod + chown 1234:1234 $m/clean/chown + $SETFATTR_PROG -n user.test -v value $m/clean/xattr + ln $m/clean/link $m/clean/link2 + mv $m/clean/rename $m/clean/renamed + rm $m/clean/remove + + # New files and directories + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full + mkdir $m/newdir + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full + ln -s new $m/symlink + # Too long to be stored in the inode + ln -s $(printf 'a%.0s' $(seq 1000)) $m/longsymlink + mkfifo $m/fifo + + # Copy up, then change the data + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full + $XFS_IO_PROG -c "truncate 4k" $m/truncate + + # The other ways to write data + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ + >> $seqres.full + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ + >> $seqres.full + + # Copy up, then change the metadata + chmod 640 $m/chmod + chown 1234:1234 $m/chown + $SETFATTR_PROG -n user.test -v value $m/xattr + + # With metacopy=on the first one only copies up the metadata + chown 1234:1234 $m/chown_append + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ + >> $seqres.full + + # Names + ln $m/link $m/link2 + mv $m/rename $m/renamed + # Without redirect_dir=on mv gets EXDEV and copies the directory + mv $m/mvdir $m/mvdir2 + rm $m/remove + rm -r $m/rmdir +} + +# No inode numbers, no timestamps, and no sizes and link counts of directories +snapshot() +{ + cd $SCRATCH_MNT + find . -type d -printf '%p %y %m %U:%G\n' -o \ + -printf '%p %y %m %U:%G %n %s %l\n' | LC_ALL=C sort + find . -type f -print0 | LC_ALL=C sort -z | xargs -0 md5sum + _getfattr --absolute-names -n user.test xattr clean/xattr 2>&1 + cd - > /dev/null +} + +# <syncfs|umount> <mount options> +do_test() +{ + local how=$1 + local opts=$2 + + echo "=== $how $opts" | tee -a $seqres.full + + create_lower + + # An atime update is a modification too. With atime the snapshot would + # make overlayfs write every file that it reads, also one that + # overlayfs has lost track of. + _scratch_mount -o noatime,$opts + + prepare + modify + snapshot > $tmp.before + + # Nobody holds on to the overlay inodes when they are synced + echo 2 > /proc/sys/vm/drop_caches + + if [ $how = syncfs ]; then + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT + _scratch_shutdown | tee -a $seqres.full + _scratch_unmount + else + # Only the overlay, the base filesystem stays mounted + _unmount $SCRATCH_MNT + _scratch_shutdown | tee -a $seqres.full + _unmount $OVL_BASE_SCRATCH_MNT + fi + + _scratch_mount -o noatime,$opts + snapshot > $tmp.after + cat $tmp.after >> $seqres.full + diff -u $tmp.before $tmp.after + _scratch_unmount +} + +for how in syncfs umount; do + for mode in relaxed on; do + for meta in off on; do + do_test $how \ + fsync=$mode,redirect_dir=$meta,metacopy=$meta + done + done +done + +# success, all done +status=0 +exit diff --git a/tests/overlay/091.out b/tests/overlay/091.out new file mode 100644 index 00000000..a8536983 --- /dev/null +++ b/tests/overlay/091.out @@ -0,0 +1,9 @@ +QA output created by 091 +=== syncfs fsync=relaxed,redirect_dir=off,metacopy=off +=== syncfs fsync=relaxed,redirect_dir=on,metacopy=on +=== syncfs fsync=on,redirect_dir=off,metacopy=off +=== syncfs fsync=on,redirect_dir=on,metacopy=on +=== umount fsync=relaxed,redirect_dir=off,metacopy=off +=== umount fsync=relaxed,redirect_dir=on,metacopy=on +=== umount fsync=on,redirect_dir=off,metacopy=off +=== umount fsync=on,redirect_dir=on,metacopy=on -- 2.55.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH fstests v4] overlay: test that syncfs and unmount persist all changes 2026-11-10 16:27 ` [PATCH fstests v4] " Josef Bacik @ 2026-11-11 10:36 ` Amir Goldstein 0 siblings, 0 replies; 9+ messages in thread From: Amir Goldstein @ 2026-11-11 10:36 UTC (permalink / raw) To: Josef Bacik; +Cc: fstests, Zorro Lang, Christian Brauner, linux-unionfs On Tue, Nov 10, 2026 at 5:27 PM Josef Bacik <josef@toxicpanda.com> wrote: > > overlayfs is about to stop syncing the whole upper filesystem on syncfs(2) > and unmount. With fsync=relaxed it only writes the upper inodes that were > modified through the overlay, so it has to know about all of them. > > Modify an overlay in many ways: new files and directories, data and > metadata copy up, stores through a shared mapping, sendfile, copy_range, > an O_TMPFILE, a short and a long symlink, hard links, renames, whiteouts. > Do the same to files that were synced before, so that nothing but the > modification itself tells overlayfs about them. Then drop the caches, sync > or unmount the overlay, shut down the base filesystem without flushing its > log, cycle the mounts and check that nothing has changed. > > Do that with fsync=relaxed and with fsync=on, which still syncs the whole > upper filesystem, with and without redirect_dir and metacopy. > > The base filesystem has to have a journal, so what gets lost if overlayfs > misses a modification is data in the page cache. Changes of metadata are > in the log, which overlayfs commits in any case. > > Signed-off-by: Josef Bacik <josef@toxicpanda.com> Yey! under 30s :) Reviewed-by: Amir Goldstein <amir73il@gmail.com> > --- > v1: https://lore.kernel.org/r/773faf2a275e0a51bd38cf68f492856c.josef@toxicpanda.com > v2: https://lore.kernel.org/r/946b46fbf1c555a47a8a9304f2ee31af.josef@toxicpanda.com > v3: https://lore.kernel.org/r/94833333cf2c5dd487022cb290174393.josef@toxicpanda.com > > v3->v4: > - It's overlay/091 now. (Amir) > - It takes 17-18s instead of 64-67s and is back in quick. (Amir) > - Only fsync=relaxed and fsync=on, that's 8 rounds instead of 16. > - The snapshot is one find -printf and one md5sum instead of a stat and an > md5sum per file. > - One xfs_io writes all the files of the lower layer, and another one all > the files that are synced before they are modified. > > Those times are from ./check in a KVM guest with 4 CPUs, on a kernel with > KASAN, lockdep, UBSAN and DEBUG_OBJECTS. A round takes 1.8s there, a third > of that is the 40 or so commands that modify the overlay. > > With the fsync=auto rounds gone nothing goes through the code that turns auto > into relaxed. In v3 those rounds never showed anything that the relaxed > rounds didn't. > > This is for "ovl: only sync what was written through the overlay" [1]. It > needs [2]. > > It passes on ext4 and xfs with the series, and with only the first patch of > it, which adds the mount options and still syncs the whole upper file > system. On the four kernels that I broke on purpose for v3 it loses exactly > the same files as v3 in the fsync=relaxed rounds, and nothing in the > fsync=on rounds. > > [1] https://lore.kernel.org/r/20261110-b4-ovl-containerized-sync-v6-0-43d1be16a4d6@toxicpanda.com > [2] https://lore.kernel.org/r/20261106155832.3867017-1-amir73il@gmail.com > > tests/overlay/091 | 204 ++++++++++++++++++++++++++++++++++++++++++ > tests/overlay/091.out | 9 ++ > 2 files changed, 213 insertions(+) > create mode 100755 tests/overlay/091 > create mode 100644 tests/overlay/091.out > > diff --git a/tests/overlay/091 b/tests/overlay/091 > new file mode 100755 > index 00000000..260978e3 > --- /dev/null > +++ b/tests/overlay/091 > @@ -0,0 +1,204 @@ > +#! /bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (c) 2026 Josef Bacik. All Rights Reserved. > +# > +# FS QA Test 091 > +# > +# Test that syncfs(2) on an overlay, and unmounting one, persist everything > +# that was modified through it. > +# > +# overlayfs used to sync the whole upper filesystem for that. With > +# fsync=relaxed it only writes the upper inodes that were modified through this > +# overlay, so it has to know about all of them. > +# > +# Modify the overlay in every way we can think of, sync it, shut down the base > +# filesystem without flushing its log, cycle the mounts and check that nothing > +# has changed. > +# > +. ./common/preamble > +_begin_fstest auto quick shutdown copyup whiteout mmap redirect metacopy > + > +. ./common/attr > + > +_require_scratch > +_require_scratch_shutdown > +_require_metadata_journaling $SCRATCH_DEV > +_require_scratch_overlay_features fsync redirect_dir metacopy > +_require_xfs_io_command "syncfs" > +_require_xfs_io_command "sendfile" > +_require_xfs_io_command "copy_range" > +_require_xfs_io_command "falloc" > +_require_xfs_io_command "-T" > +_require_xfs_io_command "flink" > +_require_attrs > +_require_chown > +_require_mknod > + > +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER > + > +create_lower() > +{ > + # This leaves the base filesystem mounted > + _scratch_mkfs >> $seqres.full 2>&1 > + > + mkdir -p $lowerdir/dir $lowerdir/rmdir $lowerdir/mvdir > + cd $lowerdir > + # xfs_io runs the command on every file > + $XFS_IO_PROG -f -c "pwrite -S 0x6c 0 256k" \ > + append truncate mmap chmod chown chown_append xattr link \ > + rename remove dir/append rmdir/file mvdir/file >> $seqres.full > + cd - > /dev/null > + > + # These were not written through the overlay, so it does not have to > + # sync them. > + $XFS_IO_PROG -c "syncfs" $OVL_BASE_SCRATCH_MNT > +} > + > +# Creating a file and copying it up are modifications. overlayfs remembers them > +# until the file is synced, and a later modification that it misses does not > +# show. So make files that are in the upper layer and have been synced. > +prepare() > +{ > + local m=$SCRATCH_MNT > + > + mkdir $m/clean > + cd $m/clean > + # xfs_io runs the command on every file > + $XFS_IO_PROG -f -c "pwrite -S 0x70 0 256k" \ > + pwrite truncate fallocate mmap sendfile copy_range chmod \ > + chown xattr link rename remove >> $seqres.full > + cd - > /dev/null > + $XFS_IO_PROG -c "syncfs" $m > +} > + > +modify() > +{ > + local m=$SCRATCH_MNT > + > + # Stores through a shared mapping, also after a syncfs, no msync. This > + # has to be the first one, the syncfs must not write anything else. > + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x66 0 64k" \ > + -c "syncfs" -c "mwrite -S 0x67 64k 64k" $m/mmap >> $seqres.full > + > + # The files that overlayfs has nothing to remember about > + $XFS_IO_PROG -c "pwrite -S 0x71 64k 64k" $m/clean/pwrite >> $seqres.full > + $XFS_IO_PROG -c "truncate 4k" $m/clean/truncate > + $XFS_IO_PROG -c "falloc 256k 64k" $m/clean/fallocate > + $XFS_IO_PROG -c "mmap -rw 0 256k" -c "mwrite -S 0x72 64k 64k" \ > + $m/clean/mmap >> $seqres.full > + $XFS_IO_PROG -c "sendfile -i $m/link 0 64k" $m/clean/sendfile \ > + >> $seqres.full > + # Not aligned, so that the data is copied and not shared > + $XFS_IO_PROG -c "copy_range -s 1 -d 1 -l 64k $m/link" \ > + $m/clean/copy_range > + chmod 640 $m/clean/chmod > + chown 1234:1234 $m/clean/chown > + $SETFATTR_PROG -n user.test -v value $m/clean/xattr > + ln $m/clean/link $m/clean/link2 > + mv $m/clean/rename $m/clean/renamed > + rm $m/clean/remove > + > + # New files and directories > + $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1m" $m/new >> $seqres.full > + mkdir $m/newdir > + $XFS_IO_PROG -f -c "pwrite -S 0x62 0 64k" $m/newdir/new >> $seqres.full > + $XFS_IO_PROG -f -c "pwrite -S 0x63 0 64k" $m/dir/new >> $seqres.full > + ln -s new $m/symlink > + # Too long to be stored in the inode > + ln -s $(printf 'a%.0s' $(seq 1000)) $m/longsymlink > + mkfifo $m/fifo > + > + # Copy up, then change the data > + $XFS_IO_PROG -c "pwrite -S 0x64 256k 64k" $m/append >> $seqres.full > + $XFS_IO_PROG -c "pwrite -S 0x65 256k 64k" $m/dir/append >> $seqres.full > + $XFS_IO_PROG -c "truncate 4k" $m/truncate > + > + # The other ways to write data > + $XFS_IO_PROG -f -c "sendfile -i $m/link 0 256k" $m/sendfile \ > + >> $seqres.full > + $XFS_IO_PROG -f -c "copy_range -l 256k $m/link" $m/copy_range > + $XFS_IO_PROG -T -c "pwrite -S 0x69 0 64k" -c "flink $m/tmpfile" $m \ > + >> $seqres.full > + > + # Copy up, then change the metadata > + chmod 640 $m/chmod > + chown 1234:1234 $m/chown > + $SETFATTR_PROG -n user.test -v value $m/xattr > + > + # With metacopy=on the first one only copies up the metadata > + chown 1234:1234 $m/chown_append > + $XFS_IO_PROG -c "pwrite -S 0x6a 256k 64k" $m/chown_append \ > + >> $seqres.full > + > + # Names > + ln $m/link $m/link2 > + mv $m/rename $m/renamed > + # Without redirect_dir=on mv gets EXDEV and copies the directory > + mv $m/mvdir $m/mvdir2 > + rm $m/remove > + rm -r $m/rmdir > +} > + > +# No inode numbers, no timestamps, and no sizes and link counts of directories > +snapshot() > +{ > + cd $SCRATCH_MNT > + find . -type d -printf '%p %y %m %U:%G\n' -o \ > + -printf '%p %y %m %U:%G %n %s %l\n' | LC_ALL=C sort > + find . -type f -print0 | LC_ALL=C sort -z | xargs -0 md5sum > + _getfattr --absolute-names -n user.test xattr clean/xattr 2>&1 > + cd - > /dev/null > +} > + > +# <syncfs|umount> <mount options> > +do_test() > +{ > + local how=$1 > + local opts=$2 > + > + echo "=== $how $opts" | tee -a $seqres.full > + > + create_lower > + > + # An atime update is a modification too. With atime the snapshot would > + # make overlayfs write every file that it reads, also one that > + # overlayfs has lost track of. > + _scratch_mount -o noatime,$opts > + > + prepare > + modify > + snapshot > $tmp.before > + > + # Nobody holds on to the overlay inodes when they are synced > + echo 2 > /proc/sys/vm/drop_caches > + > + if [ $how = syncfs ]; then > + $XFS_IO_PROG -c "syncfs" $SCRATCH_MNT > + _scratch_shutdown | tee -a $seqres.full > + _scratch_unmount > + else > + # Only the overlay, the base filesystem stays mounted > + _unmount $SCRATCH_MNT > + _scratch_shutdown | tee -a $seqres.full > + _unmount $OVL_BASE_SCRATCH_MNT > + fi > + > + _scratch_mount -o noatime,$opts > + snapshot > $tmp.after > + cat $tmp.after >> $seqres.full > + diff -u $tmp.before $tmp.after > + _scratch_unmount > +} > + > +for how in syncfs umount; do > + for mode in relaxed on; do > + for meta in off on; do > + do_test $how \ > + fsync=$mode,redirect_dir=$meta,metacopy=$meta > + done > + done > +done > + > +# success, all done > +status=0 > +exit > diff --git a/tests/overlay/091.out b/tests/overlay/091.out > new file mode 100644 > index 00000000..a8536983 > --- /dev/null > +++ b/tests/overlay/091.out > @@ -0,0 +1,9 @@ > +QA output created by 091 > +=== syncfs fsync=relaxed,redirect_dir=off,metacopy=off > +=== syncfs fsync=relaxed,redirect_dir=on,metacopy=on > +=== syncfs fsync=on,redirect_dir=off,metacopy=off > +=== syncfs fsync=on,redirect_dir=on,metacopy=on > +=== umount fsync=relaxed,redirect_dir=off,metacopy=off > +=== umount fsync=relaxed,redirect_dir=on,metacopy=on > +=== umount fsync=on,redirect_dir=off,metacopy=off > +=== umount fsync=on,redirect_dir=on,metacopy=on > -- > 2.55.0 > ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-11-11 10:36 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-06 15:04 [PATCH fstests] overlay: test that syncfs and unmount persist all changes Josef Bacik 2026-11-07 0:31 ` [PATCH fstests v2] " Josef Bacik 2026-11-07 11:58 ` Amir Goldstein 2026-11-07 12:06 ` Amir Goldstein 2026-11-09 22:52 ` [PATCH fstests v3] " Josef Bacik 2026-11-10 10:42 ` Amir Goldstein 2026-11-10 12:46 ` Zorro Lang 2026-11-10 16:27 ` [PATCH fstests v4] " Josef Bacik 2026-11-11 10:36 ` Amir Goldstein
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox