From: Zorro Lang <zlang@kernel.org>
To: Aditya Srivastava <aditya.ansh182@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>,
"Darrick J . Wong" <djwong@kernel.org>,
fstests@vger.kernel.org, linux-xfs@vger.kernel.org
Subject: Re: [PATCH] xfs/842: test close() deadlocks on frozen filesystems
Date: Mon, 15 Jun 2026 02:52:57 +0800 [thread overview]
Message-ID: <ai7oBqnd2dy32Ew9@zlang-mailbox> (raw)
In-Reply-To: <20260612123742.2389-1-aditya.ansh182@gmail.com>
On Fri, Jun 12, 2026 at 12:37:42PM +0000, Aditya Srivastava wrote:
> From: Aditya Prakash Srivastava <aditya.ansh182@gmail.com>
>
> When a file with active speculative post-EOF preallocations is closed,
> XFS synchronously attempts to free them via xfs_free_eofblocks().
> This requires allocating a write transaction, which blocks indefinitely
> if the filesystem is frozen, causing close() to hang.
>
> Add a regression test using a helper C program to verify that close()
> returns cleanly (with -EAGAIN if using XFS_TRANS_WRITECOUNT_TRYLOCK)
> without blocking the close system call.
>
> This is a regression test for the corresponding kernel patch:
> "xfs: prevent close() from hanging on frozen filesystems"
>
> On a successfully patched kernel, the close system call returns cleanly
> and the test helper outputs 'close() completed immediately!', matching
> the golden output. On an unpatched kernel, the close system call blocks,
> the helper times out and outputs 'close() hung under freeze!', cleanly
> flagging the deadlock to fstests.
>
> Link: https://bugzilla.kernel.org/show_bug.cgi?id=205833
> Link: https://bugzilla.redhat.com/show_bug.cgi?id=1474726
> Signed-off-by: Aditya Prakash Srivastava <aditya.ansh182@gmail.com>
> ---
> src/Makefile | 1 +
> src/xfs_freeze_close_repro.c | 127 +++++++++++++++++++++++++++++++++++
> tests/xfs/842 | 32 +++++++++
> tests/xfs/842.out | 2 +
> 4 files changed, 162 insertions(+)
> create mode 100644 src/xfs_freeze_close_repro.c
> create mode 100755 tests/xfs/842
> create mode 100644 tests/xfs/842.out
>
> diff --git a/src/Makefile b/src/Makefile
> index 31ac43b2..9553e7c9 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -68,6 +68,7 @@ LCFLAGS += -DHAVE_FILE_GETATTR
> endif
>
> ifeq ($(PKG_PLATFORM),linux)
> +LINUX_TARGETS += xfs_freeze_close_repro
Please add xfs_freeze_close_repro into above/original "LINUX_TARGETS = ..."
directly.
> TARGETS += $(LINUX_TARGETS)
> endif
>
> diff --git a/src/xfs_freeze_close_repro.c b/src/xfs_freeze_close_repro.c
> new file mode 100644
> index 00000000..075f2d18
> --- /dev/null
> +++ b/src/xfs_freeze_close_repro.c
> @@ -0,0 +1,127 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2026 Aditya Prakash Srivastava. All Rights Reserved.
> + *
> + * xfstests helper to reproduce close() hang on frozen XFS filesystems.
> + */
> +#define _GNU_SOURCE
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <pthread.h>
> +#include <sys/ioctl.h>
> +#include <sys/vfs.h>
> +#include <linux/fs.h>
> +#include <libgen.h>
> +
> +int close_started;
> +int close_completed;
volatile ?
> +
> +void *
> +close_thread(void *arg)
> +{
> + int fd = *(int *)arg;
> +
> + close_started = 1;
> + close(fd);
> + close_completed = 1;
> + return NULL;
> +}
> +
> +int
> +main(int argc, char *argv[])
> +{
> + struct statfs sfs;
> + char *dir_buf;
> + char *parent_dir;
> + int freeze_fd;
> + int write_fd;
> + char buf[65536];
> + pthread_t thread;
> + int hung = 1;
> + int i;
> +
> + if (argc < 2) {
> + fprintf(stderr, "Usage: %s <file_on_xfs>\n", argv[0]);
> + return 1;
> + }
> +
> + if (statfs(argv[1], &sfs) < 0) {
> + dir_buf = strdup(argv[1]);
> + parent_dir = dirname(dir_buf);
> + if (statfs(parent_dir, &sfs) < 0) {
> + perror("statfs");
> + free(dir_buf);
> + return 1;
> + }
> + free(dir_buf);
> + }
> + if (sfs.f_type != 0x58465342) {
> + fprintf(stderr, "Not on XFS\n");
> + return 1;
> + }
Why limit this program run on XFS? There're not XFS specific test steps.
> +
> + dir_buf = strdup(argv[1]);
> + freeze_fd = open(dirname(dir_buf), O_RDONLY);
> + if (freeze_fd < 0) {
> + perror("open dir");
> + free(dir_buf);
> + return 1;
> + }
> + free(dir_buf);
> +
> + write_fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
> + if (write_fd < 0) {
> + perror("open file");
> + close(freeze_fd);
> + return 1;
> + }
> +
> + memset(buf, 'A', sizeof(buf));
> + for (i = 0; i < 320; i++) {
> + if (write(write_fd, buf, sizeof(buf)) < 0) {
> + perror("write");
> + close(write_fd);
> + close(freeze_fd);
> + return 1;
goto out_err;
> + }
> + }
> +
> + if (ioctl(freeze_fd, FIFREEZE, 0) < 0) {
> + perror("ioctl FIFREEZE");
> + close(write_fd);
> + close(freeze_fd);
> + return 1;
goto out_err;
> + }
frozen = 1;
> +
> + if (pthread_create(&thread, NULL, close_thread, &write_fd) != 0) {
> + perror("pthread_create");
> + ioctl(freeze_fd, FITHAW, 0);
> + close(write_fd);
> + close(freeze_fd);
> + return 1;
goto out_err;
> + }
> +
> + while (!close_started)
> + usleep(1000);
> +
> + for (i = 0; i < 30; i++) {
I'm wondering if "3 seconds" is enough to make sure "it's hang" absolutely :)
> + usleep(100000);
> + if (close_completed) {
> + hung = 0;
> + break;
> + }
> + }
> +
> + if (hung)
> + printf("close() hung under freeze!\n");
> + else
> + printf("close() completed immediately!\n");
> +
> + ioctl(freeze_fd, FITHAW, 0);
frozen = 0;
> + pthread_join(thread, NULL);
> + close(freeze_fd);
> + return hung ? 0 : 1;
out_err:
if (write_fd >= 0)
close(write_fd);
if (frozen)
ioctl(freeze_fd, FITHAW, 0);
if (freeze_fd >= 0)
close(freeze_fd);
return 1;
> +}
> diff --git a/tests/xfs/842 b/tests/xfs/842
> new file mode 100755
> index 00000000..8b225ea2
> --- /dev/null
> +++ b/tests/xfs/842
> @@ -0,0 +1,32 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2026 Aditya Prakash Srivastava. All Rights Reserved.
> +#
> +# FS QA Test No. 842
> +#
> +# Verify that closing a file with active speculative post-EOF preallocations
> +# on a frozen XFS filesystem does not deadlock inside close().
> +#
> +. ./common/preamble
> +_begin_fstest auto quick freeze
> +
> +# Import helper functions
> +. ./common/rc
> +. ./common/filter
^^^^^^
It's useless.
> +
> +# Real-world configs
> +_supported_fs xfs
There's not "_supported_fs" helper anymore.
And I don't think this case is a xfs specific test case. I think it can be
run on all filesystems which supports fsfreeze --
_require_freeze
> +_require_scratch
> +_require_test_program "xfs_freeze_close_repro"
> +
> +_scratch_mkfs >> $seqres.full 2>&1
> +_scratch_mount
> +
> +TEST_FILE=$SCRATCH_MNT/test_eofblocks_freeze
> +
> +# Execute the reproducer helper on scratch mount
> +$here/src/xfs_freeze_close_repro $TEST_FILE
> +
> +status=$?
The output of xfs_freeze_close_repro will break the .out, don't need to
set status=$? at here. If you want, you can call _fail ... after the
xfs_freeze_close_repro returns non-zero.
> +_scratch_unmount
"unmount" is not necessary at here. But I think you need a specific _cleanup()
function for this case. Due to your C program does fs freeze. If it's killed
by something suddently, we must make the fs is freezed before running next
test case.
_cleanup()
{
# Make sure $SCRATCH_MNT is unfreezed
xfs_freeze -u $SCRATCH_MNT 2>/dev/null
....
....
}
> +_exit
_exit 0
Since to your kernel patch is still under review, let's see how it goes at first
Thanks,
Zorro
> diff --git a/tests/xfs/842.out b/tests/xfs/842.out
> new file mode 100644
> index 00000000..386fb9cc
> --- /dev/null
> +++ b/tests/xfs/842.out
> @@ -0,0 +1,2 @@
> +QA output created by 842
> +close() completed immediately!
> --
> 2.47.3
>
prev parent reply other threads:[~2026-06-14 18:53 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-12 12:37 [PATCH] xfs/842: test close() deadlocks on frozen filesystems Aditya Srivastava
2026-06-14 18:52 ` Zorro Lang [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ai7oBqnd2dy32Ew9@zlang-mailbox \
--to=zlang@kernel.org \
--cc=aditya.ansh182@gmail.com \
--cc=djwong@kernel.org \
--cc=fstests@vger.kernel.org \
--cc=hch@infradead.org \
--cc=linux-xfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox