[Ocfs2-tools-devel] Patch for journal truncate of ocfs2-tools.
Sunil Mushran
Sunil.Mushran at oracle.com
Wed May 9 15:50:08 PDT 2007
Looks good.
Marcos, Please test and provide feedback to Tao.
tao.ma wrote:
> 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
>>
>>
>
> ------------------------------------------------------------------------
>
> 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