[Ocfs2-tools-devel] [Ocfs2-test-devel] [PATCH 2/2] Enhancement for inline-data test:Add new testcases and make it workload-configurable for inline-dirs

tristan.ye tristan.ye at oracle.com
Mon Aug 25 22:15:01 PDT 2008


This patch mainly aims at ehancing the existing inline-data testing
tool(ocfs2-test/programs/inline-data/inline-dirs.c) by introducing my
newly added testcases and make it more flexbile to tune workload as to
perform different testing requirements ,such as stress test.

Following items have been enhanced or added:

1. Set a fixed workplace for inline-data test under a ocfs2 volume,where
all inlined dirs tested here.

2. Add arguments for testing binary,the mandatory ones are device and
mount point,such as ./inline-data -d /dev/sdc5 /storage,rest ones are
[-i <iteration>][-c <concurrent_process_num>] [-m
<multi_file_num>].which all have default values.specailly,we calculate
the  max_inline_size by ourselves which should be specified in original
version.

3. Add inline-dirs judgement mechanism by introducing the
ocfs2lib(that's why we need device name)to verify if dirs still inlined
or extented after test.which help us to see a cut-off for inline file
explicitly.

4. For regular testing,make the operated dirent number
configurable,which always used to be 20 instead in original version,it
also can be tuned for stress test.

5. Add testcase to fill empty entries randomly.

6. Add concurrent r/w testcase,multiple proccesses perform rename on one
directory among all its dirents,the process_nums can be tuned for stress
test.
Specially,for dirent's rename,we use shared memory and semaphore to keep
its consistency.

7.  Add multiple dirs r/w testcase,multiple process perform
rename/unlinke/recreate on different dirs separately,the file_nums can
be tuned for stress test.

8. iterations can be used for stress test.

9. for other minor modifications,please turn to patch for review.


A shell wrapper for inline-data and inline-dirs will be introduced
later,and also for multiple-nodes test(with MPI injected).

Signed-off-by: Tristan Ye <tristan.ye at oracle.com>
---

Index: ocfs2-test/programs/inline-data/inline-dirs.c
===================================================================
--- ocfs2-test/programs/inline-data/inline-dirs.c	(revision 192)
+++ ocfs2-test/programs/inline-data/inline-dirs.c	(working copy)
@@ -8,16 +8,21 @@
  */
 
 #define _XOPEN_SOURCE 600
+#define _GNU_SOURCE
 
 #include <asm/types.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/vfs.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <limits.h>
 
+#include <ocfs2/ocfs2.h>
+
 #include <dirent.h>
 
 #include <stddef.h>
@@ -26,6 +31,12 @@
 #include <errno.h>
 #include <string.h>
 
+#include <signal.h>
+#include <sys/wait.h>
+#include <inttypes.h>
+#include <linux/types.h>
+
+
 #define OCFS2_MAX_FILENAME_LEN		255
 
 /*
@@ -40,6 +51,10 @@
                                           OCFS2_DIR_ROUND) & \
 					 ~OCFS2_DIR_ROUND)
 
+#define FILE_BUFFERED_RW_FLAGS  	(O_CREAT|O_RDWR|O_TRUNC)
+#define FILE_MODE               	(S_IRUSR|S_IWUSR|S_IXUSR|S_IROTH|\
+                                	S_IWOTH|S_IXOTH|S_IRGRP|S_IWGRP|
S_IXGRP)
+
 /*
  * Quick reference table of namelen boundaries and their respective
reclens.
  *
@@ -85,16 +100,6 @@
 
 #define S_SHIFT			12
 
-struct ocfs2_dir_entry {
-/*00*/	__u64   inode;                  /* Inode number */
-	__u16   rec_len;                /* Directory entry length */
-	__u8    name_len;               /* Name length */
-	__u8    file_type;
-/*0C*/	char    name[OCFS2_MAX_FILENAME_LEN];   /* File name */
-/* Actual on-disk length specified by rec_len */
-} __attribute__ ((packed));
-
-
 struct my_dirent {
 	unsigned int	type;
 	unsigned int	name_len;
@@ -102,15 +107,51 @@
 	char		name[OCFS2_MAX_FILENAME_LEN];
 };
 
