From acb94dd9b78b7bb68b4d244589d30ad9004486c8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 15 Jul 2022 10:20:03 -0700 Subject: [PATCH] Add test of large fragmented free lists Add a test which gives the server a transaction with a free list block that contains blknos that each dirty an individiaul btree blocks in the global data free extent btree. Signed-off-by: Zach Brown --- tests/Makefile | 3 +- tests/golden/large-fragmented-free | 3 + tests/sequence | 1 + tests/src/fragmented_data_extents.c | 113 +++++++++++++++++++++++++++ tests/tests/large-fragmented-free.sh | 22 ++++++ 5 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 tests/golden/large-fragmented-free create mode 100644 tests/src/fragmented_data_extents.c create mode 100644 tests/tests/large-fragmented-free.sh diff --git a/tests/Makefile b/tests/Makefile index 60eea516..fcc43df1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,7 +10,8 @@ BIN := src/createmany \ src/bulk_create_paths \ src/stage_tmpfile \ src/find_xattrs \ - src/create_xattr_loop + src/create_xattr_loop \ + src/fragmented_data_extents DEPS := $(wildcard src/*.d) diff --git a/tests/golden/large-fragmented-free b/tests/golden/large-fragmented-free new file mode 100644 index 00000000..58aea5f7 --- /dev/null +++ b/tests/golden/large-fragmented-free @@ -0,0 +1,3 @@ +== creating fragmented extents +== unlink file with moved extents to free extents per block +== cleanup diff --git a/tests/sequence b/tests/sequence index 37f30647..db6e9ba0 100644 --- a/tests/sequence +++ b/tests/sequence @@ -9,6 +9,7 @@ fallocate.sh setattr_more.sh offline-extent-waiting.sh move-blocks.sh +large-fragmented-free.sh enospc.sh srch-basic-functionality.sh simple-xattr-unit.sh diff --git a/tests/src/fragmented_data_extents.c b/tests/src/fragmented_data_extents.c new file mode 100644 index 00000000..d3fcc877 --- /dev/null +++ b/tests/src/fragmented_data_extents.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2021 Versity Software, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* + * This creates fragmented data extents. + * + * A file is created that has alternating free and allocated extents. + * This also results in the global allocator having the matching + * fragmented free extent pattern. While that file is being created, + * occasionally an allocated extent is moved to another file. This + * results in a file that has fragmented extents at a given stride that + * can be deleted to create free data extents with a given stride. + * + * We don't have hole punching so to do this quickly we use a goofy + * combination of fallocate, truncate, and our move_blocks ioctl. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ioctl.h" + +#define BLOCK_SIZE 4096 + +int main(int argc, char **argv) +{ + struct scoutfs_ioctl_move_blocks mb = {0,}; + unsigned long long freed_extents; + unsigned long long move_stride; + unsigned long long i; + int alloc_fd; + int trunc_fd; + off_t off; + int ret; + + if (argc != 5) { + printf("%s \n", argv[0]); + return 1; + } + + freed_extents = strtoull(argv[1], NULL, 0); + move_stride = strtoull(argv[2], NULL, 0); + + alloc_fd = open(argv[3], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (alloc_fd == -1) { + fprintf(stderr, "error opening %s: %d (%s)\n", argv[3], errno, strerror(errno)); + exit(1); + } + + trunc_fd = open(argv[4], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (trunc_fd == -1) { + fprintf(stderr, "error opening %s: %d (%s)\n", argv[4], errno, strerror(errno)); + exit(1); + } + + for (i = 0, off = 0; i < freed_extents; i++, off += BLOCK_SIZE * 2) { + + ret = fallocate(alloc_fd, 0, off, BLOCK_SIZE * 2); + if (ret < 0) { + fprintf(stderr, "fallocate at off %llu error: %d (%s)\n", + (unsigned long long)off, errno, strerror(errno)); + exit(1); + } + + ret = ftruncate(alloc_fd, off + BLOCK_SIZE); + if (ret < 0) { + fprintf(stderr, "truncate to off %llu error: %d (%s)\n", + (unsigned long long)off + BLOCK_SIZE, errno, strerror(errno)); + exit(1); + } + + if ((i % move_stride) == 0) { + mb.from_fd = alloc_fd; + mb.from_off = off; + mb.len = BLOCK_SIZE; + mb.to_off = i * BLOCK_SIZE; + + ret = ioctl(trunc_fd, SCOUTFS_IOC_MOVE_BLOCKS, &mb); + if (ret < 0) { + fprintf(stderr, "move from off %llu error: %d (%s)\n", + (unsigned long long)off, + errno, strerror(errno)); + } + } + } + + if (alloc_fd > -1) + close(alloc_fd); + if (trunc_fd > -1) + close(trunc_fd); + + return 0; +} diff --git a/tests/tests/large-fragmented-free.sh b/tests/tests/large-fragmented-free.sh new file mode 100644 index 00000000..e27d1bbe --- /dev/null +++ b/tests/tests/large-fragmented-free.sh @@ -0,0 +1,22 @@ +# +# Make sure the server can handle a transaction with a data_freed whose +# blocks all hit different btree blocks in the main free list. It +# probably has to be merged in multiple commits. +# + +t_require_commands fragmented_data_extents + +EXTENTS_PER_BTREE_BLOCK=600 +EXTENTS_PER_LIST_BLOCK=8192 +FREED_EXTENTS=$((EXTENTS_PER_BTREE_BLOCK * EXTENTS_PER_LIST_BLOCK)) + +echo "== creating fragmented extents" +fragmented_data_extents $FREED_EXTENTS $EXTENTS_PER_BTREE_BLOCK "$T_D0/alloc" "$T_D0/move" + +echo "== unlink file with moved extents to free extents per block" +rm -f "$T_D0/move" + +echo "== cleanup" +rm -f "$T_D0/alloc" + +t_pass