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