+union semun {
+	int val;                    /* value for SETVAL */
+	struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
+	unsigned short int *array;  /* array for GETALL, SETALL */
+	struct seminfo *__buf;      /* buffer for IPC_INFO */
+};
+
+#define WORK_PLACE      "inline-data-test"
+static char *prog;
+static char device[100];
+
+static ocfs2_filesys *fs = NULL;
+static struct ocfs2_super_block *ocfs2_sb = NULL;
+
+static unsigned long page_size;
+static unsigned int blocksize = 4096;
+static unsigned long clustersize;
+
+unsigned int id_count;
+unsigned long i_size;
+
+static char mount_point[255];
+static char work_place[255];
+static char dirent_name[255];
+static char dir_name[255];
+
+static int iteration = 1;
+static int do_multi_process_test = 0;
+static int do_multi_file_test = 0;
+static unsigned long child_nums = 2;
+static unsigned long file_nums = 2;
+static unsigned int operated_entries = 20;
+
+pid_t *child_pid_list = NULL;
+
+int shm_id;
+int sem_id;
+
 #define MAX_DIRENTS	1024
-struct my_dirent dirents[MAX_DIRENTS];
+struct my_dirent *dirents = NULL;
 static unsigned int num_dirents = 0;
 
 static unsigned int max_inline_size;
 unsigned int usable_space;
 
-static unsigned int blocksize;
-static char *dir_name;
 static char path[PATH_MAX];
 static char path1[PATH_MAX];
 
@@ -121,58 +162,76 @@
 	if (min == 0 && max == 0)
 		return 0;
 
-	return min + ((rand() % max) - min);
+	return min + (rand() % (max - min + 1));
 }
 
 static inline char rand_char(void)
 {
-	return 'A' + (char) get_rand(0, 52);
+	return 'A' + (char) get_rand(0, 25);
 }
 
 static void usage(void)
 {
-	printf("Usage: inline-dirs [blocksize] [DIRECTORY]\n"
-	       "Run a series of tests intended to verify I/O to and from\n"
-	       "files with inline data.\n\n"
-	       "blocksize is the blocksize of the underlying file system and
\n"
-	       "must be specified.\n"
-	       "DIRECTORY is the name of a directory which will created and\n"
-	       "be used for testing.\n");
+	printf("Usage: inline-dirs [-i <iteration>] [-s operated_entries] "
+               "[-c <concurrent_process_num>] [-m <multi_file_num>] "
+               "<-d <device>> <mount_point>\n"
+               "Run a series of tests intended to verify I/O to and
from\n"
+               "dirs with inline data.\n\n"
+               "iteration specify the running times.\n"
+	       "operated_dir_entries specify the entires number to be "
+	       "operated,such as random create/unlink/rename.\n"
+               "concurrent_process_num specify the number of concurrent
"
+               "multi_file_num specify the number of multiple dirs"
+               "processes to perform inline-data read/rename.\n"
+               "device and mount_point are mandatory.\n");
+        exit(1);
+
 }
 
 static int parse_opts(int argc, char **argv)
 {
-	if (argc < 3)
-		return 1;
+	int c;
+	while (1) {
+		c = getopt(argc, argv, "D:d:I:i:C:c:M:m:S:s:");
+		if (c == -1)
+			break;
+		switch (c) {
+		case 'i':
+		case 'I':
+			iteration = atol(optarg);
+			break;
+		case 'd':
+		case 'D':
+			strcpy(device, optarg);
+			break;
+		case 'c':
+		case 'C':
+			do_multi_process_test = 1;
+			child_nums = atol(optarg);
+			break;
+		case 'm':
+		case 'M':
+			do_multi_file_test = 1;
+			file_nums = atol(optarg);
+			break;
+		case 's':
+		case 'S':
+			operated_entries = atol(optarg);
+		default:
+			break;
+		}
+	}
 
-	blocksize = atoi(argv[1]);
-	dir_name = argv[2];
+	if (strcmp(device, "") == 0)
+		return EINVAL;
 
-	switch (blocksize) {
-	case 4096:
-		max_inline_size = 3896;
-		break;
-	case 2048:
-		max_inline_size = 1848;
-		break;
-	case 1024:
-		max_inline_size = 824;
-		break;
-	case 512:
-		max_inline_size = 312;
-		break;
-	default:
-		fprintf(stderr, "Invalid blocksize, %u\n", blocksize);
-		return 1;
-	}
+	if (argc - optind != 1)
+		return EINVAL;
 
-	usable_space = max_inline_size;
-	usable_space -= OCFS2_DIR_REC_LEN(1) + OCFS2_DIR_REC_LEN(2);
+	strcpy(mount_point, argv[optind]);
+	if (mount_point[strlen(mount_point) - 1] == '/')
+		mount_point[strlen(mount_point) - 1] = '\0';
 
-	printf("Blocksize:\t\t%d\nMax Inline Data Size:\t%d\nDir Name:\t\t%s
\n"
-	       "Usable Dir Space:\t%d\n",
-	       blocksize, max_inline_size, dir_name, usable_space);
-
 	return 0;
 }
 
