[Ocfs2-tools-devel] Patch for journal truncate of ocfs2-tools.

tao.ma tao.ma at oracle.com
Wed May 9 00:39:44 PDT 2007


Sunil and Marcos,
The test patch are attached. Please review it.


Sunil Mushran wrote:
> Tao,
>
> Looks good. However, I would like few tests added in conjuction with
> this.
>
> 1. Test for ocfs2_truncate(). Test should create a file with lots of
> extents (using the same scheme of allocating multiple clusters but
> appending to the file in reverse order) and then truncate it. Run fsck
> to check correctness. We should be able to run the same basic test in
> multiple combinations of bs/cs. For each combination, we should make the
> tree level 0 and level 1 trees.
>   
The test script is added at ocfs2-test/programs/truncate/ocfs2-tools.
Another c program is also added for the test.
> 2. Specific test for resizing journals in tunefs. Test should not try to
> stress test truncate but concentrate on ensuring that the user not
> resize the journal in conjunction with num slot increase.
>   
The test case is added to ocfs2-test/programs/tunefs-test/tunefs-test.sh

The case increase journal size and increase number of nodes, decrease journal while increasing slots.
As for increasing journal without changing the number of slots, it already exists in the script.


> Thanks
> Sunil 
>
>
>   
 
-------------- next part --------------
Index: programs/tunefs-test/tunefs-test.sh
===================================================================
--- programs/tunefs-test/tunefs-test.sh	(revision 94)
+++ programs/tunefs-test/tunefs-test.sh	(working copy)
@@ -437,6 +437,66 @@ Add_Backup_Super()
 	   test_pass;
 	fi;
 }
+
+#
+# Journal_Node_Change - Change journal size and node slots simultaneously.
+#
+Journal_Node_Change()
+{
+	LogMsg "tunefs_test : Testing Changing journal size and node slots simultaneously."
+
+	# Increase journal size and node slots.
+	(( ++NUM_OF_TESTS ))
+	Set_Volume_For_Test;
+	CURRENT_TEST="Increase Journal Size to ${JOURNAL3} and increase nodes to ${NNODES2}";
+	echo "y"|${TUNEFS_BIN} -N ${NNODES2} -J size=${JOURNAL3} ${DEVICE} 2>&1 >> ${TUNEFSLOG};
+
+	# Check whether we succeed.
+	Check_Volume;
+	SB_NNODES=`${DEBUGFS_BIN} -n -R "stats" ${DEVICE}|${GREP} Slots| \
+		 ${GAWK} '{print \$4; exit}'`;
+	if [ ${SB_NNODES} -ne ${NNODES2} ]; then
+	   test_fail;
+	   LogMsg "tunefs_test : #of nodes change failed. \c"
+	   LogMsg "Superblock number of nodes (${SB_NNODES})"
+	else
+		SB_JSIZE=`${DEBUGFS_BIN} -n -R "ls -l //" ${DEVICE}|${GREP} -i Journal:0001| 
+			${GAWK} '{print \$6; exit}'`;
+		if [ ${SB_JSIZE} -ne ${JOURNAL3} ]; then
+		   test_fail;
+		   LogMsg "tunefs_test : Journal size change failed.\c"
+		   LogMsg " Superblock Journal Size (${SB_JSIZE})"
+		else
+		   test_pass;
+		fi;
+	fi;
+
+
+	# Decrease journal size and increase the slot nums.
+	(( ++NUM_OF_TESTS ))
+	CURRENT_TEST="Decrease Journal Size to ${JOURNAL2} and increase nodes to ${NNODES3}";
+	echo "y"|${TUNEFS_BIN} -N ${NNODES3} -J size=${JOURNAL2} ${DEVICE} 2>&1 >> ${TUNEFSLOG};
+
+	# Check whether we succeed.
+	Check_Volume;
+	SB_NNODES=`${DEBUGFS_BIN} -n -R "stats" ${DEVICE}|${GREP} Slots| \
+		 ${GAWK} '{print \$4; exit}'`;
+	if [ ${SB_NNODES} -ne ${NNODES3} ]; then
+	   test_fail;
+	   LogMsg "tunefs_test : #of nodes change failed. \c"
+	   LogMsg "Superblock number of nodes (${SB_NNODES})"
+	else
+		SB_JSIZE=`${DEBUGFS_BIN} -n -R "ls -l //" ${DEVICE}|${GREP} -i Journal:0001| 
+			${GAWK} '{print \$6; exit}'`;
+		if [ ${SB_JSIZE} -ne ${JOURNAL2} ]; then
+		   test_fail;
+		   LogMsg "tunefs_test : Journal size change failed.\c"
+		   LogMsg " Superblock Journal Size (${SB_JSIZE})"
+		else
+		   test_pass;
+		fi;
+	fi;
+}
 ################################################################
 
 #
