Whamcloud - gitweb
LU-5964 tests: open a large number of files at once 85/12885/10
authorAndreas Dilger <andreas.dilger@intel.com>
Fri, 28 Nov 2014 20:54:37 +0000 (13:54 -0700)
committerOleg Drokin <oleg.drokin@intel.com>
Thu, 23 Mar 2017 01:40:40 +0000 (01:40 +0000)
Modify createmany to allow keeping files open after creation to test
the total number of files that can be open at one time.  Also add a
sanity.sh test for this functionality.

Fix handling of last "-seconds" argument, but deprecate positional
parameters in favor of proper named argument parsing, since that is
more flexible and sustainable in the long term.

Order the include files alphabetically to simplify coding.
Order the argument parsing to be alphabetical to simplify coding.

Use booleans for do_* flags, and don't treat multiple of the same
argument as an error, that doesn't really improve usability at all.

Rename the "-r" option to "-u", to better match "unlinkmany", but
allow both to be accepted for the time being.  Allow "-u" (or "-r")
to be used in isolation to only delete files created in a prior run.
Print stats properly if only "-u" is specified, instead of assuming
that files are always being created in the same pass as deletion.

Print only the short program name instead of all of argv[0].  This
will also simplify multiplexing "createmany" as "unlinkmany" by name
at a later time.

Test-Parameters: trivial testlist=sanityn
Signed-off-by: Andreas Dilger <andreas.dilger@intel.com>
Change-Id: I0e8c1af65dcf0b25065f26731c694f4beaab75d7
Reviewed-on: https://review.whamcloud.com/12885
Tested-by: Jenkins
Reviewed-by: Patrick Farrell <paf@cray.com>
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Jian Yu <jian.yu@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/tests/createmany.c
lustre/tests/sanity.sh
lustre/tests/sanityn.sh

index dd60ad4..d0a3db2 100644 (file)
 /*
  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright (c) 2015 Intel Corporation.
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
  */
 
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <time.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <stdlib.h>
-#include <getopt.h>
 
