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