@@ -522,6 +582,8 @@ Change_Mount_Type
 
 Add_Backup_Super
 
+Journal_Node_Change
+
 test_summary
 
 LogMsg "\ntunefs_test: Ending test (`date +%F-%H-%M-%S`)\n" 
Index: programs/truncate/ocfs2-tools/test_truncate.c
===================================================================
--- programs/truncate/ocfs2-tools/test_truncate.c	(revision 0)
+++ programs/truncate/ocfs2-tools/test_truncate.c	(revision 0)
@@ -0,0 +1,327 @@
+/*
+ * test_truncate.c
+ *
+ * test file for ocfs2_truncate
+ *
+ * Copyright (C) 2007 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.
+ *
+ */
+
+/*
+ * This file is used to test whether ocfs2_truncate can truncate
+ * a file to a specified size.
+ *
+ * An additional option "c" is to create a file before we test truncating.
+ * We can give the tree depth so that the file can be created with the
+ * specified tree depth.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stddef.h>
+
+#include <ocfs2/ocfs2.h>
+
+
+char *progname = NULL;
+
+static void handle_signal (int sig)
+{
+	switch (sig) {
+	case SIGTERM:
+	case SIGINT:
+		exit(1);
+	}
+
+	return ;
+}
+
+enum operations {
+	CREATE = 1,
+	SET_SIZE
+};
+
+struct{
+	char file_name[OCFS2_MAX_FILENAME_LEN];
+	enum operations ops;
+	uint64_t extras;
+} options;
+
+static char *device = NULL;
+
+static void usage (const char *progname)
+{
+	fprintf(stderr, "usage: %s -f file-name\n"
+		"\t\t[-c tree-depth] [-s new-size] device\n",
+		progname);
+
+	exit(0);
+}
+
+static int read_options(int argc, char **argv)
+{
+	int c;
+
+	progname = basename(argv[0]);
+
+	if (argc < 2)
+		return 1;
+
+	while(1) {
+		c = getopt(argc, argv, "f:c:s:");
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f':	/* file name */
+			strcpy(options.file_name, optarg);
+			break;
+
+		case 'c':
+			if (options.ops != 0)
+				usage(progname);
+			options.ops = CREATE;
+			options.extras = strtoull(optarg, NULL, 0);
+			if (options.extras > 4) {
+				com_err(progname, 0, "We can only create"
+					"a file with tree_depth <= 4");
+				exit(1);
+			}
+			break;
+
+		case 's':
+			if (options.ops != 0)
+				usage(progname);
+			options.ops = SET_SIZE;
+			options.extras = strtoull(optarg, NULL, 0);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (optind < argc && argv[optind])
+		device = argv[optind];
+
+	return 0;
+}
+
+static errcode_t open_test_inode(ocfs2_filesys *fs, char *name, uint64_t *ino)
+{
+	errcode_t ret = 0;
+	uint64_t tmp_blkno = 0;
+	int namelen = strlen(name);
+
+	ret = ocfs2_lookup(fs, fs->fs_root_blkno, name, namelen,
+			   NULL, &tmp_blkno);
+	if (!ret) {
+		*ino = tmp_blkno;
+		return 0;
+	} else if (ret != OCFS2_ET_FILE_NOT_FOUND)
+		return ret;
+
+	ret = ocfs2_new_inode(fs, &tmp_blkno, S_IFREG | 0755);
+	if (ret)
+		return ret;
+
+	ret = ocfs2_link(fs, fs->fs_root_blkno, name,
+			 tmp_blkno, OCFS2_FT_REG_FILE);
+	if (ret == OCFS2_ET_DIR_NO_SPACE) {
+		ret = ocfs2_expand_dir(fs, fs->fs_root_blkno,
+				       fs->fs_root_blkno);
+		if (ret)
+			return ret;
+
+		ret = ocfs2_link(fs, fs->fs_root_blkno, name,
+				 tmp_blkno, OCFS2_FT_REG_FILE);
+		if (ret)
+			return ret;
+	} else if (ret)
+		return ret;
+
+	*ino = tmp_blkno;
+
+	return 0;
+}
+
+/*
+ * This function is similar to ocfs2_extend_allocation() as both extend files.
+ * However, this one ensures that the extent record tree grows much faster.
+ */
+static errcode_t custom_extend_allocation(ocfs2_filesys *fs, uint64_t ino,
+					  uint32_t new_clusters)
+{
+	errcode_t ret;
+	uint32_t n_clusters;
+	uint32_t i;
+	uint64_t blkno;
+	uint64_t tmpblk;
+
+	while (new_clusters) {
+		ret = ocfs2_new_clusters(fs, 1, new_clusters, &blkno,
+					 &n_clusters);
+		if (ret)
+			goto bail;
+
+		/* In order to ensure the extent records are not coalesced,
+		 * we insert each cluster in reverse. */
+		for(i = n_clusters; i; --i) {
+			tmpblk = blkno + ocfs2_clusters_to_blocks(fs, i - 1);
+		 	ret = ocfs2_insert_extent(fs, ino, tmpblk, 1);
+			if (ret) 
+				goto bail;	
+		}
+	 	new_clusters -= n_clusters;
+	}
+
+bail:
+	return ret;
+}
+
+static inline int get_rand(int min, int max)
+{
+	if (min == max)
+		return min;
+
+	return min + (rand() % (max - min));
+}
+
+/* Create the file with the specified tree_depth. */
+static errcode_t create_file(ocfs2_filesys *fs, uint64_t ino, uint64_t tree_depth)
+{
+	errcode_t ret;
+	uint64_t size;
+	char *buf = NULL;
+	uint32_t clusters = 0;
+	struct ocfs2_dinode *di = NULL;
+	int random_rec_in_last_eb, random_rec_in_dinode;
+	int ext_rec_per_inode = ocfs2_extent_recs_per_inode(fs->fs_blocksize);
+	int ext_rec_per_eb = ocfs2_extent_recs_per_eb(fs->fs_blocksize);
+
+	srand((unsigned int)fs);
+	/* In order to build up the tree quickly, we allocate only 1 cluster
+	 * to an extent, so we can calculate the extent numbers and set the
+	 * clusters accordingly.
+	 */
+	random_rec_in_dinode = get_rand(1, ext_rec_per_inode);
+
+	if (tree_depth == 0)
+		clusters = random_rec_in_dinode;
+	else {
+		/* Now we will create a tree with the tree_depth.
+		 * we will increase the tree detph gradually.
+		 * 
+		 * In order to speed up the generation, we just set
+		 * the tree root to be "3"(there are only 2 extent rec
+		 * in the dinode which are full.
+		 */
+		clusters = 2;
+		while (tree_depth--) {
+			clusters *= ext_rec_per_eb;
+			random_rec_in_last_eb = get_rand(1, ext_rec_per_eb);
+			clusters += random_rec_in_last_eb;
+		}
+	}
+	
+	if (clusters == 0)
+		return -1;
+
+	ret = custom_extend_allocation(fs, ino, clusters);
+	if (ret)
+		goto bail;
+
+	/* set the file size accordingly since ocfs2_truncate will
+	 * check the size during its operation.
+	 */
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto bail;
+	
+	ret = ocfs2_read_inode(fs, ino, buf);
+	if (ret)
+		goto bail;
+
+	size = clusters * fs->fs_clustersize;
+	di = (struct ocfs2_dinode *)buf;
+	di->i_size = size;
+
+	ret = ocfs2_write_inode(fs, ino, buf);	
+
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
+int main (int argc, char **argv)
+{
+	ocfs2_filesys *fs = NULL;
+	errcode_t ret = 1;
+	uint64_t inode;
+
+	initialize_ocfs_error_table();
+
+#define INSTALL_SIGNAL(sig)					\
+	do {							\
+		if (signal(sig, handle_signal) == SIG_ERR) {	\
+		    printf("Could not set " #sig "\n");		\
+		    goto bail;					\
+		}						\
+	} while (0)
+
+	INSTALL_SIGNAL(SIGTERM);
+	INSTALL_SIGNAL(SIGINT);
+
+	memset(&options, 0, sizeof(options));
+	if (read_options(argc, argv)) {
+		usage(progname);
+		goto bail;
+	}
+
+	if (!device || !options.ops || !options.file_name[0])
+		goto bail;
+
+	ret = ocfs2_open(device, OCFS2_FLAG_RW, 0, 0, &fs);
+	if (ret) {
+		com_err(progname, ret, "while opening \"%s\"", device);
+		goto bail;
+	}
+
+	ret = open_test_inode(fs, options.file_name, &inode);
+	if (ret) {
+		com_err(progname, ret, "while open test inode");
+		goto bail;
+	}
+
+	if (options.ops == CREATE)
+		ret = create_file(fs, inode, options.extras);
+	else
+		ret = ocfs2_truncate(fs, inode, options.extras);
+
+	if (ret)
+		com_err(progname, ret, "while doing the test");
+bail:
+	if (fs)
+		ocfs2_close(fs);
+
+	return ret;
+}
Index: programs/truncate/ocfs2-tools/ocfs2_truncate.sh
===================================================================
--- programs/truncate/ocfs2-tools/ocfs2_truncate.sh	(revision 0)
+++ programs/truncate/ocfs2-tools/ocfs2_truncate.sh	(revision 0)
@@ -0,0 +1,243 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 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, version 2,  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.
+# 
+# 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.
+#
+
+################################################################
+
+#
+# This script will check the function of ocfs2_truncate in ocfs2-tools.
+#
+# The main test script is truncate_test and it works like this:
+# 1. Create a file with the specified tree depth.
+
+# 2. Truncate the file to some size.
+#
+# 3. Using fsck.ocfs2 to check whether the volume is corrupted.
+#
+# Please Note that this script much have the --with-truncate option to
+# do the truncate test.
+#
+
+MKFS_BIN=`which mkfs.ocfs2`
+FSCK_BIN=`which fsck.ocfs2`
+DEBUGFS_BIN=`which debugfs.ocfs2`
+TRUNCATE_BIN=
+LOG_DIR=$PWD
+
+BLOCKDEV=`which blockdev`
+DEVICE=""
+
+FSCK_OUTPUT="/tmp/fsck.ocfs2.output"
+FSCK_OUTPUT_STANDARD="/tmp/fsck.ocfs2.output.std"
+
+#
+# usage			Display help information and exit.
+#
+function usage()
+{
+	local script="${0##*/}"
+	cat <<-EOF
+	Usage: $script [options] device
+
+	Options:
+	      --help                       display this help and exit
+	      --log-dir=DIRECTORY          use the DIRECTORY to store the log
+	      --with-fsck=PROGRAM          use the PROGRAM as fsck.ocfs2
+	      --with-mkfs=PROGRAM          use the PROGRAM as fswreck
+	      --with-debugfs=PROGRAM       use the PROGRAM as mkfs.ocfs2
+	      --with-truncate=PROGRAM      use the PROGRAM as truncate tools
+
+	Examples:
+
+	  $script --with-truncate=./test_truncate --log-dir=/tmp /dev/sdd1
+	EOF
+}
+
+#
+# warn_if_bad
+#
+#	$1	the result to check
+#	$2	the result we want
+#	$3	the error messge
+#	$4	the line num of the caller
+#
+#
+function warn_if_bad()
+{
+	local -i rc="$1"
+	local -i wanted="$2"
+	local script="${0##*/}"
+
+	# Ignore if no problems
+	[ "$rc" -eq "$wanted" ] && return 0
+
+	# Broken
+	shift
+	echo "$script: $@">&2
+	echo "$script: $@">&3
+	return "$rc"
+}
+
+#
+# exit_if_bad		Put out error message(s) if $1 has bad RC.
+#
+#	$1	the result to check
+#	$2	the result we want
+#	$3	the error messge
+#	$4	the line num of the caller
+#
+#       Exits with 1 unless $1 is 0
+#
+function exit_if_bad()
+{
+	warn_if_bad "$@" || exit 1
+	return 0
+}
+
+function set_log_file()
+{
+	mkdir -p $LOG_DIR
+	if [ ! -d $LOG_DIR ]; then
+		echo "log_dir[$LOG_DIR] not exist, use [$PWD] instead."
+		LOG_DIR=$PWD
+	fi
+	
+	output_log="$LOG_DIR/`date +%F-%H-%M-%S`-output.log"
+	exec 3>&1
+	exec 1>$output_log 2>&1
+}
+
+function truncate_test()
+{
+	local tree_depth="$1"
+
+	test_file="test$RANDOM"
+	$TRUNCATE_BIN -c $tree_depth -f $test_file $DEVICE
+
+	$FSCK_BIN -fy $DEVICE>$FSCK_OUTPUT
+	diff $FSCK_OUTPUT $FSCK_OUTPUT_STANDARD
+	exit_if_bad $? "0" "fsck find errors when creating file $test_file." $LINENO
+
+	# get the file size first.
+	new_size=`echo "stat $test_file"|$DEBUGFS_BIN $DEVICE|grep "Size:"|awk '{print $8}'`
+
+	# truncate the file for 5 times, every time truncate
+	# it to half size.
+	for((i=0;i<5;i++))
+	do
+		new_size=`expr $new_size / 2`
+		$TRUNCATE_BIN -f $test_file -s $new_size $DEVICE
+
+		$FSCK_BIN -fy $DEVICE>$FSCK_OUTPUT
+		diff $FSCK_OUTPUT $FSCK_OUTPUT_STANDARD
+		exit_if_bad $? "0" "fail to truncate file to size $new_size." $LINENO
+	done
+
+	#truncate the file to 0.
+	$TRUNCATE_BIN -f $test_file -s 0 $DEVICE
+
+	$FSCK_BIN -fy $DEVICE>$FSCK_OUTPUT
+	diff $FSCK_OUTPUT $FSCK_OUTPUT_STANDARD
+	exit_if_bad $? "0" "fail to truncate file to size $new_size." $LINENO
+}
+
+function normal_test()
+{
+	local test_file=""
+	local new_size=""
+	local -i i=0
+
+	for blocksize in 512 1024 2048 4096
+	do
+		for clustersize in \
+			4096 8192 16384 32768 65536 131072 262144 524288 1048576
+		do
+
+			dd if=/dev/zero of=$DEVICE bs=4096 count=3
+			$MKFS_BIN -b $blocksize -C $clustersize $DEVICE
+
+			#save the perfect fsck output first for our future use.
+			$FSCK_BIN -f $DEVICE>$FSCK_OUTPUT_STANDARD
+
+			truncate_test 0
+			truncate_test 1
+		done
+	done
+}
+
+################################################################
+
+#
+# main
+#
+if [ "$#" -eq "0" ]
+then
+	usage
+	exit 255
+fi
+
+while [ "$#" -gt "0" ]
+do
+	case "$1" in
+	"--help")
+		usage
+		exit 255
+		;;
+	"--log-dir="*)
+		LOG_DIR="${1#--log-dir=}"
+		;;
+	"--with-fsck="*)
+		FSCK_BIN="${1#--with-fsck=}"
+		;;
+	"--with-mkfs="*)
+		MKFS_BIN="${1#--with-mkfs=}"
+		;;
+	"--with-debugfs="*)
+		DEBUGFS_BIN="${1#--with-debugfs=}"
+		;;
+	"--with-truncate="*)
+		TRUNCATE_BIN="${1#--with-truncate=}"
+		;;
+	*)
+		DEVICE="$1"
+		;;
+	esac
+	shift
+done
+
+if [ ! -b "$DEVICE" ]; then
+	echo "invalid block device - $DEVICE"
+	usage
+	exit 1
+fi
+
+which $TRUNCATE_BIN
+if [ "$?" != "0" ]; then
+	echo "$TRUNCATE_BIN not exist, can't go on the test."
+	usage
+	exit 1
+fi
+
+set_log_file
+
+#from now on all the command and log will be recorded to the logfile.
+set -x
+
+normal_test
+
+exit 0

Property changes on: programs/truncate/ocfs2-tools/ocfs2_truncate.sh
___________________________________________________________________
Name: svn:executable
   + *

Index: programs/truncate/ocfs2-tools/Makefile
===================================================================
--- programs/truncate/ocfs2-tools/Makefile	(revision 0)
+++ programs/truncate/ocfs2-tools/Makefile	(revision 0)
@@ -0,0 +1,19 @@
+TOPDIR = ../../../
+
+include $(TOPDIR)/Preamble.make
+
+TESTS = test_truncate
+
+CFLAGS = -O2 -Wall -g
+
+SOURCES = test_truncate.c
+OBJECTS = $(patsubst %.c,%.o,$(SOURCES))
+
+DIST_FILES = $(SOURCES)
+
+BIN_PROGRAMS = test_truncate
+
+test_truncate: $(OBJECTS)
+	$(LINK)  -locfs2 -lcom_err
+
+include $(TOPDIR)/Postamble.make


More information about the Ocfs2-tools-devel mailing list