Skip to content

Commit

Permalink
generic: add a regression test for fiemap into an mmap range
Browse files Browse the repository at this point in the history
Btrfs had a deadlock that you could trigger by mmap'ing a large file and
using that as the buffer for fiemap.  This test adds a c program to do
this, and the fstest creates a large enough file and then runs the
reproducer on the file.  Without the fix btrfs deadlocks, with the fix
we pass fine.

Signed-off-by: Josef Bacik <[email protected]>
Reviewed-by: Anand Jain <[email protected]>
Reviewed-by: Filipe Manana <[email protected]>
Reviewed-by: "Darrick J. Wong" <[email protected]>
Reviewed-by: Zorro Lang <[email protected]>
Signed-off-by: Zorro Lang <[email protected]>
  • Loading branch information
josefbacik authored and Zorro Lang committed Mar 30, 2024
1 parent 9e760cf commit 34cdaf0
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ tags
/src/vfs/mount-idmapped
/src/log-writes/replay-log
/src/perf/*.pyc
/src/fiemap-fault

# Symlinked files
/tests/generic/035.out
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
fscrypt-crypt-util bulkstat_null_ocount splice-test chprojid_fail \
detached_mounts_propagation ext4_resize t_readdir_3 splice2pipe \
uuid_ioctl t_snapshot_deleted_subvolume
uuid_ioctl t_snapshot_deleted_subvolume fiemap-fault

EXTRA_EXECS = dmerror fill2attr fill2fs fill2fs_check scaleread.sh \
btrfs_crc32c_forged_name.py popdir.pl popattr.py \
Expand Down
74 changes: 74 additions & 0 deletions src/fiemap-fault.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2024 Meta Platforms, Inc. All Rights Reserved.
*/

#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/fiemap.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int prep_mmap_buffer(int fd, void **addr)
{
struct stat st;
int ret;

ret = fstat(fd, &st);
if (ret)
err(1, "failed to stat %d", fd);

*addr = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (*addr == MAP_FAILED)
err(1, "failed to mmap %d", fd);

return st.st_size;
}

int main(int argc, char *argv[])
{
struct fiemap *fiemap;
size_t sz, last = 0;
void *buf = NULL;
int ret, fd;

if (argc != 2)
errx(1, "no in and out file name arguments given");

fd = open(argv[1], O_RDWR, 0666);
if (fd == -1)
err(1, "failed to open %s", argv[1]);

sz = prep_mmap_buffer(fd, &buf);

fiemap = (struct fiemap *)buf;
fiemap->fm_flags = 0;
fiemap->fm_extent_count = (sz - sizeof(struct fiemap)) /
sizeof(struct fiemap_extent);

while (last < sz) {
int i;

fiemap->fm_start = last;
fiemap->fm_length = sz - last;

ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
if (ret < 0)
err(1, "fiemap failed %d", errno);
for (i = 0; i < fiemap->fm_mapped_extents; i++)
last = fiemap->fm_extents[i].fe_logical +
fiemap->fm_extents[i].fe_length;
}

munmap(buf, sz);
close(fd);
return 0;
}
50 changes: 50 additions & 0 deletions tests/generic/742
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2024 Meta Platforms, Inc. All Rights Reserved.
#
# FS QA Test No. 742
#
# Test fiemap into an mmaped buffer of the same file
#
# Create a reasonably large file, then run a program which mmaps it and uses
# that as a buffer for an fiemap call. This is a regression test for btrfs
# where we used to hold a lock for the duration of the fiemap call which would
# result in a deadlock if we page faulted.
#
. ./common/preamble
_begin_fstest quick auto fiemap
[ $FSTYP == "btrfs" ] && \
_fixed_by_kernel_commit b0ad381fa769 \
"btrfs: fix deadlock with fiemap and extent locking"

_cleanup()
{
rm -f $dst
cd /
rm -r -f $tmp.*
}

# real QA test starts here
_supported_fs generic
_require_test
_require_test_program "fiemap-fault"
_require_test_program "punch-alternating"
_require_xfs_io_command "fpunch"

dst=$TEST_DIR/$seq/fiemap-fault

mkdir -p $TEST_DIR/$seq

echo "Silence is golden"

# Generate a file with lots of extents
blksz=$(_get_file_block_size $TEST_DIR)
$XFS_IO_PROG -f -c "pwrite -q 0 $((blksz * 10000))" $dst
$here/src/punch-alternating $dst

# Now run the reproducer
$here/src/fiemap-fault $dst

# success, all done
status=$?
exit
2 changes: 2 additions & 0 deletions tests/generic/742.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
QA output created by 742
Silence is golden

0 comments on commit 34cdaf0

Please sign in to comment.