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