[Ocfs2-test-devel] [PATCH 4/8] defrag-test: add an utility to frag fs on purpose.

Tristan Ye tristan.ye at oracle.com
Fri Apr 15 02:01:48 PDT 2011


Actually mark's reservation patches is making the attempt to frag fs no that
esay in a short team, the utility is try to growing-write the fs in small chunks
with massive processes being spawned and interleaved, to simulate the situation
of a long-lived fs. then helps fraging the files/fs in a relatively tolerable time.

Signed-off-by: Tristan Ye <tristan.ye at oracle.com>
---
 programs/Makefile             |    3 +-
 programs/defrag-test/Makefile |   26 +++
 programs/defrag-test/frager.c |  484 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 512 insertions(+), 1 deletions(-)
 create mode 100644 programs/defrag-test/Makefile
 create mode 100755 programs/defrag-test/frager.c

diff --git a/programs/Makefile b/programs/Makefile
index 991802f..fdf2b68 100644
--- a/programs/Makefile
+++ b/programs/Makefile
@@ -58,7 +58,8 @@ SUBDIRS =			\
 	inode_alloc_perf_tests	\
 	reflink_tests		\
 	directio_test		\
-	discontig_bg_test
+	discontig_bg_test	\
+	defrag-test
 
 ifdef OCFS2_TEST_DX_DIRS
 	SUBDIRS += dx_dirs_tests