@@ -252,7 +311,7 @@
 	int ret;
 	struct my_dirent *dirent;
 
-	memset(dirents, 0, sizeof(dirents));
+	memset(dirents, 0, sizeof(struct my_dirent) * MAX_DIRENTS);
 
 	dirent = &dirents[0];
 	dirent->type = S_IFDIR >> S_SHIFT;
@@ -265,8 +324,10 @@
 	strcpy(dirent->name, "..");
 
 	num_dirents = 2;
+	snprintf(dirent_name, 255, "inline-data-dir-test-%d", getpid());
+	snprintf(dir_name, 255, "%s/%s", work_place, dirent_name);
 
-	ret = mkdir(dir_name, 0755);
+	ret = mkdir(dir_name, FILE_MODE);
 	if (ret) {
 		ret = errno;
 		fprintf(stderr, "mkdir failure %d: %s\n", ret, strerror(ret));
@@ -289,7 +350,7 @@
 
 	sprintf(path, "%s/%s", dir_name, dirent->name);
 
-	fd = open(path, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+	fd = open(path, FILE_BUFFERED_RW_FLAGS, FILE_MODE);
 	if (fd == -1) {
 		ret = errno;
 		fprintf(stderr, "open failure %d: %s\n", ret, strerror(ret));
@@ -308,36 +369,50 @@
 		create_file(path1);
 	}
 }
-
-static void get_directory_almost_full(int minus_this_many)
+static int get_max_inlined_entries(int max_inline_size)
 {
 	unsigned int almost_full_entries;
 
-	/*
-	 * This will create enough entries to leave only 280 free
-	 * bytes in the directory.
-	 *
-	 * max_inline_size % 512 = 312    [always]
-	 * rec overhead for '.' and '..' = 32
-	 * So, 312 - 32 = 280.
-	 *
-	 */
+        /*
+         * This will create enough entries to leave only 280 free
+         * bytes in the directory.
+         *
+         * max_inline_size % 512 = 312    [always]
+         * rec overhead for '.' and '..' = 32
+         * So, 312 - 32 = 280.
+         *
+         */
 	almost_full_entries = max_inline_size / 512;
 	almost_full_entries *= 512;
 	almost_full_entries /= 32;
 
-	/*
-	 * Now we add enough 32 byte entries to fill that remaining 280 bytes:
-	 *
-	 * 280 / 32 = 8
-	 *
-	 * And we'll be left over with 24 bytes:
-	 *
-	 * 280 % 32 = 24
-	 *
-	 * Which can easily be overflowed by adding one more 32 byte entry.
-	 */
+        /*
+         * Now we add enough 32 byte entries to fill that remaining 280
bytes:
+         *
+         * 280 / 32 = 8
+         *
+         * And we'll be left over with 24 bytes:
+         *
+         * 280 % 32 = 24
+         *
+         * Which can easily be overflowed by adding one more 32 byte
entry.
+         */
 	almost_full_entries += 8;
+        /*if user-specified operated_entries larger than this,decrease
to a
+          right inlined number
+        */
+	if (operated_entries > almost_full_entries)
+		operated_entries = almost_full_entries - 1;
+
+	return almost_full_entries;
+
+}
+static void get_directory_almost_full(int minus_this_many)
+{
+	int almost_full_entries;
+
+	almost_full_entries = get_max_inlined_entries(max_inline_size);
+
 	almost_full_entries -= minus_this_many;
 
 	/* Need up to 20 characters to get a 32 byte entry */
@@ -349,10 +424,8 @@
 	int i, ret;
 	struct my_dirent *dirent;
 
-	while (iters--) {
-		i = get_rand(0, num_dirents);
-		if (i >= num_dirents)
-			abort();
+	while (iters > 0) {
+		i = get_rand(0, num_dirents - 1);
 		dirent = &dirents[i];
 
 		if (is_dot_entry(dirent))
@@ -369,18 +442,52 @@
 		}
 
 		dirent->name_len = 0;
+		iters--;
 	}
 }
 
+static void random_fill_empty_entries(int iters)
+{
+	int i, ret, fd;
+	struct my_dirent *dirent;
+
+	while (iters > 0) {
+		i = get_rand(0, num_dirents - 1);
+		dirent = &dirents[i];
+
+		if (is_dot_entry(dirent))
+			continue;
+		if (dirent->name_len > 0)
+			continue;
+
+		//sprintf(path1, "%s%011d", "filename-", i);
+		dirent->type = S_IFREG >> S_SHIFT;
+		dirent->name_len = strlen(dirent->name);
+		dirent->seen = 0;
+		//strcpy(dirent->name, path1);
+
+		sprintf(path, "%s/%s", dir_name, dirent->name);
+
+		fd = open(path, FILE_BUFFERED_RW_FLAGS, FILE_MODE);
+		if (fd == -1) {
+			ret = errno;
+			fprintf(stderr, "open failure %d: %s\n", ret, strerror(ret));
+			exit(ret);
+		}
+
+		close(fd);
+		
+		iters--;
+	}
+}
+
 static void random_rename_same_reclen(int iters)
 {
 	int i, ret;
 	struct my_dirent *dirent;
 
-	while (iters--) {
-		i = get_rand(0, num_dirents);
-		if (i >= num_dirents)
-			abort();
+	while (iters > 0) {
+		i = get_rand(0, num_dirents - 1);
 		dirent = &dirents[i];
 
 		if (is_dot_entry(dirent))
@@ -391,8 +498,10 @@
 		/*
 		 * We already renamed this one
 		 */
+		/*
 		if (dirent->name[0] == 'R')
 			continue;
+		*/
 
 		strcpy(path, dirent->name);
 		path[0] = 'R';
@@ -411,6 +520,7 @@
 			exit(ret);
 		}
 		dirent->name[0] = 'R';
+		iters--;
 	}
 }
 
@@ -420,12 +530,8 @@
 	struct my_dirent *dirent1, *dirent2;
 
 	while (iters--) {
-		i = get_rand(0, num_dirents);
-		if (i >= num_dirents)
-			abort();
-		j = get_rand(0, num_dirents);
-		if (j >= num_dirents)
-			abort();
+		i = get_rand(0, num_dirents - 1);
+		j = get_rand(0, num_dirents - 1);
 		dirent1 = &dirents[i];
 		dirent2 = &dirents[j];
 
@@ -520,6 +626,89 @@
 	closedir(dir);
 }
 
+static int is_dir_inlined(char *dirent_name, unsigned long *i_size,
+                           unsigned int *id_count)
+{
+        int ret;
+	uint64_t workplace_blk_no = 1;
+	uint64_t testdir_blk_no = 1;
+	char *buf = NULL;
+	struct ocfs2_dinode *di;
+	struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super);
+
+	sync();
+
+	ocfs2_malloc_block(fs->fs_io, &buf);
+
+	/*lookup worksplace inode*/
+	ret = ocfs2_lookup(fs, sb->s_root_blkno, WORK_PLACE,
+			   strlen(WORK_PLACE), NULL, &workplace_blk_no);
+	if ( ret < 0 ) {
+		fprintf(stderr, "failed to lookup work_place(%s)'s"
+			" inode blkno\n", work_place);
+		ocfs2_free(&buf);
+		exit(ret);
+	}
+
+	/*lookup file inode,then read*/
+	ret = ocfs2_lookup(fs, workplace_blk_no, dirent_name,
strlen(dirent_name),
+			   NULL, &testdir_blk_no);
+	if ( ret < 0 ) {
+		fprintf(stderr, "failed to lookup file(%s/%s)'s"
+			" inode blkno\n", work_place, dirent_name);
+		ocfs2_free(&buf);
+		exit(ret);
+	}
+
+	ret = ocfs2_read_inode(fs, testdir_blk_no, buf);
+	if ( ret < 0 ) {
+		fprintf(stderr, "failed to read file(%s/%s/%s)'s"
+			" inode.\n", mount_point, WORK_PLACE, dirent_name);
+		ocfs2_free(&buf);
+		exit(ret);
+	}
+
+	di = (struct ocfs2_dinode *)buf;
+	*i_size = di->i_size;
+	*id_count = ((di->id2).i_data).id_count;
+	
+	if (di->i_dyn_features & OCFS2_INLINE_DATA_FL)
+		ret = 1;
+	else
+		ret = 0;
+
+	ocfs2_free(&buf);
+	return ret;
+}
+
+static void should_inlined_or_not(int is_inlined, int should_inlined,
int test_no)
+{
+	/* is_inlined represent if the ret is inlined or not
+	   while should_inlined represnt if we expect it inlined or not.
+	*/
+	if (should_inlined) {
+		if (!is_inlined) {
+			fprintf(stderr, "After Test #%d, dir %s should be "
+				"inlined here!\n", test_no, dir_name);
+			fprintf(stderr, "Dir(%s): i_size = %d,id_count = %d\n",
+				dir_name, i_size, id_count);
+			exit(-1);
+		}
+
+	} else {
+		if (is_inlined) {
+			fprintf(stderr, "After Test #%d, dir %s should be "
+				"extented here!\n", test_no, dir_name);
+			fprintf(stderr, "Dir(%s): i_size = %d,id_count = %d\n",
+				dir_name, i_size, id_count);
+			exit(-1);
+
+		}
+	}
+
+	return;
+}
+
 /*
  * [I] Basic tests of inline-dir code.
  *    1) Basic add files
@@ -529,12 +718,23 @@
  */
 static void run_basic_tests(void)
 {
+	int ret;
+
 	printf("Test %d: fill directory\n", testno);
 	create_and_prep_dir();
 	get_directory_almost_full(0);
 	verify_dirents();
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 1, testno);
 	testno++;
 