-static void usage(char *prog)
+static void usage(const char *prog)
 {
-        printf("usage: %s {-o|-m|-d|-l<tgt>} [-r altpath ] filenamefmt count\n", prog);
-        printf("       %s {-o|-m|-d|-l<tgt>} [-r altpath ] filenamefmt ] -seconds\n", prog);
-        printf("       %s {-o|-m|-d|-l<tgt>} [-r altpath ] filenamefmt start count\n", prog);
-        exit(EXIT_FAILURE);
+       printf("usage: %s {-o [-k]|-m|-d|-l<tgt>} [-u[<unlinkfmt>]] "
+              "[-t seconds] filenamefmt [[start] count]\n", prog);
+       printf("\t-l\tlink files to existing <tgt> file\n"
+              "\t-m\tmknod regular files (don't create OST objects)\n"
+              "\t-o\topen+create files with path and printf format\n"
+              "\t-k\t    keep files open until all files are opened\n"
+              "\t-u\tunlink file/dir (with optional <unlinkfmt>)\n");
+       printf("\t-d\tuse directories instead of regular files\n"
+              "\t-t\tstop creating files after <seconds> have elapsed\n");
+
+       exit(EXIT_FAILURE);
 }
 
 static char *get_file_name(const char *fmt, long n, int has_fmt_spec)
 {
-        static char filename[4096];
-        int bytes;
-
-        bytes = has_fmt_spec ? snprintf(filename, 4095, fmt, n) :
-                snprintf(filename, 4095, "%s%ld", fmt, n);
-        if (bytes >= 4095) {
-                printf("file name too long\n");
-                exit(EXIT_FAILURE);
-        }
-        return filename;
+       static char filename[4096];
+       int bytes;
+
+       bytes = has_fmt_spec ? snprintf(filename, 4095, fmt, n) :
+               snprintf(filename, 4095, "%s%ld", fmt, n);
+       if (bytes >= 4095) {
+               printf("file name too long\n");
+               exit(EXIT_FAILURE);
+       }
+       return filename;
 }
 
 double now(void)
 {
-        struct timeval tv;
-        gettimeofday(&tv, NULL);
-        return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
 }
 
 int main(int argc, char ** argv)
 {
-        long i;
-        int rc = 0, do_open = 0, do_link = 0, do_mkdir = 0;
-        int do_unlink = 0, do_mknod = 0;
-        char *filename;
-        char *fmt = NULL, *fmt_unlink = NULL, *tgt = NULL;
-        double start, last;
-        long begin = 0, end = ~0UL >> 1, count = ~0UL >> 1;
-        int c, has_fmt_spec = 0, unlink_has_fmt_spec = 0;
-
-        /* Handle the last argument in form of "-seconds" */
-        if (argc > 1 && argv[argc - 1][0] == '-') {
-                char *endp;
-
-                argc--;
-                end = strtol(argv[argc] + 1, &endp, 0);
-                if (end <= 0 || *endp != '\0')
-                        usage(argv[0]);
-                end = end + time(NULL);
-        }
-
-        while ((c = getopt(argc, argv, "omdl:r:")) != -1) {
-                switch(c) {
-                case 'o':
-                        do_open++;
-                        break;
-                case 'm':
-                        do_mknod++;
-                        break;
-                case 'd':
-                        do_mkdir++;
-                        break;
-                case 'l':
-                        do_link++;
-                        tgt = optarg;
-                        break;
-                case 'r':
-                        do_unlink++;
-                        fmt_unlink = optarg;
-                        break;
-                case '?':
-                        printf("Unknown option '%c'\n", optopt);
-                        usage(argv[0]);
-                }
-        }
-
-        if (do_open + do_mkdir + do_link + do_mknod != 1 ||
-            do_unlink > 1)
-                usage(argv[0]);
-
-        switch (argc - optind) {
-        case 3:
-                begin = strtol(argv[argc - 2], NULL, 0);
-        case 2:
-                count = strtol(argv[argc - 1], NULL, 0);
-                if (end != ~0UL >> 1)
-                        usage(argv[0]);
-        case 1:
-                fmt = argv[optind];
-                break;
-        default:
-                usage(argv[0]);
-        }
-
-        start = last = now();
-
-        has_fmt_spec = strchr(fmt, '%') != NULL;
-        if (do_unlink)
-                unlink_has_fmt_spec = strchr(fmt_unlink, '%') != NULL;
-
-        for (i = 0; i < count && time(NULL) < end; i++, begin++) {
-                filename = get_file_name(fmt, begin, has_fmt_spec);
-                if (do_open) {
-                        int fd = open(filename, O_CREAT|O_RDWR, 0644);
-                        if (fd < 0) {
-                                printf("open(%s) error: %s\n", filename,
-                                       strerror(errno));
-                                rc = errno;
-                                break;
-                        }
-                        close(fd);
-                } else if (do_link) {
-                        rc = link(tgt, filename);
-                        if (rc) {
-                                printf("link(%s, %s) error: %s\n",
-                                       tgt, filename, strerror(errno));
-                                rc = errno;
-                                break;
-                        }
-                } else if (do_mkdir) {
-                        rc = mkdir(filename, 0755);
-                        if (rc) {
-                                printf("mkdir(%s) error: %s\n",
-                                       filename, strerror(errno));
-                                rc = errno;
-                                break;
-                        }
-                } else {
-                        rc = mknod(filename, S_IFREG| 0444, 0);
-                        if (rc) {
-                                printf("mknod(%s) error: %s\n",
-                                       filename, strerror(errno));
-                                rc = errno;
-                                break;
-                        }
-                }
-                if (do_unlink) {
-                        filename = get_file_name(fmt_unlink, begin,
-                                      unlink_has_fmt_spec);
-                        rc = do_mkdir ? rmdir(filename) : unlink(filename);
-                        if (rc) {
-                                printf("unlink(%s) error: %s\n",
-                                       filename, strerror(errno));
-                                rc = errno;
-                                break;
-                        }
-                }
-
-                if (i && (i % 10000) == 0) {
-                        printf(" - created %ld (time %.2f total %.2f last %.2f)"
-                               "\n", i, now(), now() - start, now() - last);
-                        last = now();
-                }
-        }
-        printf("total: %ld creates%s in %.2f seconds: %.2f creates/second\n", i,
-               do_unlink ? "/deletions" : "",
-               now() - start, ((double)i / (now() - start)));
-
-        return rc;
+       bool do_open = false, do_keep = false, do_link = false;
+       bool do_unlink = false, do_mknod = false, do_mkdir = false;
+       char *filename, *progname;
+       char *fmt = NULL, *fmt_unlink = NULL, *tgt = NULL;
+       char *endp = NULL;
+       double start, last_t, end;
+       long begin = 0, count = ~0UL >> 1;
+       int has_fmt_spec = 0, unlink_has_fmt_spec = 0;
+       long i, total, last_i = 0;
+       int c, last_fd = -1, stderr_fd;
+       int rc = 0;
+
+       /* Handle the deprecated positional last argument "-seconds" */
+       if (argc > 1 && argv[argc - 1][0] == '-' &&
+           (end = strtol(argv[argc - 1] + 1, &endp, 0)) && *endp == '\0') {
+               fprintf(stderr, "warning: '-runtime' deprecated, "
+                       "use '-t runtime' instead\n");
+               argv[--argc] = NULL;
+       } else {
+               /* Not '-number', let regular argument parsing handle it. */
+               end = ~0U >> 1;
+       }
+
+       if ((endp = strrchr(argv[0], '/')) != NULL)
+               progname = endp + 1;
+       else
+               progname = argv[0];
+
+       while ((c = getopt(argc, argv, "dl:kmor::t:u::")) != -1) {
+               switch (c) {
+               case 'd':
+                       do_mkdir = true;
+                       break;
+               case 'k':
+                       do_keep = true;
+                       break;
+               case 'l':
+                       do_link = true;
+                       tgt = optarg;
+                       break;
+               case 'm':
+                       do_mknod = true;
+                       break;
+               case 'o':
+                       do_open = true;
+                       break;
+               case 't':
+                       end = strtol(optarg, &endp, 0);
+                       if (end <= 0.0 || *endp != '\0')
+                               usage(progname);
+                       break;
+               case 'r':
+               case 'u':
+                       do_unlink = true;
+                       fmt_unlink = optarg;
+                       break;
+               case '?':
+                       fprintf(stderr, "Unknown option '%c'\n", optopt);
+                       usage(progname);
+               }
+       }
+
+       if (do_open + do_mkdir + do_link + do_mknod > 1 ||
+           do_open + do_mkdir + do_link + do_mknod + do_unlink == 0) {
+               fprintf(stderr, "error: only one of -o, -m, -l, -d\n");
+               usage(progname);
+       }
+
+       if (!do_open && do_keep) {
+               fprintf(stderr, "error: can only use -k with -o\n");
+               usage(progname);
+       }
+
+       switch (argc - optind) {
+       case 3:
+               begin = strtol(argv[argc - 2], NULL, 0);
+       case 2:
+               count = strtol(argv[argc - 1], NULL, 0);
+       case 1:
+               fmt = argv[optind];
+               break;
+       default:
+               usage(progname);
+       }
+
+       has_fmt_spec = strchr(fmt, '%') != NULL;
+       if (fmt_unlink != NULL)
+               unlink_has_fmt_spec = strchr(fmt_unlink, '%') != NULL;
+
+       for (i = 0, start = last_t = now(), end += start;
+            i < count && now() < end; i++, begin++) {
+               filename = get_file_name(fmt, begin, has_fmt_spec);
+               if (do_open) {
+                       int fd = open(filename, O_CREAT|O_RDWR, 0644);
+                       if (fd < 0) {
+                               printf("open(%s) error: %s\n", filename,
+                                      strerror(errno));
+                               rc = errno;
+                               break;
+                       }
+                       if (!do_keep)
+                               close(fd);
+                       else if (fd > last_fd)
+                               last_fd = fd;
+               } else if (do_link) {
+                       rc = link(tgt, filename);
+                       if (rc) {
+                               printf("link(%s, %s) error: %s\n",
+                                      tgt, filename, strerror(errno));
+                               rc = errno;
+                               break;
+                       }
+               } else if (do_mkdir) {
+                       rc = mkdir(filename, 0755);
+                       if (rc) {
+                               printf("mkdir(%s) error: %s\n",
+                                      filename, strerror(errno));
+                               rc = errno;
+                               break;
+                       }
+               } else if (do_mknod) {
+                       rc = mknod(filename, S_IFREG | 0444, 0);
+                       if (rc) {
+                               printf("mknod(%s) error: %s\n",
+                                      filename, strerror(errno));
+                               rc = errno;
+                               break;
+                       }
+               }
+               if (do_unlink) {
+                       if (fmt_unlink != NULL)
+                               filename = get_file_name(fmt_unlink, begin,
+                                                        unlink_has_fmt_spec);
+
+                       rc = do_mkdir ? rmdir(filename) : unlink(filename);
+                       if (rc) {
+                               printf("unlink(%s) error: %s\n",
+                                      filename, strerror(errno));
+                               rc = errno;
+                               break;
+                       }
+               }
+
+               if ((i != 0 && (i % 10000) == 0) || now() - last_t >= 10.0) {
+                       double tmp = now();
+
+                       printf(" - %s%s %ld (time %.2f total %.2f last %.2f)"
+                              "\n",
+                              do_open ? do_keep ? "open/keep" : "open/close" :
+                                       do_mkdir ? "mkdir" : do_link ? "link" :
+                                       do_mknod ? "create" : "",
+                              do_unlink ? do_mkdir ? "/rmdir" : "/unlink" : "",
+                              i, tmp, tmp - start,
+                              (i - last_i) / (tmp - last_t));
+                       last_t = tmp;
+                       last_i = i;
+               }
+       }
+       last_t = now();
+       total = i;
+       printf("total: %ld %s%s in %.2f seconds: %.2f ops/second\n", total,
+              do_open ? do_keep ? "open/keep" : "open/close" :
+                       do_mkdir ? "mkdir" : do_link ? "link" :
+                                            do_mknod ? "create" : "",
+              do_unlink ? do_mkdir ? "/rmdir" : "/unlink" : "",
+              last_t - start, ((double)total / (last_t - start)));
+
+       if (!do_keep)
+               return rc;
+
+       stderr_fd = fileno(stderr);
+       start = last_t;
+       /* Assume fd is allocated in order, doing extra closes is not harmful */
+       for (i = 0; i < total && last_fd > stderr_fd; i++, --last_fd) {
+               close(last_fd);
+
+               if ((i != 0 && (i % 10000) == 0) || now() - last_t >= 10.0) {
+                       double tmp = now();
+
+                       printf(" - closed %ld (time %.2f total %.2f last %.2f)"
+                              "\n", i, tmp, tmp - start,
+                              (i - last_i) / (tmp - last_t));
+                       last_t = tmp;
+                       last_i = i;
+               }
+       }
+       last_t = now();
+
+       printf("total: %ld close in %.2f seconds: %.2f close/second\n",
+              total, last_t - start, ((double)total / (last_t - start)));
+       return rc;
 }
