Whamcloud - gitweb
LU-1205 tests: add timestamps to sanityn 18 mmap
[fs/lustre-release.git] / lustre / tests / mmap_sanity.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
31  *
32  * Copyright (c) 2012, Whamcloud, Inc.
33  */
34 /*
35  * This file is part of Lustre, http://www.lustre.org/
36  * Lustre is a trademark of Sun Microsystems, Inc.
37  */
38
39 #include <stdio.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <sys/mman.h>
43 #include <errno.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <fcntl.h>
47 #include <netinet/in.h>
48 #include <sys/socket.h>
49 #include <netdb.h>
50 #include <string.h>
51 #include <sys/wait.h>
52 #include <sys/time.h>
53
54 char *dir = NULL, *dir2 = NULL;
55 long page_size;
56 char mmap_sanity[256];
57
58
59 static void usage(void)
60 {
61         printf("Usage: mmap_sanity -d dir [-m dir2]\n");
62         printf("       dir      lustre mount point\n");
63         printf("       dir2     another mount point\n");
64         exit(127);
65 }
66
67 static int remote_tst(int tc, char *mnt);
68 static int mmap_run(int tc)
69 {
70         pid_t child;
71         int rc = 0;
72
73         child = fork();
74         if (child < 0)
75                 return errno;
76         else if (child)
77                 return 0;
78
79         if (dir2 != NULL) {
80                 rc = remote_tst(tc, dir2);
81         } else {
82                 rc = EINVAL;
83                 fprintf(stderr, "invalid argument!\n");
84         }
85         _exit(rc);
86 }
87
88 static int mmap_initialize(char *myself)
89 {
90         char buf[1024], *file;
91         int fdr, fdw, count, rc = 0;
92         
93         page_size = sysconf(_SC_PAGESIZE);
94         if (page_size == -1) {
95                 perror("sysconf(_SC_PAGESIZE)");
96                 return errno;
97         }
98
99         /* copy myself to lustre for another client */
100         fdr = open(myself, O_RDONLY);
101         if (fdr < 0) {
102                 perror(myself);
103                 return EINVAL;
104         }
105         file = strrchr(myself, '/');
106         if (file == NULL) {
107                 fprintf(stderr, "can't get test filename\n");
108                 close(fdr);
109                 return EINVAL;
110         }
111         file++;
112         sprintf(mmap_sanity, "%s/%s", dir, file);
113
114         fdw = open(mmap_sanity, O_CREAT|O_WRONLY, 0777);
115         if (fdw < 0) {
116                 perror(mmap_sanity);
117                 close(fdr);
118                 return EINVAL;
119         }
120         while ((count = read(fdr, buf, sizeof(buf))) != 0) {
121                 int writes;
122
123                 if (count < 0) {
124                         perror("read()");
125                         rc = errno;
126                         break;
127                 }
128                 writes = write(fdw, buf, count);
129                 if (writes != count) {
130                         perror("write()");
131                         rc = errno;
132                         break;
133                 }
134         }
135         close(fdr);
136         close(fdw);
137         return rc;
138 }
139
140 static void mmap_finalize()
141 {
142         unlink(mmap_sanity);
143 }
144
145 /* basic mmap operation on single node */
146 static int mmap_tst1(char *mnt)
147 {
148         char *ptr, mmap_file[256];
149         int region, fd, rc = 0;
150
151         region = page_size * 10;
152         sprintf(mmap_file, "%s/%s", mnt, "mmap_file1");
153         
154         if (unlink(mmap_file) && errno != ENOENT) {
155                 perror("unlink()");
156                 return errno;
157         }
158
159         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
160         if (fd < 0) {
161                 perror(mmap_file);
162                 return errno;
163         }
164         if (ftruncate(fd, region) < 0) {
165                 perror("ftruncate()");
166                 rc = errno;
167                 goto out_close;
168         }
169
170         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
171         if (ptr == MAP_FAILED) {
172                 perror("mmap()");
173                 rc = errno;
174                 goto out_close;
175         }
176         memset(ptr, 'a', region);
177
178         munmap(ptr, region);
179 out_close:
180         close(fd);
181         unlink(mmap_file);
182         return rc;
183 }
184
185 /* MAP_PRIVATE create a copy-on-write mmap */
186 static int mmap_tst2(char *mnt)
187 {
188         char *ptr, mmap_file[256], buf[256];
189         int fd, rc = 0;
190
191         sprintf(mmap_file, "%s/%s", mnt, "mmap_file2");
192
193         if (unlink(mmap_file) && errno != ENOENT) {
194                 perror("unlink()");
195                 return errno;
196         }
197
198         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
199         if (fd < 0) {
200                 perror(mmap_file);
201                 return errno;
202         }
203         if (ftruncate(fd, page_size) < 0) {
204                 perror("ftruncate()");
205                 rc = errno;
206                 goto out_close;
207         }
208
209         ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
210         if (ptr == MAP_FAILED) {
211                 perror("mmap()");
212                 rc = errno;
213                 goto out_close;
214         }
215         memcpy(ptr, "blah", strlen("blah"));
216
217         munmap(ptr, page_size);
218 out_close:
219         close(fd);
220         if (rc)
221                 return rc;
222
223         fd = open(mmap_file, O_RDONLY);
224         if (fd < 0) {
225                 perror(mmap_file);
226                 return errno;
227         }
228         rc = read(fd, buf, sizeof(buf));
229         if (rc < 0) {
230                 perror("read()");
231                 rc = errno;
232                 goto out_close;
233         }
234         rc = 0;
235         
236         if (strncmp("blah", buf, strlen("blah")) == 0) {
237                 fprintf(stderr, "mmap write back with MAP_PRIVATE!\n");
238                 rc = EFAULT;
239         }
240         close(fd);
241         unlink(mmap_file);
242         return rc;
243 }
244
245 /* concurrent mmap operations on two nodes */
246 static int mmap_tst3(char *mnt)
247 {
248         char *ptr, mmap_file[256];
249         int region, fd, rc = 0;
250
251         region = page_size * 100;
252         sprintf(mmap_file, "%s/%s", mnt, "mmap_file3");
253         
254         if (unlink(mmap_file) && errno != ENOENT) {
255                 perror("unlink()");
256                 return errno;
257         }
258
259         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
260         if (fd < 0) {
261                 perror(mmap_file);
262                 return errno;
263         }
264         if (ftruncate(fd, region) < 0) {
265                 perror("ftruncate()");
266                 rc = errno;
267                 goto out_close;
268         }
269
270         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
271         if (ptr == MAP_FAILED) {
272                 perror("mmap()");
273                 rc = errno;
274                 goto out_close;
275         }
276
277         rc = mmap_run(3);
278         if (rc)
279                 goto out_unmap;
280         
281         memset(ptr, 'a', region);
282         sleep(2);       /* wait for remote test finish */
283 out_unmap:
284         munmap(ptr, region);
285 out_close:
286         close(fd);
287         unlink(mmap_file);
288         return rc;
289 }       
290
291 static int remote_tst3(char *mnt)
292 {
293         char *ptr, mmap_file[256];
294         int region, fd, rc = 0;
295
296         region = page_size * 100;
297         sprintf(mmap_file, "%s/%s", mnt, "mmap_file3");
298
299         fd = open(mmap_file, O_RDWR, 0600);
300         if (fd < 0) {
301                 perror(mmap_file);
302                 return errno;
303         }
304
305         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
306         if (ptr == MAP_FAILED) {
307                 perror("mmap()");
308                 rc = errno;
309                 goto out_close;
310         }
311         memset(ptr, 'b', region);
312         memset(ptr, 'c', region);
313         
314         munmap(ptr, region);
315 out_close:
316         close(fd);
317         return rc;
318 }
319
320 /* client1 write to file_4a from mmap()ed file_4b;
321  * client2 write to file_4b from mmap()ed file_4a. */
322 static int mmap_tst4(char *mnt)
323 {
324         char *ptr, filea[256], fileb[256];
325         int region, fdr, fdw, rc = 0;
326
327         region = page_size * 100;
328         sprintf(filea, "%s/%s", mnt, "mmap_file_4a");
329         sprintf(fileb, "%s/%s", mnt, "mmap_file_4b");
330
331         if (unlink(filea) && errno != ENOENT) {
332                 perror("unlink()");
333                 return errno;
334         }
335         if (unlink(fileb) && errno != ENOENT) {
336                 perror("unlink()");
337                 return errno;
338         }
339
340         fdr = fdw = -1;
341         fdr = open(fileb, O_CREAT|O_RDWR, 0600);
342         if (fdr < 0) {
343                 perror(fileb);
344                 return errno;
345         }
346         if (ftruncate(fdr, region) < 0) {
347                 perror("ftruncate()");
348                 rc = errno;
349                 goto out_close;
350         }
351         fdw = open(filea, O_CREAT|O_RDWR, 0600);
352         if (fdw < 0) {
353                 perror(filea);
354                 rc = errno;
355                 goto out_close;
356         }
357         if (ftruncate(fdw, region) < 0) {
358                 perror("ftruncate()");
359                 rc = errno;
360                 goto out_close;
361         }
362         
363         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fdr, 0);
364         if (ptr == MAP_FAILED) {
365                 perror("mmap()");
366                 rc = errno;
367                 goto out_close;
368         }
369
370         rc = mmap_run(4);
371         if (rc)
372                 goto out_unmap;
373         
374         memset(ptr, '1', region);
375         
376         rc = write(fdw, ptr, region);
377         if (rc <= 0) {
378                 perror("write()");
379                 rc = errno;
380         } else
381                 rc = 0;
382
383         sleep(2);       /* wait for remote test finish */
384 out_unmap:
385         munmap(ptr, region);
386 out_close:
387         if (fdr >= 0)
388                 close(fdr);
389         if (fdw >= 0)
390                 close(fdw);
391         unlink(filea);
392         unlink(fileb);
393         return rc;
394 }
395
396 static int remote_tst4(char *mnt)
397 {
398         char *ptr, filea[256], fileb[256];
399         int region, fdr, fdw, rc = 0;
400
401         region = page_size * 100;
402         sprintf(filea, "%s/%s", mnt, "mmap_file_4a");
403         sprintf(fileb, "%s/%s", mnt, "mmap_file_4b");
404
405         fdr = fdw = -1;
406         fdr = open(filea, O_RDWR, 0600);
407         if (fdr < 0) {
408                 perror(filea);
409                 return errno;
410         }
411         fdw = open(fileb, O_RDWR, 0600);
412         if (fdw < 0) {
413                 perror(fileb);
414                 rc = errno;
415                 goto out_close;
416         }
417
418         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fdr, 0);
419         if (ptr == MAP_FAILED) {
420                 perror("mmap()");
421                 rc = errno;
422                 goto out_close;
423         }
424
425         memset(ptr, '2', region);
426
427         rc = write(fdw, ptr, region);
428         if (rc <= 0) {
429                 perror("write()");
430                 rc = errno;
431         } else
432                 rc = 0;
433      
434         munmap(ptr, region);
435 out_close:
436         if (fdr >= 0)
437                 close(fdr);
438         if (fdw >= 0)
439                 close(fdw);
440         return rc;
441 }
442
443 static int cancel_lru_locks(char *prefix)
444 {
445         char cmd[256], line[1024];
446         FILE *file;
447         pid_t child;
448         int len = 1024, rc = 0;
449
450         child = fork();
451         if (child < 0)
452                 return errno;
453         else if (child) {
454                 int status;
455                 rc = waitpid(child, &status, WNOHANG);
456                 if (rc == child)
457                         rc = 0;
458                 return rc;
459         }
460
461         if (prefix)
462                 sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*-%s-*/lru_size", prefix);
463         else
464                 sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*/lru_size");
465
466         file = popen(cmd, "r");
467         if (file == NULL) {
468                 perror("popen()");
469                 return errno;
470         }
471
472         while (fgets(line, len, file)) {
473                 FILE *f;
474
475                 if (!strlen(line))
476                         continue;
477                 /* trim newline character */
478                 *(line + strlen(line) - 1) = '\0';
479                 f = fopen(line, "w");
480                 if (f == NULL) {
481                         perror("fopen()");
482                         rc = errno;
483                         break;
484                 }
485                 rc = fwrite("clear", strlen("clear") + 1, 1, f);
486                 if (rc < 1) {
487                         perror("fwrite()");
488                         rc = errno;
489                         fclose(f);
490                         break;
491                 }
492                 fclose(f);
493         }
494
495         pclose(file);
496         _exit(rc);
497 }
498
499 /* don't dead lock while read/write file to/from the buffer which
500  * mmaped to just this file */
501 static int mmap_tst5(char *mnt)
502 {
503         char *ptr, mmap_file[256];
504         int region, fd, off, rc = 0;
505
506         region = page_size * 40;
507         off = page_size * 10;
508         sprintf(mmap_file, "%s/%s", mnt, "mmap_file5");
509
510         if (unlink(mmap_file) && errno != ENOENT) {
511                 perror("unlink()");
512                 return errno;
513         }
514
515         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
516         if (fd < 0) {
517                 perror(mmap_file);
518                 return errno;
519         }
520         if (ftruncate(fd, region) < 0) {
521                 perror("ftruncate()");
522                 rc = errno;
523                 goto out_close;
524         }
525
526         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
527         if (ptr == MAP_FAILED) {
528                 perror("mmap()");
529                 rc = errno;
530                 goto out_close;
531         }
532         memset(ptr, 'a', region);
533
534         /* cancel unused locks */
535         rc = cancel_lru_locks("osc");
536         if (rc)
537                 goto out_unmap;
538
539         /* read/write region of file and buffer should be overlap */
540         rc = read(fd, ptr + off, off * 2);
541         if (rc != off * 2) {
542                 perror("read()");
543                 rc = errno;
544                 goto out_unmap;
545         }
546         rc = write(fd, ptr + off, off * 2);
547         if (rc != off * 2) {
548                 perror("write()");
549                 rc = errno;
550         }
551         rc = 0;
552 out_unmap:
553         munmap(ptr, region);
554 out_close:
555         close(fd);
556         unlink(mmap_file);
557         return rc;
558 }
559
560 /* mmap write to a file form client1 then mmap read from client2 */
561 static int mmap_tst6(char *mnt)
562 {
563         char mmap_file[256], mmap_file2[256];
564         char *ptr = NULL, *ptr2 = NULL;
565         int fd = 0, fd2 = 0, rc = 0;
566
567         sprintf(mmap_file, "%s/%s", mnt, "mmap_file6");
568         sprintf(mmap_file2, "%s/%s", dir2, "mmap_file6");
569         if (unlink(mmap_file) && errno != ENOENT) {
570                 perror("unlink()");
571                 return errno;
572         }
573
574         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
575         if (fd < 0) {
576                 perror(mmap_file);
577                 return errno;
578         }
579         if (ftruncate(fd, page_size) < 0) {
580                 perror("ftruncate()");
581                 rc = errno;
582                 goto out;
583         }
584
585         fd2 = open(mmap_file2, O_RDWR, 0600);
586         if (fd2 < 0) {
587                 perror(mmap_file2);
588                 goto out;
589         }
590
591         ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
592         if (ptr == MAP_FAILED) {
593                 perror("mmap()");
594                 rc = errno;
595                 goto out;
596         }
597         
598         ptr2 = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
599         if (ptr2 == MAP_FAILED) {
600                 perror("mmap()");
601                 rc = errno;
602                 goto out;
603         }
604
605         rc = cancel_lru_locks("osc");
606         if (rc)
607                 goto out;
608
609         memcpy(ptr, "blah", strlen("blah"));
610         if (strncmp(ptr, ptr2, strlen("blah"))) {
611                 fprintf(stderr, "client2 mmap mismatch!\n");
612                 rc = EFAULT;
613                 goto out;
614         }
615         memcpy(ptr2, "foo", strlen("foo"));
616         if (strncmp(ptr, ptr2, strlen("foo"))) {
617                 fprintf(stderr, "client1 mmap mismatch!\n");
618                 rc = EFAULT;
619         }
620 out:
621         if (ptr2)
622                 munmap(ptr2, page_size);
623         if (ptr)
624                 munmap(ptr, page_size);
625         if (fd2 > 0)
626                 close(fd2);
627         if (fd > 0)
628                 close(fd);
629         unlink(mmap_file);
630         return rc;
631 }
632
633 static int mmap_tst7_func(char *mnt, int rw)
634 {
635         char  fname[256];
636         char *buf = MAP_FAILED;
637         ssize_t bytes;
638         int fd = -1;
639         int rc = 0;
640
641         if (snprintf(fname, 256, "%s/mmap_tst7.%s",
642                      mnt, (rw == 0) ? "read":"write") >= 256) {
643                 fprintf(stderr, "dir name too long\n");
644                 rc = ENAMETOOLONG;
645                 goto out;
646         }
647         fd = open(fname, O_RDWR | O_DIRECT | O_CREAT, 0644);
648         if (fd == -1) {
649                 perror("open");
650                 rc = errno;
651                 goto out;
652         }
653         if (ftruncate(fd, 2 * page_size) == -1) {
654                 perror("truncate");
655                 rc = errno;
656                 goto out;
657         }
658         buf = mmap(NULL, page_size * 2,
659                    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
660         if (buf == MAP_FAILED) {
661                 perror("mmap");
662                 rc = errno;
663                 goto out;
664         }
665         /* ensure the second page isn't mapped */
666         munmap(buf + page_size, page_size);
667         bytes = (rw == 0) ? read(fd, buf, 2 * page_size) :
668                 write(fd, buf, 2 * page_size);
669         /* Expected behavior */
670         if (bytes == page_size)
671                 goto out;
672         fprintf(stderr, "%s returned %zd, errno = %d\n",
673                 (rw == 0)?"read":"write", bytes, errno);
674         rc = EIO;
675 out:
676         if (buf != MAP_FAILED)
677                 munmap(buf, page_size);
678         if (fd != -1)
679                 close(fd);
680         return rc;
681 }
682
683 static int mmap_tst7(char *mnt)
684 {
685         int rc;
686
687         rc = mmap_tst7_func(mnt, 0);
688         if (rc != 0)
689                 return rc;
690         rc = mmap_tst7_func(mnt, 1);
691         return rc;
692 }
693
694 static int mmap_tst8(char *mnt)
695 {
696         char  fname[256];
697         char *buf = MAP_FAILED;
698         int fd = -1;
699         int rc = 0;
700         pid_t pid;
701         char xyz[page_size * 2];
702
703         if (snprintf(fname, 256, "%s/mmap_tst8", mnt) >= 256) {
704                 fprintf(stderr, "dir name too long\n");
705                 rc = ENAMETOOLONG;
706                 goto out;
707         }
708         fd = open(fname, O_RDWR | O_CREAT, 0644);
709         if (fd == -1) {
710                 perror("open");
711                 rc = errno;
712                 goto out;
713         }
714         if (ftruncate(fd, page_size) == -1) {
715                 perror("truncate");
716                 rc = errno;
717                 goto out;
718         }
719         buf = mmap(NULL, page_size * 2,
720                    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
721         if (buf == MAP_FAILED) {
722                 perror("mmap");
723                 rc = errno;
724                 goto out;
725         }
726
727         pid = fork();
728         if (pid == 0) { /* child */
729                 memcpy(xyz, buf, page_size * 2);
730                 /* shouldn't reach here. */
731                 exit(0);
732         } else if (pid > 0) { /* parent */
733                 int status = 0;
734                 pid = waitpid(pid, &status, 0);
735                 if (pid < 0) {
736                         perror("wait");
737                         rc = errno;
738                         goto out;
739                 }
740
741                 rc = EFAULT;
742                 if (WIFSIGNALED(status) && SIGBUS == WTERMSIG(status))
743                         rc = 0;
744         } else {
745                 perror("fork");
746                 rc = errno;
747         }
748
749 out:
750         if (buf != MAP_FAILED)
751                 munmap(buf, page_size);
752         if (fd != -1)
753                 close(fd);
754         return rc;
755 }
756
757 static int remote_tst(int tc, char *mnt)
758 {
759         int rc = 0;
760         switch(tc) {
761         case 3:
762                 rc = remote_tst3(mnt);
763                 break;
764         case 4:
765                 rc = remote_tst4(mnt);
766                 break;
767         default:
768                 fprintf(stderr, "wrong test case number %d\n", tc);
769                 rc = EINVAL;
770                 break;
771         }
772         return rc;
773 }
774
775 struct test_case {
776         int     tc;                     /* test case number */
777         char    *desc;                  /* test description */
778         int     (* test_fn)(char *mnt); /* test function */
779         int     node_cnt;               /* node count */
780 };
781
782 struct test_case tests[] = {
783         { 1, "mmap test1: basic mmap operation", mmap_tst1, 1 },
784         { 2, "mmap test2: MAP_PRIVATE not write back", mmap_tst2, 1 },
785         { 3, "mmap test3: concurrent mmap ops on two nodes", mmap_tst3, 2 },
786         { 4, "mmap test4: c1 write to f1 from mmapped f2, " 
787              "c2 write to f1 from mmapped f1", mmap_tst4, 2 },
788         { 5, "mmap test5: read/write file to/from the buffer "
789              "which mmapped to just this file", mmap_tst5, 1 },
790         { 6, "mmap test6: check mmap write/read content on two nodes", 
791                 mmap_tst6, 2 },
792         { 7, "mmap test7: file i/o with an unmapped buffer", mmap_tst7, 1},
793         { 8, "mmap test8: SIGBUS for beyond file size", mmap_tst8, 1},
794         { 0, NULL, 0, 0 }
795 };
796
797 int main(int argc, char **argv)
798 {
799         extern char *optarg;
800         struct test_case *test;
801         int c, rc = 0;
802
803         for(;;) {
804                 c = getopt(argc, argv, "d:m:");
805                 if ( c == -1 )
806                         break;
807
808                 switch(c) {
809                         case 'd':
810                                 dir = optarg;
811                                 break;
812                         case 'm':
813                                 dir2 = optarg;
814                                 break;
815                         default:
816                         case '?':
817                                 usage();
818                                 break;
819                 }
820         }
821
822         if (dir == NULL)
823                 usage();
824
825         if (mmap_initialize(argv[0]) != 0) {
826                 fprintf(stderr, "mmap_initialize failed!\n");
827                 return EINVAL;
828         }
829
830         for (test = tests; test->tc; test++) {
831                 double duration;
832                 char *rs;
833
834                 if (test->node_cnt == 1 || dir2 != NULL) {
835                         struct timeval start, end;
836
837                         gettimeofday(&start, NULL);
838                         rc = test->test_fn(dir);
839                         gettimeofday(&end, NULL);
840
841                         duration = (double)(end.tv_sec - start.tv_sec) +
842                                 (double)(end.tv_usec - start.tv_usec) / 1000000;
843                         rs = rc ? "FAIL" : "PASS";
844                 } else {
845                         duration = 0.0;
846                         rs = "SKIP";
847                 }
848                 fprintf(stderr, "%s (%s, %.5gs)\n", test->desc, rs, duration);
849                 if (rc)
850                         break;
851         }
852
853         mmap_finalize();
854         return rc;
855 }