+	printf("Test %d: expand inlined dir to extent exactly\n", testno);
+	/*Should be 13 bits len dirent_name*/
+	create_file("Iam13bitshere");
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 0, testno);
+	testno++;
+
 	printf("Test %d: remove directory\n", testno);
 	destroy_dir();
 	testno++;
@@ -542,26 +742,44 @@
 	printf("Test %d: rename files with same namelen\n", testno);
 	create_and_prep_dir();
 	get_directory_almost_full(1);
-	random_rename_same_reclen(20);
+	random_rename_same_reclen(operated_entries);
 	verify_dirents();
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 1, testno);
 	destroy_dir();
 	testno++;
 
+	
 	printf("Test %d: rename files with same namelen on top of each other
\n", testno);
 	create_and_prep_dir();
 	get_directory_almost_full(1);
-	random_deleting_rename(20);
+	random_deleting_rename(operated_entries);
 	verify_dirents();
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 1, testno);
 	destroy_dir();
 	testno++;
 
+	printf("Test %d: random unlink/fill entries.\n", testno);
+	create_and_prep_dir();
+	get_directory_almost_full(0);
+	random_unlink(operated_entries);
+	random_fill_empty_entries(operated_entries);
+	verify_dirents();
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 1, testno);
+	destroy_dir();
+	testno++;
+
 	printf("Test %d: random rename/unlink files with same namelen\n",
 	       testno);
 	create_and_prep_dir();
 	get_directory_almost_full(1);