diff --git a/programs/defrag-test/Makefile b/programs/defrag-test/Makefile
new file mode 100644
index 0000000..cef9252
--- /dev/null
+++ b/programs/defrag-test/Makefile
@@ -0,0 +1,26 @@
+TOPDIR = ../..
+
+include $(TOPDIR)/Preamble.make
+
+TESTS = defrag-test
+
+CFLAGS = -O2 -Wall -g $(OCFS2_CFLAGS)
+
+CFLAGS += $(EXTRA_CFLAGS)
+
+INCLUDES = -I$(TOPDIR)/programs/libocfs2test
+
+CFLAGS += $(INCLUDES)
+
+LIBO2TEST = $(TOPDIR)/programs/libocfs2test/libocfs2test.a
+
+SOURCES = frager.c
+
+DIST_FILES = $(SOURCES)
+
+BIN_PROGRAMS = frager
+
+frager: frager.o
+	$(LINK) $(OCFS2_LIBS) $(LIBO2TEST)
+
+include $(TOPDIR)/Postamble.make
diff --git a/programs/defrag-test/frager.c b/programs/defrag-test/frager.c
new file mode 100755
index 0000000..f510dde
--- /dev/null
+++ b/programs/defrag-test/frager.c
@@ -0,0 +1,484 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * frager.c
+ *
+ * A simple utility to frag the filesystem on purpose.
+ *
+ * boost the disk defragmenation by spawning massive
+ * processes with growing writes, to simulate the interleaving
+ * writes from users over time in a real world.
+ *
+ * Copyright (C) 2011 Oracle.  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 as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ *
+ */
+
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE 500
+#define _LARGEFILE64_SOURCE
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <linux/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <ocfs2/ocfs2.h>
+
+#include "file_verify.h"
+
+#define FILE_FLAGS		(O_CREAT|O_RDWR|O_APPEND)
+
+#define FILE_MODE               (S_IRUSR|S_IWUSR|S_IXUSR|S_IROTH|\
+				 S_IWOTH|S_IXOTH|S_IRGRP|S_IWGRP|S_IXGRP)
+
+char *work_place = NULL, *log_place = NULL;
+char hostname[256];
+unsigned long num_files = 1000;
+unsigned long num_processes = 10;
+unsigned long file_size = 1024 * 1024;
+unsigned long chunk_size = 32 * 1024;
+int is_random = 0;
+int verbose = 0;
+int do_refcount = 0;
+union log_handler w_log;
+
+pid_t *child_pid_list;
+
+static int usage(void)
+{
+	fprintf(stdout, "frager <-n num_files_per_process> <-m num_processes> "
+		"<-l file_size> <-k chunk_size> <-o logfiles_place> <-r> <-v>"
+		" <-w work_place> [-R]\n");
+	fprintf(stdout, "Example:\n"
+			"       ./frager -n 10 -m 10 -l 104857600 -k 32768 -o "
+		"logs -w /storage\n");
+	exit(1);
+}
+
+static void sigchld_handler()
+{
+	pid_t   pid;
+	union wait status;
+
+	while (1) {
+		pid = wait3(&status, WNOHANG, NULL);
+		if (pid <= 0)
+			break;
+	}
+}
+
+static void kill_all_children()
+{
+	unsigned long long i;
+
+	for (i = 0; i < num_processes; i++)
+		if (child_pid_list[i])
+			kill(child_pid_list[i], SIGTERM);
+}
+
+static void sigint_handler()
+{
+	kill_all_children();
+
+	signal(SIGINT, SIG_DFL);
+	kill(getpid(), SIGINT);
+}
+
+static void sigterm_handler()
+{
+	kill_all_children();
+
+	signal(SIGTERM, SIG_DFL);
+	kill(getpid(), SIGTERM);
+}
+
+int parse_opts(int argc, char **argv)
+{
+	char c;
+
+	while (1) {
+		c = getopt(argc, argv, "n:m:w:hk:rRl:vo:");
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'w':
+			work_place = optarg;
+			break;
+		case 'n':
+			num_files = atol(optarg);
+			break;
+		case 'm':
+			num_processes = atol(optarg);
+			break;
+		case 'l':
+			file_size = atol(optarg);
+			break;
+		case 'k':
+			chunk_size = atol(optarg);
+			break;
+		case 'o':
+			log_place = optarg;
+			break;
+		case 'r':
+			is_random = 1;
+			break;
+		case 'R':
+			do_refcount = 1;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case 'h':
+			usage();
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+int setup(int argc, char *argv[])
+{
+	int ret = 0;
+
+	work_place = NULL;
+	is_random = 0;
+
+	if (parse_opts(argc, argv))
+		usage();
+
+	if ((!work_place) || (!log_place)) {
+		fprintf(stderr, "work_place and log_place is a mandatory"
+			" option.\n");
+		usage();
+	}
+
+	if (gethostname(hostname, 256) < 0) {
+		fprintf(stderr, "gethostname failed.\n");
+		exit(1);
+	}
+
+	child_pid_list = (pid_t *)malloc(sizeof(pid_t) * num_processes);
+
+	memset(child_pid_list, 0, sizeof(pid_t) * num_processes);
+
+	/*
+	 * Setup SIGCHLD correctly to avoid zombie processes.
+	 */
+	signal(SIGCHLD, sigchld_handler);
+
+	return ret;
+}
+
+void teardown()
+{
+	if (child_pid_list)
+		free(child_pid_list);
+}
+
+int write_at(int fd, const void *buf, size_t count, off_t offset)
+{
+	int ret;
+
+	size_t bytes_write;
+
+	ret = pwrite(fd, buf, count, offset);
+
+	if (ret < 0) {
+		ret = errno;
+		fprintf(stderr, "write error %d: \"%s\"\n", ret, strerror(ret));
+		return -1;
+	}
+
+	bytes_write = ret;
+	while (bytes_write < count) {
+
+		ret = pwrite(fd, buf + bytes_write, count - bytes_write,
+			     offset + bytes_write);
+
+		if (ret < 0) {
+			ret = errno;
+			fprintf(stderr, "write error %d: \"%s\"\n", ret,
+				strerror(ret));
+			return -1;
+		}
+
+		bytes_write += ret;
+	}
+
+	return 0;
+}
+
+unsigned long get_rand_ul(unsigned long min, unsigned long max)
+{
+	if (min == 0 && max == 0)
+		return 0;
+
+	return min + (rand() % (max - min + 1));
+}
+
+int reflink(const char *oldpath, const char *newpath)
+{
+	int fd, ret;
+	struct reflink_arguments args;
+
+	args.old_path = (__u64)oldpath;
+	args.new_path = (__u64)newpath;
+
+	fd = open64(oldpath, O_RDONLY);
+
+	if (fd < 0) {
+		fd = errno;
+		fprintf(stderr, "open file %s failed:%d:%s\n", oldpath, fd,
+			strerror(fd));
+		return fd;
+	}
+
+	ret = ioctl(fd, OCFS2_IOC_REFLINK, &args);
+
+	if (ret) {
+		ret = errno;
+		fprintf(stderr, "ioctl failed:%d:%s\n", ret, strerror(ret));
+		return ret;
+	}
+
+	close(fd);
+
+	return 0;
+}
+
+int append_write_file(char *file_name, char *logfile_name,
+		      unsigned long file_size, unsigned long chunk_size,
+		      int flags, int verbose)
+{
+	int fd = -1, ret = 0;
+	unsigned long num_chunks, chunk_no, i;
+	unsigned long index, *write_order_map = NULL;
+	char *pattern = NULL;
+	static struct write_unit wu;
+	FILE *logfile = NULL;
+
+	num_chunks = (file_size + chunk_size - 1) / chunk_size;
+
+	pattern = (char *)malloc(chunk_size);
+	write_order_map = (unsigned long *)malloc(num_chunks *
+						  sizeof(unsigned long));
+	memset(write_order_map, 0, num_chunks * sizeof(unsigned long));
+
+	if (!is_random) {
+		for (i = 0; i < num_chunks; i++)
+			write_order_map[i] = i;
+	} else {
+		/*
+		 * in random mode, chunks within file will be filled
+		 * up randomly, it however guarantees all chunks get
+		 * written.
+		 */
+		write_order_map[0] = 0;
+
+		for (i = 1; i < num_chunks; i++) {
+again:
+			index = get_rand_ul(1, num_chunks - 1);
+			if (!write_order_map[index])
+				write_order_map[index] = i;
+			else
+				goto again;
+		}
+	}
+
+	fd = open64(file_name, flags, FILE_MODE);
+	if (fd < 0) {
+		ret = errno;
+		fprintf(stderr, "create file %s failed:%d:%s\n", file_name, ret,
+			strerror(ret));
+		ret = fd;
+		goto bail;
+	}
+
+	ret = open_logfile(&logfile, logfile_name, 0);
+	if (ret)
+		goto bail;
+
+	w_log.stream_log = logfile;
+
+	for (i = 0; i < num_chunks; i++) {		
+
+		chunk_no = write_order_map[i];
+		prep_rand_dest_write_unit(&wu, chunk_no, chunk_size);
+		/*
+		 * each chunk is written in a unique pattern, which helps
+		 * making the following verfication more accurate and easier.
+		 */
+		fill_chunk_pattern(pattern, &wu);
+
+		if (verbose)
+			fprintf(stdout, "Process %d writing #%lu chunk to "
+				"file %s\n", getpid(), wu.wu_chunk_no, file_name);
+
+		ret = do_write_chunk(fd, wu);
+		if (ret < 0)
+			goto bail;
+
+		/*
+		 * writes the log records into local logfile.
+		 */
+		ret = log_write(&wu, w_log, 0);
+		if (ret < 0)
+			goto bail;
+	}
+
+bail:
+	if (pattern)
+		free(pattern);
+
+	if (write_order_map)
+		free(write_order_map);
+
+	if (fd > 0)
+		close(fd);
+
+	if (w_log.stream_log)
+		fclose(w_log.stream_log);
+
+	return ret;
+}
+
+int write_files(unsigned long long files,
+		 unsigned long long processes,
+		 const char *dir, const char *log_dir)
+{
+	int status = 0, ret = 0;
+	pid_t pid;
+	unsigned int seed;
+	unsigned long long i, j;
+	char path[PATH_MAX], log_path[PATH_MAX], ref_path[PATH_MAX];
+
+	for (i = 0; i < processes; i++) {
+
+		pid = fork();
+		if (pid < 0) {
+			fprintf(stderr, "fork error:%s\n", strerror(pid));
+			teardown();
+			exit(pid);
+		}
+
+		if (pid == 0) {
+
+			if (is_random) {
+				seed = time(NULL) ^ getpid();
+				srandom(seed);
+			}
+
+			snprintf(path, PATH_MAX, "%s/%s-%d", dir, hostname,
+				 getpid());
+
+			snprintf(log_path, PATH_MAX, "%s/%s-%d",
+				 log_dir, hostname, getpid());
+
+			ret = mkdir(path, FILE_MODE);
+			if (ret < 0) {
+				fprintf(stderr, "%d failed to mkdir %s: %s\n",
+					getpid(), path, strerror(errno));
+				teardown();
+				exit(ret);
+			}
+
+			ret = mkdir(log_path, FILE_MODE);
+			if (ret < 0) {
+				fprintf(stderr, "%d failed to mkdir(log) %s: "
+					"%s\n", getpid(), log_path, strerror(errno));
+				teardown();
+				exit(ret);
+			}
+
+			for (j = 0; j < files; j++) {
+				snprintf(path, PATH_MAX, "%s/%s-%d/file-%llu",
+					 dir, hostname, getpid(), j);
+				snprintf(log_path, PATH_MAX, "%s/%s-%d/logfile-%llu",
+					 log_dir, hostname, getpid(), j);
+
+				ret = append_write_file(path, log_path,
+							file_size, chunk_size,
+							FILE_FLAGS, verbose);
+				if (ret < 0) {
+					teardown();
+					exit(ret);
+				}
+
+				if (do_refcount) {
+					snprintf(ref_path, PATH_MAX, "%s/%s-%d/refile-%llu",
+						 dir, hostname, getpid(), j);
+					ret = reflink(path, ref_path);
+					if (ret < 0) {
+						teardown();
+						exit(ret);
+					}
+				}
+			}
+
+			teardown();
+			exit(0);
+		}
+
+		if (pid > 0)
+			child_pid_list[i] = pid;
+	}
+
+	/*
+	 * We're only going to setup following signals
+	 * for father to avoid zombies and cleaup children.
+	 */
+	signal(SIGINT, sigint_handler);
+	signal(SIGTERM, sigterm_handler);
+
+	for (i = 0; i < num_processes; i++)
+		ret = waitpid(child_pid_list[i], &status, 0);
+
+	return ret;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret = 0;
+
+	ret = setup(argc, argv);
+	if (ret)
+		return ret;
+
+	ret = write_files(num_files, num_processes, work_place, log_place);
+
+	teardown();
+
+	return ret;
+}
-- 
1.6.5.2




More information about the Ocfs2-test-devel mailing list