FS/XFS testing framework
 help / color / mirror / Atom feed
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
> 

      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