-	random_unlink(20);
-	random_rename_same_reclen(20);
+	random_unlink(operated_entries / 2);
+	random_rename_same_reclen(operated_entries / 2);
 	verify_dirents();
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 1, testno);
 	destroy_dir();
 	testno++;
 
@@ -569,14 +787,14 @@
 	       testno);
 	create_and_prep_dir();
 	get_directory_almost_full(1);
-	random_unlink(20);
-	random_rename_same_reclen(20);
-	create_files("frag1a", 20);
-	random_unlink(20);
-	create_files("frag1b", 20);
-	random_deleting_rename(20);
-	random_rename_same_reclen(20);
-	create_files("frag1c", 20);
+	random_unlink(operated_entries / 2);
+	random_rename_same_reclen(operated_entries / 2);
+	create_files("frag1a", operated_entries / 2);
+	random_unlink(operated_entries / 2);
+	create_files("frag1b", operated_entries / 2);
+	random_deleting_rename(operated_entries / 2);
+	random_rename_same_reclen(operated_entries / 2);
+	create_files("frag1c", operated_entries / 2);
 	verify_dirents();
 	destroy_dir();
 	testno++;
@@ -590,11 +808,20 @@
  */
 static void run_large_dir_tests(void)
 {
+	int ret;
+
 	printf("Test %d: Add file name large enough to push out one block\n",
testno);
 	create_and_prep_dir();
 	get_directory_almost_full(0);
 	create_files("Pushedfn-", 1);
 	verify_dirents();
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 0, testno);
+	/*verify i_size should be one block size here*/
+	if (i_size != blocksize) {
+		fprintf(stderr, "i_size should be %d,while it's % here!\n",
+			blocksize, i_size);
+	}
 	destroy_dir();
 	testno++;
 
