Whamcloud - gitweb
Land b1_8_gate onto b1_8 (20081218_1708)
[fs/lustre-release.git] / lustre / tests / mdsrate.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * 2003, Copyright, Hewlett-Packard Development Compnay, LP.
5  *
6  * Developed under the sponsorship of the U.S. Government
7  *     under Subcontract No. B514193
8  */
9
10 /*
11  * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
12  * Use is subject to license terms.
13  */
14
15 #include <stdio.h>
16 #include <getopt.h>
17 #include <libgen.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <time.h>
21 #include <limits.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <signal.h>
29 #include <sys/ioctl.h>
30 #include <dirent.h>
31
32 #include "mpi.h"
33
34 /* lustre */
35 #include <lustre/liblustreapi.h>        /* for O_LOV_DELAY_CREATE */
36
37 #define CHECK_COUNT 10000
38 #define DISPLAY_COUNT (CHECK_COUNT * 10)
39 #define DISPLAY_TIME 100
40
41 enum {
42         CREATE   = 'c',
43         LOOKUP   = 'l',
44         MKNOD    = 'm',
45         OPEN     = 'o',
46         STAT     = 's',
47         UNLINK   = 'u',
48         BEGIN    = 'b',
49         ITERS    = 'i',
50         TIME     = 't',
51         DIRFMT   = 'd',
52         NDIRS    = 'D',
53         FILEFMT  = 'f',
54         NFILES   = 'F',
55         NOEXCL   = 'X',
56         STRIPES  = 'S',
57         SEED     = 'r',
58         SEEDFILE = 'R',
59         RANDOM   = 'A',
60         READDIR  = 'B',
61         RECREATE = 'C',
62         VERBOSE  = 'V',
63         DEBUG    = 'v',
64         HELP     = 'h',
65 };
66
67 struct option longOpts[] = {
68         {"create",        0, NULL, CREATE     },
69         {"lookup",        0, NULL, LOOKUP     },
70         {"mknod",         0, NULL, MKNOD      },
71         {"open",          0, NULL, OPEN       },
72         {"stat",          0, NULL, STAT       },
73         {"unlink",        0, NULL, UNLINK     },
74         {"begin",         1, NULL, BEGIN      },
75         {"iters",         1, NULL, ITERS      },
76         {"time",          1, NULL, TIME       },   /* seconds */
77         {"dirfmt",        1, NULL, DIRFMT     },
78         {"ndirs",         1, NULL, NDIRS      },
79         {"filefmt",       1, NULL, FILEFMT    },
80         {"nfiles",        1, NULL, NFILES     },
81         {"noexcl",        0, NULL, NOEXCL     },
82         {"stripes",       1, NULL, STRIPES    },
83         {"seed",          1, NULL, SEED       },
84         {"seedfile",      1, NULL, SEEDFILE   },
85         {"random_order",  0, NULL, RANDOM     },
86         {"readdir_order", 0, NULL, READDIR    },
87         {"recreate",      0, NULL, RECREATE   },
88         {"verbose",       0, NULL, VERBOSE    },
89         {"debug",         0, NULL, DEBUG      },
90         {"help",          0, NULL, HELP       },
91         { 0,              0, NULL, 0          }
92 };
93
94 int foo1, foo2;
95
96 char   shortOpts[128];
97 int    myrank = -1;
98 int    nthreads = -1;
99 char * prog;
100 char   hostname[512] = "unknown";
101 char   mode;
102 char * cmd;
103 int    openflags = O_RDWR|O_CREAT|O_EXCL;
104 int    ndirs = 1;
105 char * dirfmt;
106 char   dir[PATH_MAX];
107 char   mkdir_cmd[PATH_MAX+14];
108 int    dirthreads;
109 int    dirnum;
110 DIR *  directory;
111 struct dirent *dir_entry;
112 int    nfiles;
113 char   filefmt[PATH_MAX];
114 char   filename[PATH_MAX];
115 int    stripes = -1;
116 int    begin;
117 int    beginsave;
118 int    end;
119 int    iters;
120 int    seconds;
121 int    alarm_caught;
122 struct sigaction act;
123 int    order = RANDOM;
124 int    seed;
125 int    recreate;
126 int    verbose;
127 int    debug;
128 struct stat statbuf;
129
130 #define dmesg if (debug) printf
131
132 #define DISPLAY_PROGRESS() {                                                \
133         if ((++nops % CHECK_COUNT) == 0 && verbose) {                       \
134                 curTime = time(0);                                          \
135                 interval = curTime - lastTime;                              \
136                 if (interval > DISPLAY_TIME || nops % DISPLAY_COUNT == 0) { \
137                         rate = (float)(nops - lastOps);                     \
138                         if (interval > 1)                                   \
139                                 rate /= (float)interval;                    \
140                         printf("Rank %d: %.2f %ss/sec %lu secs "            \
141                                "(total: %d %ss %lu secs)\n",                \
142                                myrank, rate, cmd, interval,                 \
143                                nops, cmd, curTime - startTime);             \
144                         lastOps = nops;                                     \
145                         lastTime = curTime;                                 \
146                 }                                                           \
147         }                                                                   \
148 }
149
150 char *usage_msg = "usage: %s\n"
151                   "    { --create [ --noexcl ] | --lookup | --mknod |\n"
152                   "      --open | --stat | --unlink  [ --recreate ] }\n"
153                   "    [ --help ] [ --verbose ] [ --debug ]\n"
154                   "    { [ --begin <num> ] --nfiles <num> }\n"
155                   "    [ --iters <num> ] [ --time <secs> ]\n"
156                   "    [ --dirfmt <str> ] [ --ndirs  <num> ]\n"
157                   "    [ --filefmt <str> ] [ --stripes <num> ]\n"
158                   "    [ --random_order [--seed <num> | --seedfile <file>] ]\n"
159                   "    [ --readdir_order ]\n";
160
161 static void
162 usage(FILE *stream, char *fmt, ...)
163 {
164         if (myrank == 0) {
165                 if (fmt != NULL) {
166                         va_list       ap;
167
168                         fprintf(stream, "%s: ", prog);
169                         va_start(ap, fmt);
170                         vfprintf(stderr, fmt, ap);
171                         va_end(ap);
172                 }
173                 fprintf(stream, usage_msg, prog);
174         }
175
176         MPI_Finalize();
177         exit(stream == stderr);
178 }
179
180 /* Print process myrank and message, and exit (i.e. a fatal error) */
181 static int
182 fatal(int rank, const char *fmt, ...)
183 {
184         if (rank == myrank) {
185                 va_list       ap;
186
187                 fprintf(stderr, "rank %d: ", rank);
188                 va_start(ap, fmt);
189                 vfprintf(stderr, fmt, ap);
190                 va_end(ap);
191         }
192
193         MPI_Abort(MPI_COMM_WORLD, 1);
194         exit(1);
195 }
196
197 static void
198 sigalrm_handler(int signum)
199 {
200         alarm_caught++;
201 }
202
203 /* HAVE_LLAPI_FILE_LOOKUP is defined by liblustreapi.h if this function is
204  * defined therein.  Otherwise we can do the equivalent operation via ioctl
205  * if we have access to a complete lustre build tree to get the various
206  * definitions - then compile with USE_MDC_LOOKUP defined. */
207 #if defined(HAVE_LLAPI_FILE_LOOKUP)
208 #define HAVE_MDC_LOOKUP
209 #elif defined(USE_MDC_LOOKUP)
210 #include <config.h>
211 #include <liblustre.h>
212 #include <linux/lustre_lib.h>
213
214 int llapi_file_lookup(int dirfd, const char *name)
215 {
216         struct obd_ioctl_data data = { 0 };
217         char rawbuf[8192];
218         char *buf = rawbuf;
219         int rc;
220
221         if (dirfd < 0 || name == NULL)
222                 return -EINVAL;
223
224         data.ioc_version = OBD_IOCTL_VERSION;
225         data.ioc_len = sizeof(data);
226         data.ioc_inlbuf1 = name;
227         data.ioc_inllen1 = strlen(name) + 1;
228
229         rc = obd_ioctl_pack(&data, &buf, sizeof(rawbuf));
230         if (rc) {
231                 fatal(myrank, "ioctl_pack failed: rc = %d\n", rc);
232                 return rc;
233         }
234
235         return ioctl(fd, IOC_MDC_LOOKUP, buf);
236 }
237 #define HAVE_MDC_LOOKUP
238 #endif
239
240 static void
241 process_args(int argc, char *argv[])
242 {
243         char   c, *cp, *endptr;
244         int    i, index, offset, tmpend, rc;
245         char   tmp[16];
246         FILE * seed_file;
247         struct option *opt;
248
249         setbuf(stdout, 0);
250         setbuf(stderr, 0);
251         prog = basename(argv[0]);
252         strcpy(filefmt, "f%d");
253         gethostname(hostname, sizeof(hostname));
254
255         /* auto create shortOpts rather than maintaining a static string. */
256         for (opt = longOpts, cp = shortOpts; opt->name != NULL; opt++, cp++) {
257                 *cp = opt->val;
258                 if (opt->has_arg)
259                         *++cp = ':';
260         }
261
262         while ((c = getopt_long(argc,argv, shortOpts, longOpts,&index)) != -1) {
263                 switch (c) {
264                 case OPEN:
265                         openflags &= ~(O_CREAT|O_EXCL);
266                 case CREATE:
267 #ifdef HAVE_MDC_LOOKUP
268                 case LOOKUP:
269 #endif
270                 case MKNOD:
271                 case STAT:
272                 case UNLINK:
273                         if (cmd != NULL) {
274                                 fatal(0, "Invalid - more than one operation "
275                                            "specified: --%s\n",
276                                         longOpts[index].name);
277                         }
278                         mode = c;
279                         cmd = (char *)longOpts[index].name;
280                         break;
281                 case NOEXCL:
282                         if (mode != CREATE && mode != MKNOD) {
283                                 usage(stderr, "--noexcl only applies to "
284                                               "--create or --mknod.\n");
285                         }
286                         openflags &= ~O_EXCL;
287                         break;
288                 case RECREATE:
289                         if (mode != UNLINK) {
290                                 usage(stderr, "--recreate only makes sense"
291                                               "with --unlink.\n");
292                         }
293                         recreate++;
294                         break;
295                 case BEGIN:
296                         begin = strtol(optarg, &endptr, 0);
297                         if ((*endptr != 0) || (begin < 0)) {
298                                 fatal(0, "Invalid --start value.\n");
299                         }
300                         break;
301                 case ITERS:
302                         iters = strtol(optarg, &endptr, 0);
303                         if ((*endptr != 0) || (iters <= 0)) {
304                                 fatal(0, "Invalid --iters value.\n");
305                         }
306                         if (mode != LOOKUP && mode != OPEN && mode != STAT) {
307                                 usage(stderr, "--iters only makes sense with "
308                                               "--lookup, --open, or --stat.\n");
309                         }
310                         break;
311                 case TIME:
312                         seconds = strtol(optarg, &endptr, 0);
313                         if ((*endptr != 0) || (seconds <= 0)) {
314                                 fatal(0, "Invalid --time value.\n");
315                         }
316                         break;
317                 case DIRFMT:
318                         if (strlen(optarg) > (PATH_MAX - 16)) {
319                                 fatal(0, "--dirfmt too long\n");
320                         }
321                         dirfmt = optarg;
322                         break;
323                 case NDIRS:
324                         ndirs = strtol(optarg, &endptr, 0);
325                         if ((*endptr != 0) || (ndirs <= 0)) {
326                                 fatal(0, "Invalid --ndirs value.\n");
327                         }
328                         if ((ndirs > nthreads) &&
329                             ((mode == CREATE) || (mode == MKNOD))) {
330                                 fatal(0, "--ndirs=%d must be less than or "
331                                       "equal to the number of threads (%d).\n",
332                                       ndirs, nthreads);
333                         }
334                         break;
335                 case FILEFMT:
336                         if (strlen(optarg) > 4080) {
337                                 fatal(0, "--filefmt too long\n");
338                         }
339
340                         /* Use %%d where you want the file # in the name. */
341                         sprintf(filefmt, optarg, myrank);
342                         break;
343                 case NFILES:
344                         nfiles = strtol(optarg, &endptr, 0);
345                         if ((*endptr != 0) || (nfiles <= 0)) {
346                                 fatal(0, "Invalid --nfiles value.\n");
347                         }
348                         break;
349                 case STRIPES:
350                         stripes = strtol(optarg, &endptr, 0);
351                         if ((*endptr != 0) || (stripes < 0)) {
352                                 fatal(0, "Invalid --stripes value.\n");
353                         }
354
355                         if (stripes == 0) {
356                                 openflags |= O_LOV_DELAY_CREATE;
357                         } else {
358                                 fatal(0, "non-zero --stripes value "
359                                          "not yet supported.\n");
360                         }
361
362                         break;
363                 case SEED:
364                         seed = strtoul(optarg, &endptr, 0);
365                         if (*endptr) {
366                                 fatal(0, "bad --seed option %s\n", optarg);
367                         }
368                         break;
369                 case SEEDFILE:
370                         seed_file = fopen(optarg, "r");
371                         if (!seed_file) {
372                               fatal(myrank, "fopen(%s) error: %s\n",
373                                       optarg, strerror(errno));
374                         }
375
376                         for (i = -1; fgets(tmp, 16, seed_file) != NULL;) {
377                                 if (++i == myrank)
378                                         break;
379                         }
380
381                         if (i == myrank) {
382                                 rc = sscanf(tmp, "%d", &seed);
383                                 if ((rc != 1) || (seed < 0)) {
384                                         fatal(myrank, "Invalid seed value '%s' "
385                                               "at line %d in %s.\n",
386                                               tmp, i, optarg);
387                                 }
388                         } else {
389                                 fatal(myrank, "File '%s' too short. Does not "
390                                       "contain a seed for thread %d.\n",
391                                       optarg, myrank);
392                         }
393
394                         fclose(seed_file);
395                         break;
396                 case RANDOM:
397                 case READDIR:
398                         if (mode != LOOKUP && mode != OPEN && mode != STAT)  {
399                                 fatal(0, "--%s can only be specified with "
400                                          "--lookup, --open, or --stat.\n",
401                                       (char *)longOpts[index].name);
402                         }
403                         order = c;
404                         break;
405                 case DEBUG:
406                         ++debug;
407                 case VERBOSE:
408                         ++verbose;
409                         break;
410                 case HELP:
411                         usage(stdout, NULL);
412                 default:
413                         usage(stderr, "unrecognized option: '%c'.\n", optopt);
414                 }
415         }
416
417         if (optind < argc) {
418                 usage(stderr, "too many arguments %d >= %d.\n", optind, argc);
419         }
420
421         if (mode == CREATE || mode == MKNOD || mode == UNLINK) {
422                 if (seconds != 0) {
423                         if (nfiles == 0)
424                                 nfiles = INT_MAX;
425                 } else if (nfiles == 0) {
426                         usage(stderr, "--nfiles or --time must be specified "
427                                       "with %s.\n", cmd);
428                 }
429         } else if (mode == LOOKUP || mode == OPEN || mode == STAT) {
430                 if (seconds != 0) {
431                         if (iters == 0)
432                                 iters = INT_MAX;
433                 } else if (iters == 0) {
434                         usage(stderr, "--iters or --time must be specifed "
435                                       "with %s.\n", cmd);
436                 }
437
438                 if (nfiles == 0) {
439                         usage(stderr, "--nfiles must be specifed with --%s.\n",
440                               cmd);
441                 }
442
443                 if (seed == 0) {
444                         int fd = open("/dev/urandom", O_RDONLY);
445
446                         if (fd >= 0) {
447                                 if (read(fd, &seed, sizeof(seed)) <
448                                     sizeof(seed))
449                                         seed = time(0);
450                                 close(fd);
451                         } else {
452                                 seed = time(0);
453                         }
454                 }
455
456                 srand(seed);
457
458                 dmesg("%s: rank %d seed %d (%s).\n", prog, myrank, seed,
459                       (order == RANDOM) ? "random_order" : "readdir_order");
460         } else {
461                 usage(stderr, "one --create, --mknod, --open, --stat,"
462 #ifdef HAVE_MDC_LOOKUP
463                       " --lookup,"
464 #endif
465                       " or --unlink must be specifed.");
466         }
467
468         /* support for multiple threads in a dir, set begin/end appropriately.*/
469         dirnum = myrank % ndirs;
470         dirthreads = nthreads / ndirs;
471         if (nthreads > (ndirs * dirthreads + dirnum))
472                 ++dirthreads;
473
474         offset = myrank / ndirs;
475
476         tmpend = begin + nfiles - 1;
477         if (tmpend <= 0)
478                 tmpend = INT_MAX;
479
480         end = begin + (nfiles / dirthreads) * dirthreads + offset;
481         if ((end > tmpend) || (end <= 0))
482                 end -= dirthreads;
483
484         begin += offset;
485         if (begin < 0)
486                 begin = INT_MAX;
487
488         beginsave = begin;
489
490         dmesg("%d: iters %d nfiles %d time %d begin %d end %d dirthreads %d."
491               "\n", myrank, iters, nfiles, seconds, begin, end, dirthreads);
492
493         if (dirfmt == NULL) {
494                 strcpy(dir, ".");
495         } else {
496                 sprintf(dir, dirfmt, dirnum);
497
498                 sprintf(mkdir_cmd, "/bin/mkdir -p %s", dir);
499                 #ifdef _LIGHTWEIGHT_KERNEL
500                         printf("NOTICE: not running system(%s)\n", mkdir_cmd);
501                 #else
502                         rc = system(mkdir_cmd);
503                         if (rc) {
504                                 fatal(myrank, "'%s' failed.\n", mkdir_cmd);
505                         }
506                 #endif
507
508                 rc = chdir(dir);
509                 if (rc) {
510                         fatal(myrank, "unable to chdir to '%s'.\n", dir);
511                 }
512         }
513 }
514
515 static inline char *next_file()
516 {
517         if (order == RANDOM) {
518                 sprintf(filename, filefmt, random() % nfiles);
519                 return(filename);
520         }
521
522         /* readdir order */
523
524         dir_entry = readdir(directory);
525         if (dir_entry == NULL) {
526                 rewinddir(directory);
527                 while ((dir_entry = readdir(directory)) != NULL) {
528                         if (dir_entry->d_name[0] != '.')
529                                 return(dir_entry->d_name);
530                 }
531
532                 fatal(myrank, "unable to read directory %s (%s).\n",
533                       dir, strerror(errno));
534         }
535
536         return(dir_entry->d_name);
537 }
538
539 int
540 main(int argc, char *argv[])
541 {
542         int    i, j, fd, rc, nops, lastOps, ag_ops;
543         float  rate, ag_rate;
544         time_t startTime, lastTime, curTime, interval;
545         char * file;
546
547         rc = MPI_Init(&argc, &argv);
548         if (rc != MPI_SUCCESS)
549                 fatal(myrank, "MPI_Init failed: %d\n", rc);
550
551         rc = MPI_Comm_size(MPI_COMM_WORLD, &nthreads);
552         if (rc != MPI_SUCCESS)
553                 fatal(myrank, "MPI_Comm_size failed: %d\n", rc);
554
555         rc = MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
556         if (rc != MPI_SUCCESS)
557                 fatal(myrank, "MPI_Comm_rank failed: %d\n", rc);
558
559         process_args(argc, argv);
560
561         startTime = time(0);
562         if ((myrank == 0) || debug) {
563                 printf("%d: %s starting at %s",
564                        myrank, hostname, ctime(&startTime));
565         }
566
567         /* if we're not measuring creation rates then precreate
568          * the files we're operating on. */
569         if ((mode != CREATE) && (mode != MKNOD)) {
570                 /* create the files in reverse order. When we encounter
571                  * a file that already exists, assume the remainder of 
572                  * the files exist to save time. The timed performance
573                  * test scripts make use of this behavior. */
574                 for (i = end, j = 0; i >= begin; i -= dirthreads) {
575                         sprintf(filename, filefmt, i);
576                         fd = open(filename, openflags, 0644);
577                         if (fd < 0) {
578                                 if (errno == EEXIST)
579                                         break;
580                                 rc = errno;
581                                 fatal(myrank, "precreate open(%s) error: %s\n",
582                                       filename, strerror(rc));
583                         }
584                         j++;
585                         close(fd);
586                 }
587                 dmesg("%d: %s pre-created %d files.\n",myrank,hostname,j);
588
589                 rc = MPI_Barrier(MPI_COMM_WORLD);
590                 if (rc != MPI_SUCCESS)
591                         fatal(myrank, "prep MPI_Barrier failed: %d\n", rc);
592         }
593
594         if (order == READDIR) {
595                 directory = opendir(dir);
596                 if (directory == NULL) {
597                         rc = errno;
598                         fatal(myrank, "opendir(%s) error: %s\n",
599                               dir, strerror(rc));
600                 }
601
602                 startTime = time(0);
603                 j = random() % nfiles;
604                 dmesg("%d: %s initializing dir offset %u: %s",
605                       myrank, hostname, j, ctime(&startTime));
606
607                 for (i = 0; i <= j; i++) {
608                         if ((dir_entry = readdir(directory)) == NULL) {
609                                 fatal(myrank, "could not read entry number %d "
610                                       "in directory %s.\n", i, dir);
611                         }
612                 }
613
614                 lastTime = time(0);
615                 dmesg("%d: index %d, filename %s, offset %ld: "
616                       "%s initialization complete: %s",
617                       myrank, i, dir_entry->d_name, telldir(directory),
618                       hostname, ctime(&lastTime));
619         }
620
621         rc = MPI_Barrier(MPI_COMM_WORLD);
622         if (rc != MPI_SUCCESS)
623                 fatal(myrank, "prep MPI_Barrier failed: %d\n", rc);
624
625         if (seconds) {
626                 act.sa_handler = sigalrm_handler;
627                 (void)sigemptyset(&act.sa_mask);
628                 act.sa_flags = 0;
629                 sigaction(SIGALRM, &act, NULL);
630                 alarm(seconds);
631         }
632
633         startTime = lastTime = time(0);
634         nops = lastOps = 0;
635
636         switch (mode) {
637         case CREATE:
638                 for (; begin <= end && !alarm_caught; begin += dirthreads) {
639                         sprintf(filename, filefmt, begin);
640                         if ((fd = open(filename, openflags, 0644)) < 0) {
641                                 if (((rc = errno) == EINTR) && alarm_caught)
642                                         break;
643                                 fatal(myrank, "open(%s) error: %s\n",
644                                       filename, strerror(rc));
645                         }
646
647                         close(fd);
648                         DISPLAY_PROGRESS();
649                 }
650
651                 dmesg("%d: created %d files, last file '%s'.\n",
652                       myrank, nops, filename);
653                 break;
654 #ifdef HAVE_MDC_LOOKUP
655         case LOOKUP:
656                 fd = open(dir, O_RDONLY);
657                 if (fd < 0) {
658                         fatal(myrank, "open(dir == '%s') error: %s\n",
659                               dir, strerror(errno));
660                 }
661
662                 for (; nops < iters && !alarm_caught;) {
663                         char *filename = next_file();
664                         rc = llapi_file_lookup(fd, filename);
665                         if (rc < 0) {
666                                 if (((rc = errno) == EINTR) && alarm_caught)
667                                         break;
668                                 fatal(myrank, "llapi_file_lookup(%s) "
669                                       "error: %s\n", filename, strerror(rc));
670                         }
671
672                         DISPLAY_PROGRESS();
673                 }
674                 break;
675 #endif
676         case MKNOD:
677                 for (; begin <= end && !alarm_caught; begin += dirthreads) {
678                         sprintf(filename, filefmt, begin);
679                         rc = mknod(filename, S_IFREG| 0644, 0);
680                         if (rc) {
681                                 if (((rc = errno) == EINTR) && alarm_caught)
682                                         break;
683                                 fatal(myrank, "mknod(%s) error: %s\n",
684                                       filename, strerror(rc));
685                         }
686
687                         DISPLAY_PROGRESS();
688                 }
689                 break;
690         case OPEN:
691                 for (; nops < iters && !alarm_caught;) {
692                         file = next_file();
693                         if ((fd = open(file, openflags, 0644)) < 0) {
694                                 if (((rc = errno) == EINTR) && alarm_caught)
695                                         break;
696                                 fatal(myrank, "open(%s) error: %s\n",
697                                       file, strerror(rc));
698                         }
699
700                         close(fd);
701
702                         DISPLAY_PROGRESS();
703                 }
704                 break;
705         case STAT:
706                 for (; nops < iters && !alarm_caught;) {
707                         rc = stat(file = next_file(), &statbuf);
708                         if (rc) {
709                                 if (((rc = errno) == EINTR) && alarm_caught)
710                                         break;
711                                 fatal(myrank, "stat(%s) error: %s\n",
712                                       file, strerror(rc));
713                         }
714
715                         DISPLAY_PROGRESS();
716                 }
717                 break;
718         case UNLINK:
719                 for (; begin <= end && !alarm_caught; begin += dirthreads) {
720                         sprintf(filename, filefmt, begin);
721                         rc = unlink(filename);
722                         if (rc) {
723                                 if (((rc = errno) == EINTR) && alarm_caught)
724                                         break;
725                                 fatal(myrank, "unlink(%s) error: %s\n",
726                                       filename, strerror(rc));
727                         }
728
729                         DISPLAY_PROGRESS();
730                 }
731                 break;
732         }
733
734         curTime = time(0);
735         interval = curTime - startTime;
736         rate = (float)(nops);
737         if (interval != 0)
738                 rate /= (float)interval;
739
740         rc = MPI_Reduce(&nops, &ag_ops, 1, MPI_INT, MPI_SUM, 0,
741                         MPI_COMM_WORLD);
742         if (rc != MPI_SUCCESS) {
743                 fatal(myrank, "Failure in MPI_Reduce of total ops.\n");
744         }
745
746         rc = MPI_Reduce(&rate, &ag_rate, 1, MPI_FLOAT, MPI_SUM, 0,
747                         MPI_COMM_WORLD);
748         if (rc != MPI_SUCCESS) {
749                 fatal(myrank, "Failure in MPI_Reduce of aggregated rate.\n");
750         }
751
752         if (myrank == 0) {
753                 printf("Rate: %.2f %ss/sec (total: %d threads %d %ss %lu secs)"
754                        "\n", ag_rate, cmd, nthreads, ag_ops, cmd, interval);
755         }
756
757         if (recreate) {
758                 for (begin = beginsave; begin <= end; begin += dirthreads) {
759                         sprintf(filename, filefmt, begin);
760                         if ((fd = open(filename, openflags, 0644)) < 0) {
761                                 rc = errno;
762                                 if (rc == EEXIST)
763                                         break;
764                                 fatal(myrank, "recreate open(%s) error: %s\n",
765                                       filename, strerror(rc));
766                         }
767
768                         close(fd);
769                 }
770         }
771
772         curTime = time(0);
773         if ((myrank == 0) || debug) {
774                 printf("%d: %s finished at %s",
775                        myrank, hostname, ctime(&curTime));
776         }
777
778         MPI_Finalize();
779         return(0);
780 }