index 91e20a1..b0921f5 100755 (executable)
@@ -3644,7 +3644,7 @@ test_42a() {
                error "$BEFOREWRITES < $AFTERWRITES"
        start_writeback
 }
-run_test 42a "ensure that we don't flush on close =============="
+run_test 42a "ensure that we don't flush on close"
 
 test_42b() {
        [ $PARALLEL == "yes" ] && skip "skip parallel run" && return
@@ -3805,56 +3805,53 @@ test_43A() { # was test_43
        # give multiop a chance to open
        sleep 1
 
-       $DIR/$tdir/$tfile && error || true
+       $DIR/$tdir/$tfile && error "execute $DIR/$tdir/$tfile succeeded" || true
        kill -USR1 $pid
 }
 run_test 43A "execution of file opened for write should return -ETXTBSY"
 
 test_43a() {
-       [ $PARALLEL == "yes" ] && skip "skip parallel run" && return
-       test_mkdir -p $DIR/$tdir
-       cp -p `which $MULTIOP` $DIR/$tdir/multiop ||
-                       cp -p multiop $DIR/$tdir/multiop
+       test_mkdir $DIR/$tdir
+       cp -p $(which $MULTIOP) $DIR/$tdir/multiop ||
+               cp -p multiop $DIR/$tdir/multiop
        MULTIOP_PROG=$DIR/$tdir/multiop multiop_bg_pause $TMP/$tfile.junk O_c ||
-                       return 1
-        MULTIOP_PID=$!
-        $MULTIOP $DIR/$tdir/multiop Oc && error "expected error, got success"
-        kill -USR1 $MULTIOP_PID || return 2
-        wait $MULTIOP_PID || return 3
-       rm $TMP/$tfile.junk $DIR/$tdir/multiop
+               error "multiop open $TMP/$tfile.junk failed"
+       MULTIOP_PID=$!
+       $MULTIOP $DIR/$tdir/multiop Oc && error "expected error, got success"
+       kill -USR1 $MULTIOP_PID || error "kill -USR1 PID $MULTIOP_PID failed"
+       wait $MULTIOP_PID || error "wait PID $MULTIOP_PID failed"
 }
 run_test 43a "open(RDWR) of file being executed should return -ETXTBSY"
 
 test_43b() {
        [ $PARALLEL == "yes" ] && skip "skip parallel run" && return
-       test_mkdir -p $DIR/$tdir
-       cp -p `which $MULTIOP` $DIR/$tdir/multiop ||
-                       cp -p multiop $DIR/$tdir/multiop
+       test_mkdir $DIR/$tdir
+       cp -p $(which $MULTIOP) $DIR/$tdir/multiop ||
+               cp -p multiop $DIR/$tdir/multiop
        MULTIOP_PROG=$DIR/$tdir/multiop multiop_bg_pause $TMP/$tfile.junk O_c ||
-                       return 1
-        MULTIOP_PID=$!
-        $TRUNCATE $DIR/$tdir/multiop 0 && error "expected error, got success"
-        kill -USR1 $MULTIOP_PID || return 2
-        wait $MULTIOP_PID || return 3
-       rm $TMP/$tfile.junk $DIR/$tdir/multiop
+               error "multiop open $TMP/$tfile.junk failed"
+       MULTIOP_PID=$!
+       $TRUNCATE $DIR/$tdir/multiop 0 && error "expected error, got success"
+       kill -USR1 $MULTIOP_PID || error "kill -USR1 PID $MULTIOP_PID failed"
+       wait $MULTIOP_PID || error "wait PID $MULTIOP_PID failed"
 }
 run_test 43b "truncate of file being executed should return -ETXTBSY"
 
 test_43c() {
        local testdir="$DIR/$tdir"
-       test_mkdir -p $DIR/$tdir
+       test_mkdir $testdir
        cp $SHELL $testdir/
-       ( cd $(dirname $SHELL) && md5sum $(basename $SHELL) ) | \
-               ( cd $testdir && md5sum -c)
+       ( cd $(dirname $SHELL) && md5sum $(basename $SHELL) ) |
+               ( cd $testdir && md5sum -c )
 }
-run_test 43c "md5sum of copy into lustre========================"
+run_test 43c "md5sum of copy into lustre"
 
 test_44A() { # was test_44
        [[ $OSTCOUNT -lt 2 ]] && skip_env "skipping 2-stripe test" && return
        dd if=/dev/zero of=$DIR/f1 bs=4k count=1 seek=1023
        dd if=$DIR/f1 bs=4k count=1 > /dev/null
 }
-run_test 44A "zero length read from a sparse stripe ============="
+run_test 44A "zero length read from a sparse stripe"
 
 test_44a() {
        local nstripe=$($LCTL lov_getconfig $DIR | grep default_stripe_count: |
@@ -4236,6 +4233,35 @@ test_51e() {
 }
 run_test 51e "check file nlink limit"
 
+test_51f() {
+       test_mkdir $DIR/$tdir
+
+       local max=100000
+       local ulimit_old=$(ulimit -n)
+       local spare=20 # number of spare fd's for scripts/libraries, etc.
+       local mdt=$(lfs getstripe -M $DIR/$tdir)
+       local numfree=$(lfs df -i $DIR/$tdir | awk '/MDT:'$mdt'/ { print $4 }')
+
+       echo "MDT$mdt numfree=$numfree, max=$max"
+       [[ $numfree -gt $max ]] && numfree=$max || numfree=$((numfree * 7 / 8))
+       if [ $((numfree + spare)) -gt $ulimit_old ]; then
+               while ! ulimit -n $((numfree + spare)); do
+                       numfree=$((numfree * 3 / 4))
+               done
+               echo "changed ulimit from $ulimit_old to $((numfree + spare))"
+       else
+               echo "left ulimit at $ulimit_old"
+       fi
+
+       createmany -o -k -t 120 $DIR/$tdir/f $numfree ||
+               error "create+open $numfree files in $DIR/$tdir failed"
+       ulimit -n $ulimit_old
+
+       # if createmany exits at 120s there will be fewer than $numfree files
+       unlinkmany $DIR/$tdir/f $numfree || true
+}
+run_test 51f "check many open files limit"
+
 test_52a() {
        [ -f $DIR/$tdir/foo ] && chattr -a $DIR/$tdir/foo
        test_mkdir -p $DIR/$tdir
index 7f99537..4fd09a2 100644 (file)
@@ -876,7 +876,7 @@ test_33a() {
 
             [ $fstype = ldiskfs ] && jbdold=$(print_jbd_stat)
             echo "=== START createmany old: $jbdold transaction"
-            local elapsed=$(do_and_time "do_nodes $CLIENT1,$CLIENT2 createmany -o $DIR1/$tdir-\\\$(hostname)-$i/f- -r $DIR2/$tdir-\\\$(hostname)-$i/f- $nfiles > /dev/null 2>&1")
+            local elapsed=$(do_and_time "do_nodes $CLIENT1,$CLIENT2 createmany -o $DIR1/$tdir-\\\$(hostname)-$i/f- -r$DIR2/$tdir-\\\$(hostname)-$i/f- $nfiles > /dev/null 2>&1")
             [ $fstype = ldiskfs ] && jbdnew=$(print_jbd_stat)
             [ $fstype = ldiskfs ] && jbd=$(( jbdnew - jbdold ))
             echo "=== END   createmany new: $jbdnew transaction :  $jbd transactions  nfiles $nfiles time $elapsed COS=$COS"
@@ -932,7 +932,7 @@ test_33b() {
                        echo "=== START createmany old: $jbdold transaction"
                        local elapsed=$(do_and_time "do_nodes $CLIENT1,$CLIENT2\
                                createmany -o $DIR1/$tdir-\\\$(hostname)-$i/f- \
-                               -r $DIR2/$tdir-\\\$(hostname)-$i/f- $nfiles > \
+                               -r$DIR2/$tdir-\\\$(hostname)-$i/f- $nfiles > \
                                                                /dev/null 2>&1")
                        jbdnew=$(print_jbd_stat)
                        jbd=$(( jbdnew - jbdold ))
@@ -2955,7 +2955,7 @@ test_76() { #LU-946
 
        rm -rf $DIR/$tdir
 }
-run_test 76 "Verify open file for 2048 files"
+run_test 76 "Verify MDS open_files tracking for 2048 files"
 
 nrs_write_read() {
        local n=16