@@ -603,6 +830,13 @@
 	get_directory_almost_full(0);

create_files("this_is_an_intentionally_long_filename_prefix_to_stress_the_dir_code-this_is_an_intentionally_long_filename_prefix_to_stress_the_dir_code-this_is_an_intentionally_long_filename_prefix_to_stress_the_dir_code", 1);
 	verify_dirents();
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 0, testno);
+	/*verify i_size should be one block size here*/
+	if (i_size != blocksize * 2) {
+		fprintf(stderr, "i_size should be %d,while it's % here!\n",
+			blocksize * 2, i_size);
+	}
 	destroy_dir();
 	testno++;
 
@@ -610,33 +844,384 @@
 	       testno);
 	create_and_prep_dir();
 	get_directory_almost_full(1);
-	random_unlink(20);
-	random_rename_same_reclen(20);
-	create_files("frag2a", 20);
-	random_unlink(20);
-	create_files("frag2b", 20);
-	random_deleting_rename(20);
-	random_rename_same_reclen(20);
-	create_files("frag2c", 30);
+	random_unlink(operated_entries / 2);
+	random_rename_same_reclen(operated_entries / 2);
+	create_files("frag2a", operated_entries / 2);
+	random_unlink(operated_entries / 2);
+	create_files("frag2b", operated_entries / 2);
+	random_deleting_rename(operated_entries / 2);
+	random_rename_same_reclen(operated_entries / 2);
+	create_files("frag2c", operated_entries / 2 + 1);
+	create_files("frag2d", operated_entries / 2 + 1);
 	verify_dirents();
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 0, testno);
 	destroy_dir();
 	testno++;
 }
 
