[Ocfs2-tools-devel] [PATCH 2/2] Test for the io_cache

Joel Becker Joel.Becker at oracle.com
Tue Mar 27 08:56:36 PDT 2007


On Mon, Mar 26, 2007 at 09:25:25PM -0700, Joel Becker wrote:
> This patch introduces a test program for the libocfs2 io_cache.  The
> program takes 12 steps to prove that the caching is functional.
> 
> Please see the top of the test program for the steps.  I'm looking for
> evaluation of the steps.  If there is anything else I can add or
> re-order, I'm all about suggestions.

	This is a new version of the patch that adds tests for
multiblock reads and writes.
 
Sunil, Marcos, Tao, I'm looking for signoff.

Index: programs/iocache/iocache.c
===================================================================
--- programs/iocache/iocache.c	(.../trunk)	(revision 0)
+++ programs/iocache/iocache.c	(.../branches/iocache)	(revision 87)
@@ -0,0 +1,659 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * iocache.c - Test libocfs2 io cache.
+ *
+ * The program is simple.
+ *
+ * 1) Read the original blocks off.
+ * 2) Initialize the cache.
+ * 3) Read the blocks again, compare against the originals.
+ * 4) Read again, expecting the cache to return them.  Compare.
+ * 5) Write a pattern to the blocks.
+ * 6) Read them again, expecting the pattern.
+ * 7) Start a child process.  It drops and recreates its cache.
+ * 8)           The child reads the blocks, expecting the pattern.
+ * 9)           The child writes a new pattern to the blocks.
+ * 10) The parent reads the blocks again.  Because it has a cache, it
+ *     should still see the old pattern.
+ * 11) Drop and recreate the cache.  Read the blocks again.  It should see
+ *     the new pattern.
+ * 12) To test multiblock read and write, write an alternating pattern to
+ *     blknos[0].
+ * 13) Read the blocks via a multiblock read.  It should see the
+ *     alternating pattern.
+ * 14) Drop and recreate the cache.  Read the blocks via a multiblock read.
+ *     It should see the alternating pattern.
+ * 15) Write the original blocks back.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/wait.h>
+
+#include <ocfs2/ocfs2.h>
+
+#define TRACE 1
+#ifdef TRACE
+# define _called()      fprintf(stderr, "TRACE: %s called\n", __func__)
+# define _here()        fprintf(stderr, "TRACE: %s line %d\n", __func__, __LINE__)
+# define _done()        fprintf(stderr, "TRACE: %s done\n", __func__)
+#else
+# define _called()      do { } while (0)
+# define _here()        do { } while (0)
+# define _done()        do { } while (0)
+#endif
+
+#define try_and_fail(_failcount, _call, _label) do {			\
+		fprintf(stdout, "Testing %s ... ", #_call);		\
+		fflush(stdout);						\
+		if (_call()) {						\
+			(_failcount)++;					\
+			fprintf(stdout, "FAIL\n");			\
+			goto _label;					\
+		} else							\
+			fprintf(stdout, "PASS\n");			\
+	} while (0)
+#define try_and_continue(_failcount, _call) do {			\
+		fprintf(stdout, "Testing %s ... ", #_call);		\
+		fflush(stdout);						\
+		if (_call()) {						\
+			(_failcount)++;					\
+			fprintf(stdout, "FAIL\n");			\
+		} else							\
+			fprintf(stdout, "PASS\n");			\
+	} while (0)
+
+
+#define NUM_BLOCKS 10
+#define BLOCKSIZE 4096
+#define PATTERN0 'a'
+#define PATTERN1 'b'
+#define POISON 'p'
+
+char *progname;
+static int number_of_blocks = NUM_BLOCKS;
+static uint64_t blknos[NUM_BLOCKS];
+static int blocksize = BLOCKSIZE;
+static char *original_blocks[NUM_BLOCKS];
+static char *pattern_blocks[NUM_BLOCKS];
+static char *working_blocks[NUM_BLOCKS];
+static io_channel *channel;
+static int childp;
+
+
+static void usage(int rc)
+{
+	fprintf(stderr, "Usage: %s <filename>\n", progname);
+	exit(rc);
+}
+
+static int open_device(const char *dev)
+{
+	errcode_t ret;
+
+	ret = io_open(dev, OCFS2_FLAG_RW, &channel);
+	if (ret) {
+		com_err(progname, ret, "while opening file \"%s\"", dev);
+		return -ENXIO;
+	}
+
+	io_set_blksize(channel, blocksize);
+
+	return 0;
+}
+
+static void finalize(void);
+static int initialize(int argc, char *argv[])
+{
+	int rc, i;
+	errcode_t ret = 0;
+	progname = argv[0];
+	char *original_buffer, *pattern_buffer, *working_buffer;
+
+	if (argc != 2)
+		usage(-EINVAL);
+	if (!strcmp(argv[1], "-h") ||
+	    !strcmp(argv[1], "-?") ||
+	    !strcmp(argv[1], "--help"))
+		usage(0);
+	if (argv[1][0] == '-')
+		usage(-EINVAL);
+
+	rc = open_device(argv[1]);
+	if (rc)
+		goto out;
+
+	ret = ocfs2_malloc_blocks(channel, number_of_blocks,
+				  &original_buffer);
+	if (ret)
+		goto out;
+	ret = ocfs2_malloc_blocks(channel, number_of_blocks,
+				  &pattern_buffer);
+	if (ret)
+		goto out;
+	ret = ocfs2_malloc_blocks(channel, number_of_blocks,
+				  &working_buffer);
+	if (ret)
+		goto out;
+	/* open_device() set our blocksize */
+	for (i = 0; i < number_of_blocks; i++) {
+		original_blocks[i] = original_buffer;
+		pattern_blocks[i] = pattern_buffer;
+		working_blocks[i] = working_buffer;
+
+		original_buffer += io_get_blksize(channel);
+		pattern_buffer += io_get_blksize(channel);
+		working_buffer += io_get_blksize(channel);
+	}
+
+	/*
+	 * XXX: Make this smarter :-)
+	 * This loop just takes the number_of_blocks blocks starting at
+	 * block 20.  It really should be randomly picking blocks all
+	 * over the disk.
+	 */
+	for (i = 0; i < number_of_blocks; i++)
+		blknos[i] = i + 20;
+
+out:
+	if (ret) {
+		com_err(progname, ret, "while initializing the test");
+		rc = -ENOMEM;
+
+		finalize();
+	}
+
+	return rc;
+}
+
+static void fill_pattern(int even_pat, int odd_pat)
+{
+	int i, pat;
+
+	for (i = 0; i < number_of_blocks; i++) {
+		pat = (i % 2) ? even_pat : odd_pat;
+		memset(pattern_blocks[i], pat, io_get_blksize(channel));
+	}
+}
+
+/* Because we're comparing, we want to avoid seeing stale data. */
+static void poison_working_blocks(void)
+{
+	int i;
+
+	for (i = 0; i < number_of_blocks; i++)
+		memset(working_blocks[i], POISON, io_get_blksize(channel));
+}
+
+
+static int step1(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_read_block(channel, blknos[i], 1, 
+				    original_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while reading block %"PRIu64, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step2(void)
+{
+	int failed = 0;
+	errcode_t ret;
+
+	ret = io_init_cache(channel, number_of_blocks);
+	if (ret) {
+		com_err(progname, ret, "while initializing cache");
+		failed = 1;
+	}
+
+	return failed;
+}
+
+static int step3(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	poison_working_blocks();
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_read_block(channel, blknos[i], 1, 
+				    working_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while reading block %"PRIu64, blknos[i]);
+		}
+	}
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		if (memcmp(original_blocks[i], working_blocks[i],
+			   io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Cache filling read of block %"PRIu64" doesn't match original\n",
+				progname, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step4(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	poison_working_blocks();
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_read_block(channel, blknos[i], 1, 
+				    working_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while reading cached block %"PRIu64, blknos[i]);
+		}
+	}
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		if (memcmp(original_blocks[i], working_blocks[i],
+			   io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Cached read of block %"PRIu64" doesn't match original\n",
+				progname, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step5(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	fill_pattern(PATTERN0, PATTERN0);
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_write_block(channel, blknos[i], 1,
+				     pattern_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while writing pattern 0 to block %"PRIu64,
+				blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step6(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	poison_working_blocks();
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_read_block(channel, blknos[i], 1, 
+				    working_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while reading cached block %"PRIu64, blknos[i]);
+		}
+	}
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		if (memcmp(pattern_blocks[i], working_blocks[i],
+			   io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Cached read of block %"PRIu64" doesn't match pattern 0\n",
+				progname, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+
+static int step7(void)
+{
+	int tries = 5;
+	pid_t pid, wpid;
+	int status;
+	int failed = 0;
+
+	do {
+		pid = fork();
+		tries--;
+	} while (tries && (pid < 0));
+
+	if (pid < 0) {
+		fprintf(stderr, "%s: Unable to create child process: %s\n",
+			progname, strerror(errno));
+		return 1;
+	}
+
+	if (!pid) {
+		childp = 1;
+		
+		/* Erase the child's cache */
+		io_destroy_cache(channel);
+		io_init_cache(channel, 10);
+		return 0;
+	}
+
+	while (1) {
+		wpid = waitpid(pid, &status, 0);
+		if (wpid == pid)
+			break;
+		if (errno == EINTR)
+			continue;
+		fprintf(stderr, "%s: Unable to wait on child process: %s\n",
+			progname, strerror(errno));
+		failed = 1;
+	}
+
+	fprintf(stderr, "Parent  step7 ... ");
+	fflush(stdout);
+
+	return failed;
+}
+
+static int step8(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	poison_working_blocks();
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_read_block(channel, blknos[i], 1, 
+				    working_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while child reading block %"PRIu64, blknos[i]);
+		}
+	}
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		if (memcmp(pattern_blocks[i], working_blocks[i],
+			   io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Child's read of block %"PRIu64" doesn't match pattern 0\n",
+				progname, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step9(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	fill_pattern(PATTERN1, PATTERN1);
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_write_block(channel, blknos[i], 1,
+				     pattern_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while child writing pattern 1 to block %"PRIu64,
+				blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step10(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	poison_working_blocks();
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_read_block(channel, blknos[i], 1, 
+				    working_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while re-reading cached block %"PRIu64, blknos[i]);
+		}
+	}
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		if (memcmp(pattern_blocks[i], working_blocks[i],
+			   io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Cached re-read of block %"PRIu64" doesn't match pattern 0\n",
+				progname, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step11(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	io_destroy_cache(channel);
+	io_init_cache(channel, 10);
+
+	poison_working_blocks();
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		ret = io_read_block(channel, blknos[i], 1, 
+				    working_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while re-reading cached block %"PRIu64, blknos[i]);
+		}
+	}
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		/* Note, this memcmp is reversed from step 10 */
+		if (!memcmp(pattern_blocks[i], working_blocks[i],
+			    io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Re-read of block %"PRIu64" incorrectly matches pattern 0\n",
+				progname, blknos[i]);
+		}
+	}
+
+	/* Bring the parent's pattern to PATTERN1 */
+	fill_pattern(PATTERN1, PATTERN1);
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		if (memcmp(pattern_blocks[i], working_blocks[i],
+			   io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Re-read of block %"PRIu64" doesn't match pattern 1\n",
+				progname, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step12(void)
+{
+	int failed = 0;
+	errcode_t ret;
+
+	fill_pattern(PATTERN0, PATTERN1);
+
+	ret = io_write_block(channel, blknos[0], number_of_blocks,
+			     pattern_blocks[0]);
+	if (ret) {
+		failed = 1;
+		com_err(progname, ret,
+			"while writing alternating pattern to block %"PRIu64,
+			blknos[0]);
+	}
+
+	return failed;
+}
+
+static int step13(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	poison_working_blocks();
+	ret = io_read_block(channel, blknos[0], number_of_blocks, 
+			    working_blocks[0]);
+	if (ret) {
+		failed = 1;
+		com_err(progname, ret,
+			"while doing multiblock read of block %"PRIu64,
+			blknos[0]);
+	}
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		if (memcmp(pattern_blocks[i], working_blocks[i],
+			   io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Cached re-read of block %"PRIu64" doesn't match alternating pattern\n",
+				progname, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step14(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	io_destroy_cache(channel);
+	io_init_cache(channel, number_of_blocks);
+
+	poison_working_blocks();
+	ret = io_read_block(channel, blknos[0], number_of_blocks, 
+			    working_blocks[0]);
+	if (ret) {
+		failed = 1;
+		com_err(progname, ret,
+			"while doing multiblock read of block %"PRIu64,
+			blknos[0]);
+	}
+
+	for (i = 0; !failed && (i < number_of_blocks); i++) {
+		if (memcmp(pattern_blocks[i], working_blocks[i],
+			   io_get_blksize(channel))) {
+			failed = 1;
+			fprintf(stderr,
+				"%s: Cached re-read of block %"PRIu64" doesn't match alternating pattern\n",
+				progname, blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static int step15(void)
+{
+	int i, failed = 0;
+	errcode_t ret;
+
+	/* Drop the cache so writeback is through the sync codepaths */
+	io_destroy_cache(channel);
+
+	for (i = 0; i < number_of_blocks; i++) {
+		ret = io_write_block(channel, blknos[i], 1,
+				     original_blocks[i]);
+		if (ret) {
+			failed = 1;
+			com_err(progname, ret,
+				"while writing back original block %"PRIu64,
+				blknos[i]);
+		}
+	}
+
+	return failed;
+}
+
+static void finalize(void)
+{
+	/* The blocks are actually allocated in one chunk */
+	ocfs2_free(&original_blocks[0]);
+	ocfs2_free(&pattern_blocks[0]);
+	ocfs2_free(&working_blocks[0]);
+
+	io_close(channel);
+}
+
+int main(int argc, char *argv[])
+{
+	int failed = 0;
+	int rc;
+
+	initialize_ocfs_error_table();
+
+	rc = initialize(argc, argv);
+	if (rc)
+		goto out;
+
+	try_and_fail(failed, step1, out);
+	try_and_fail(failed, step2, out);
+	try_and_fail(failed, step3, out);
+	try_and_continue(failed, step4);
+	try_and_fail(failed, step5, out_writeback);
+	try_and_continue(failed, step6);
+	try_and_fail(failed, step7, out_writeback);
+
+	if (childp) {
+		try_and_fail(failed, step8, out);
+		try_and_fail(failed, step9, out);
+		goto out;
+	}
+
+	try_and_fail(failed, step10, out_writeback);
+	try_and_fail(failed, step11, out_writeback);
+	try_and_fail(failed, step12, out_writeback);
+	try_and_fail(failed, step13, out_writeback);
+	try_and_fail(failed, step14, out_writeback);
+
+out_writeback:
+	try_and_continue(failed, step15);
+
+out:
+	finalize();
+
+	if (!rc && failed)
+		rc = 1;
+
+	return rc;
+}
Index: programs/iocache/Makefile
===================================================================
--- programs/iocache/Makefile	(.../trunk)	(revision 0)
+++ programs/iocache/Makefile	(.../branches/iocache)	(revision 87)
@@ -0,0 +1,18 @@
+TOPDIR = ../..
+
+include $(TOPDIR)/Preamble.make
+
+TESTS = iocache
+
+CFLAGS = -O2 -Wall -g $(OCFS2_CFLAGS)
+
+SOURCES = iocache.c
+
+DIST_FILES = $(SOURCES)
+
+BIN_PROGRAMS = iocache
+
+iocache: iocache.o
+	$(LINK) $(OCFS2_LIBS)
+
+include $(TOPDIR)/Postamble.make

Property changes on: programs/iocache
___________________________________________________________________
Name: svn:ignore
   + .*.sw?
.*.d
iocache


Index: programs/Makefile
===================================================================
--- programs/Makefile	(.../trunk)	(revision 87)
+++ programs/Makefile	(.../branches/iocache)	(revision 87)
@@ -23,6 +23,7 @@ SUBDIRS =			\
 	fill_verify_holes	\
 	fsck-tests		\
 	fsx			\
+	iocache			\
 	iozone			\
 	kernel_build_parallel_find			\
 	lock_grab		\

Property changes on: programs/iozone/iozone3_263/src/current
___________________________________________________________________
Name: svn:ignore
   + .*.d
.*.sw?
iozone
fileop



Property changes on: programs/extend_files
___________________________________________________________________
Name: svn:ignore
   + .*.d
.*.sw?
truncate_direct



Property changes on: .
___________________________________________________________________
Name: svn:ignore
   - .*.sw?
configure
Config.make
config.log
config.status
autom4te.cache
ocfs2-test*.tar.gz

   + .*.sw?
configure
Config.make
config.log
config.status
autom4te.cache
ocfs2-test*.tar.gz
core


-- 

Life's Little Instruction Book #80

	"Slow dance"

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127



More information about the Ocfs2-tools-devel mailing list