-int main(int argc, char **argv)
+static void sigchld_handler()
 {
-	if (parse_opts(argc, argv)) {
+	pid_t pid;
+	union wait status;
+
+	while (1) {
+		pid = wait3(&status, WNOHANG, NULL);
+		if (pid <= 0)
+			break;
+	}
+}
+
+static void sigint_handler()
+{
+	/*Kill all children*/
+	int i;
+	int process_nums;
+
+	if (do_multi_process_test)
+		process_nums = child_nums;
+	else
+		process_nums = file_nums;
+
+	for (i = 0; i < process_nums; i++)
+		kill(child_pid_list[i], SIGTERM);
+
+	free(child_pid_list);
+	signal(SIGINT, SIG_DFL);
+	kill(getpid(), SIGINT);
+
+}
+
+static void sigterm_handler()
+{
+	/*Kill all children*/
+	int i;
+	int process_nums;
+
+	if (do_multi_process_test)
+		process_nums = child_nums;
+	else
+		process_nums = file_nums;
+
+	for (i = 0; i < process_nums; i++)
+		kill(child_pid_list[i], SIGTERM);
+
+	free(child_pid_list);
+	signal(SIGTERM, SIG_DFL);
+	kill(getpid(), SIGTERM);
+
+}
+
+static int set_semvalue(void)
+{
+	union semun sem_union;
+
+	sem_union.val = 1;
+	if (semctl(sem_id, 0, SETVAL, sem_union) == -1) {
+		perror("semctl");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int semaphore_p(void)
+{
+	struct sembuf sem_b;
+    
+	sem_b.sem_num = 0;
+	sem_b.sem_op = -1; /* P() */
+	sem_b.sem_flg = SEM_UNDO;
+	if (semop(sem_id, &sem_b, 1) == -1) {
+		fprintf(stderr, "semaphore_p failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int semaphore_v(void)
+{
+	struct sembuf sem_b;
+    
+	sem_b.sem_num = 0;
+	sem_b.sem_op = 1; /* V() */
+	sem_b.sem_flg = SEM_UNDO;
+	if (semop(sem_id, &sem_b, 1) == -1) {
+		fprintf(stderr, "semaphore_v failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void del_semvalue(void)
+{
+	union semun sem_union;
+    
+	if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
+		fprintf(stderr, "Failed to delete semaphore\n");
+}
+
+static void run_concurrent_test(void)
+{
+	int ret, rc;
+	int i, j, status;
+	struct my_dirent *old_dirents;
+	key_t sem_key = IPC_PRIVATE, shm_key = IPC_PRIVATE;
+	
+	pid_t pid;
+
+	if (!do_multi_process_test)
+		return;
+
+	printf("Test %d: concurrent dir RW with multiple processes!\n",
testno);
+        create_and_prep_dir();
+        get_directory_almost_full(1);
+
+	/*get and init semaphore*/
+	sem_id = semget(sem_key, 1, 0766 | IPC_CREAT);
+	if (set_semvalue() < 0) {
+		fprintf(stderr,"Set semaphore value failed!\n");
+		exit(1);
+	}
+
+	/*should use shared memory here */
+	old_dirents = dirents;
+	dirents = NULL;
+
+	shm_id = shmget(shm_key, sizeof(struct my_dirent) * MAX_DIRENTS,
IPC_CREAT | 0766);
+	if (shm_id < 0) {
+		perror("shmget");
+		exit(1);
+	}
+
+	dirents = (struct my_dirent *)shmat(shm_id, 0, 0);
+	if (dirents < 0) {
+		perror("shmat");
+		exit(1);
+	}
+
+	shmctl(shm_id, IPC_RMID, 0);
+	memmove(dirents, old_dirents, sizeof(struct my_dirent) * MAX_DIRENTS);
+	
+	/*flush out the father's i/o buffer*/
+	fflush(stderr);
+	fflush(stdout);
+
+	signal(SIGCHLD, sigchld_handler);
+
+	for (i = 0; i < child_nums; i++) {
+		pid = fork();
+		if (pid < 0) {
+			fprintf(stderr, "Fork process error!\n");
+			exit(pid);
+		}
+		if (pid == 0) {
+			if (semaphore_p() < 0)
+				exit(-1);
+			/*Concurrent rename for dirents*/
+			random_rename_same_reclen(operated_entries);
+			if (semaphore_v() < 0)
+				exit(-1);
+			/*child exits normally*/
+			sleep(1);
+			exit(0);
+		}
+		if (pid > 0)
+			child_pid_list[i] = pid;
+	}
+
+	signal(SIGINT, sigint_handler);
+	signal(SIGTERM, sigterm_handler);
+
+	/*father wait all children to leave*/
+	for (i = 0; i < child_nums; i ++) {
+		ret = waitpid(child_pid_list[i], &status,0);
+		rc = WEXITSTATUS(status);
+		if (rc) {
+			fprintf(stderr, "Child %d exits abnormally with "
+				"RC=%d\n", child_pid_list[i], rc);
+			exit(rc);
+		}
+	}
+	/*father help to verfiy dirents' consistency*/
+	sleep(2);
+        verify_dirents();
+
+	ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+	should_inlined_or_not(ret, 1, testno);
+	destroy_dir();
+
+	/*detach shared memory*/
+	if (shmdt(dirents) == -1) {
+		perror("shmdt");
+		 exit(1);
+	}
+
+	dirents = old_dirents;
+	testno++;
+
+	return;
+}
+
+static void run_multiple_test(void)
+{
+	int i, j, status;
+	
+	pid_t pid;
+	int ret, rc;
+	
+	if (!do_multi_file_test)
+		return;
+
+	printf("Test %d: multiple dirs RW with multiple processes!\n",
testno);
+
+	fflush(stderr);
+	fflush(stdout);
+
+	signal(SIGCHLD, sigchld_handler);
+	
+	for (i = 0; i < file_nums; i++) {
+		pid = fork();
+		if (pid < 0) {
+			fprintf(stderr, "Fork process error!\n");
+			exit(pid);
+		}
+		if (pid == 0) {
+			create_and_prep_dir();
+			get_directory_almost_full(1);
+			random_rename_same_reclen(operated_entries);
+			random_unlink(operated_entries);
+			random_fill_empty_entries(operated_entries);
+			verify_dirents();
+			sync();
+			sleep(1);
+			ret = is_dir_inlined(dirent_name, &i_size, &id_count);
+			should_inlined_or_not(ret, 1, testno);
+			destroy_dir();
+			exit(0);
+
+		}
+		if (pid > 0)
+			child_pid_list[i] = pid;
+
+	}
+
+	signal(SIGINT, sigint_handler);
+	signal(SIGTERM, sigterm_handler);
+
+	/*father wait all children to leave*/
+	for (i = 0; i < file_nums; i ++) {
+		ret = waitpid(child_pid_list[i], &status,0);
+		rc = WEXITSTATUS(status);
+		if (rc) {
+			fprintf(stderr, "Child %d exits abnormally with "
+				"RC=%d\n", child_pid_list[i], rc);
+			exit(rc);
+		}
+	}
+
+	testno++;
+	return;
+}
+
+static int open_ocfs2_volume(char *device_name)
+{
+	int open_flags = OCFS2_FLAG_HEARTBEAT_DEV_OK | OCFS2_FLAG_RO;
+	int ret;
+
+	ret = ocfs2_open(device_name, open_flags, 0, 0, &fs);
+	if (ret < 0) {
+		fprintf(stderr,"Not a ocfs2 volume!\n");
+		return ret;
+	}
+
+	ocfs2_sb = OCFS2_RAW_SB(fs->fs_super);
+	if (!(ocfs2_sb->s_feature_incompat &
OCFS2_FEATURE_INCOMPAT_INLINE_DATA)) {
+		fprintf(stderr,"Inline-data not supported"
+			" on this ocfs2 volume\n");
+		return -1;
+	}
+
+	blocksize = 1 << ocfs2_sb->s_blocksize_bits;
+	clustersize = 1 << ocfs2_sb->s_clustersize_bits;
+	max_inline_size = ocfs2_max_inline_data(blocksize);
+
+	return 0;
+}
+
+static void setup(int argc, char *argv[])
+{
+	int ret;
+
+	prog = strrchr(argv[0], '/');
+	if (prog == NULL)
+		prog = argv[0];
+	else
+		prog++;
+
+	if (parse_opts(argc, argv))
 		usage();
-		return EINVAL;
+
+	ret = open_ocfs2_volume(device);
+	if (ret < 0) {
+		fprintf(stderr,"Open_ocfs2_volume failed!\n");
+		exit(ret);
 	}
 
+	if (do_multi_process_test)
+		child_pid_list = (pid_t *)malloc(sizeof(pid_t) * child_nums);
+	if (do_multi_file_test)
+		child_pid_list = (pid_t *)malloc(sizeof(pid_t) * file_nums);
+
+	dirents = (struct my_dirent *)malloc(sizeof(struct my_dirent) *
MAX_DIRENTS);
+	memset(dirents, 0, sizeof(struct my_dirent) * MAX_DIRENTS);
+
 	srand(getpid());
+	page_size = sysconf(_SC_PAGESIZE);
 
-	run_basic_tests();
+	snprintf(work_place, 255, "%s/%s",mount_point,WORK_PLACE);
+	mkdir(work_place, FILE_MODE);
 
-	run_large_dir_tests();
+	printf("BlockSize:\t\t%d\nMax Inline Data Size:\t%d\n"
+	       "ClusterSize:\t\t%d\nPageSize:\t\t%d\nWorkingPlace:\t\t%s\n"
+	       "NumOfMaxInlinedEntries:\t\t%d\n\n", blocksize,
max_inline_size,
+	       clustersize, page_size, work_place,
+               get_max_inlined_entries(max_inline_size));
 
-	printf("All File I/O Tests Passed\n");
+        return ;
 
+}
+
+static void teardown(void)
+{
+	if (dirents)
+		free(dirents);
+	
+	if (child_pid_list)
+		free(child_pid_list);
+}
+
+int main(int argc, char **argv)
+{
+	int i;
+
+	setup(argc,argv);
+
+	for (i = 0; i < iteration; i++){
+
+		printf("################Test Round %d################\n", i);
+		testno = 1;
+		run_basic_tests();
+		run_large_dir_tests();
+		run_multiple_test();
+		run_concurrent_test();
+		printf("All File I/O Tests Passed\n");
+
+	}
+
+	teardown();
+
 	return 0;
 }




More information about the Ocfs2-tools-devel mailing list