Whamcloud - gitweb
LU-8141 tests: fix for acl test
[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, 2015, 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 <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <sys/time.h>
41 #include <sys/mman.h>
42 #include <limits.h>
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <stdlib.h>
46 #include <fcntl.h>
47 #include <getopt.h>
48 #include <string.h>
49 #include <errno.h>
50
51 #include <libcfs/util/param.h>
52
53 char *dir = NULL, *dir2 = NULL;
54 long page_size;
55 char mmap_sanity[256];
56
57 static void usage(void)
58 {
59         printf("Usage: mmap_sanity -d dir [-m dir2] [-e <test cases>]\n");
60         printf("       -d dir        lustre mount point\n");
61         printf("       -m dir2       another mount point\n");
62         printf("       -e testcases  skipped test cases, -e 1 -e 2 to exclude"
63                                     " test cases 1 and 2.\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 *filter)
444 {
445         glob_t paths;
446         pid_t child;
447         int rc, i;
448
449         child = fork();
450         if (child < 0)
451                 return -errno;
452         else if (child) {
453                 int status;
454
455                 rc = waitpid(child, &status, WNOHANG);
456                 if (rc == child)
457                         rc = 0;
458                 return rc;
459         }
460
461         if (filter != NULL)
462                 rc = cfs_get_param_paths(&paths,
463                                         "ldlm/namespaces/*-%s-*/lru_size",
464                                         filter);
465         else
466                 rc = cfs_get_param_paths(&paths,
467                                         "ldlm/namespaces/*/lru_size");
468         if (rc != 0)
469                 return -EINVAL;
470
471         for (i = 0; i < paths.gl_pathc; i++) {
472                 FILE *f = fopen(paths.gl_pathv[i], "r");
473                 if (f == NULL) {
474                         rc = -errno;
475                         fprintf(stderr, "cannot open '%s': %s\n",
476                                 paths.gl_pathv[i], strerror(errno));
477                         break;
478                 }
479
480                 rc = fwrite("clear", strlen("clear") + 1, 1, f);
481                 if (rc < 1) {
482                         rc = -errno;
483                         fprintf(stderr, "fwrite failed for '%s': %s\n",
484                                 paths.gl_pathv[i], strerror(errno));
485                         fclose(f);
486                         break;
487                 }
488                 fclose(f);
489         }
490
491         cfs_free_param_data(&paths);
492         _exit(rc);
493 }
494
495 /* don't dead lock while read/write file to/from the buffer which
496  * mmaped to just this file */
497 static int mmap_tst5(char *mnt)
498 {
499         char *ptr, mmap_file[256];
500         int region, fd, off, rc = 0;
501
502         region = page_size * 40;
503         off = page_size * 10;
504         sprintf(mmap_file, "%s/%s", mnt, "mmap_file5");
505
506         if (unlink(mmap_file) && errno != ENOENT) {
507                 perror("unlink()");
508                 return -errno;
509         }
510
511         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
512         if (fd < 0) {
513                 perror(mmap_file);
514                 return -errno;
515         }
516         if (ftruncate(fd, region) < 0) {
517                 perror("ftruncate()");
518                 rc = -errno;
519                 goto out_close;
520         }
521
522         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
523         if (ptr == MAP_FAILED) {
524                 perror("mmap()");
525                 rc = -errno;
526                 goto out_close;
527         }
528         memset(ptr, 'a', region);
529
530         /* cancel unused locks */
531         rc = cancel_lru_locks("osc");
532         if (rc)
533                 goto out_unmap;
534
535         /* read/write region of file and buffer should be overlap */
536         rc = read(fd, ptr + off, off * 2);
537         if (rc != off * 2) {
538                 perror("read()");
539                 rc = -errno;
540                 goto out_unmap;
541         }
542         rc = write(fd, ptr + off, off * 2);
543         if (rc != off * 2) {
544                 perror("write()");
545                 rc = -errno;
546         }
547         rc = 0;
548 out_unmap:
549         munmap(ptr, region);
550 out_close:
551         close(fd);
552         unlink(mmap_file);
553         return rc;
554 }
555
556 /* mmap write to a file form client1 then mmap read from client2 */
557 static int mmap_tst6(char *mnt)
558 {
559         char mmap_file[256], mmap_file2[256];
560         char *ptr = NULL, *ptr2 = NULL;
561         int fd = 0, fd2 = 0, rc = 0;
562
563         sprintf(mmap_file, "%s/%s", mnt, "mmap_file6");
564         sprintf(mmap_file2, "%s/%s", dir2, "mmap_file6");
565         if (unlink(mmap_file) && errno != ENOENT) {
566                 perror("unlink()");
567                 return -errno;
568         }
569
570         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
571         if (fd < 0) {
572                 perror(mmap_file);
573                 return -errno;
574         }
575         if (ftruncate(fd, page_size) < 0) {
576                 perror("ftruncate()");
577                 rc = -errno;
578                 goto out;
579         }
580
581         fd2 = open(mmap_file2, O_RDWR, 0600);
582         if (fd2 < 0) {
583                 perror(mmap_file2);
584                 rc = -errno;
585                 goto out;
586         }
587
588         ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
589         if (ptr == MAP_FAILED) {
590                 perror("mmap()");
591                 rc = -errno;
592                 goto out;
593         }
594
595         ptr2 = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
596         if (ptr2 == MAP_FAILED) {
597                 perror("mmap()");
598                 rc = -errno;
599                 goto out;
600         }
601
602         rc = cancel_lru_locks("osc");
603         if (rc)
604                 goto out;
605
606         memcpy(ptr, "blah", strlen("blah"));
607         if (strncmp(ptr, ptr2, strlen("blah"))) {
608                 fprintf(stderr, "client2 mmap mismatch!\n");
609                 rc = -EFAULT;
610                 goto out;
611         }
612         memcpy(ptr2, "foo", strlen("foo"));
613         if (strncmp(ptr, ptr2, strlen("foo"))) {
614                 fprintf(stderr, "client1 mmap mismatch!\n");
615                 rc = -EFAULT;
616         }
617 out:
618         if (ptr2)
619                 munmap(ptr2, page_size);
620         if (ptr)
621                 munmap(ptr, page_size);
622         if (fd2 > 0)
623                 close(fd2);
624         if (fd > 0)
625                 close(fd);
626         unlink(mmap_file);
627         return rc;
628 }
629
630 static int mmap_tst7_func(char *mnt, int rw)
631 {
632         char  fname[256];
633         char *buf = MAP_FAILED;
634         ssize_t bytes;
635         int fd = -1;
636         int rc = 0;
637
638         if (snprintf(fname, 256, "%s/mmap_tst7.%s", mnt,
639                      (rw == 0) ? "read" : "write") >= 256) {
640                 fprintf(stderr, "dir name too long\n");
641                 rc = -ENAMETOOLONG;
642                 goto out;
643         }
644         fd = open(fname, O_RDWR | O_DIRECT | O_CREAT, 0644);
645         if (fd == -1) {
646                 perror("open");
647                 rc = -errno;
648                 goto out;
649         }
650         if (ftruncate(fd, 2 * page_size) == -1) {
651                 perror("truncate");
652                 rc = -errno;
653                 goto out;
654         }
655         buf = mmap(NULL, page_size * 2,
656                    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
657         if (buf == MAP_FAILED) {
658                 perror("mmap");
659                 rc = -errno;
660                 goto out;
661         }
662         /* ensure the second page isn't mapped */
663         munmap(buf + page_size, page_size);
664         bytes = (rw == 0) ? read(fd, buf, 2 * page_size) :
665                             write(fd, buf, 2 * page_size);
666         /* Expected behavior */
667         if (bytes == page_size)
668                 goto out;
669
670         fprintf(stderr, "%s returned %zd, errno = %d\n",
671                 (rw == 0) ? "read" : "write", bytes, errno);
672         rc = -EIO;
673 out:
674         if (buf != MAP_FAILED)
675                 munmap(buf, page_size);
676         if (fd != -1)
677                 close(fd);
678         return rc;
679 }
680
681 static int mmap_tst7(char *mnt)
682 {
683         int rc;
684
685         rc = mmap_tst7_func(mnt, 0);
686         if (rc != 0)
687                 return rc;
688         rc = mmap_tst7_func(mnt, 1);
689         return rc;
690 }
691
692 static int mmap_tst8(char *mnt)
693 {
694         char  fname[256];
695         char *buf = MAP_FAILED;
696         int fd = -1;
697         int rc = 0;
698         pid_t pid;
699         char xyz[page_size * 2];
700
701         if (snprintf(fname, 256, "%s/mmap_tst8", mnt) >= 256) {
702                 fprintf(stderr, "dir name too long\n");
703                 rc = -ENAMETOOLONG;
704                 goto out;
705         }
706         fd = open(fname, O_RDWR | O_CREAT, 0644);
707         if (fd == -1) {
708                 perror("open");
709                 rc = -errno;
710                 goto out;
711         }
712         if (ftruncate(fd, page_size) == -1) {
713                 perror("truncate");
714                 rc = -errno;
715                 goto out;
716         }
717         buf = mmap(NULL, page_size * 2,
718                    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
719         if (buf == MAP_FAILED) {
720                 perror("mmap");
721                 rc = -errno;
722                 goto out;
723         }
724
725         pid = fork();
726         if (pid == 0) { /* child */
727                 memcpy(xyz, buf, page_size * 2);
728                 /* shouldn't reach here. */
729                 exit(0);
730         } else if (pid > 0) { /* parent */
731                 int status = 0;
732                 pid = waitpid(pid, &status, 0);
733                 if (pid < 0) {
734                         perror("wait");
735                         rc = -errno;
736                         goto out;
737                 }
738
739                 rc = -EFAULT;
740                 if (WIFSIGNALED(status) && SIGBUS == WTERMSIG(status))
741                         rc = 0;
742         } else {
743                 perror("fork");
744                 rc = -errno;
745         }
746
747 out:
748         if (buf != MAP_FAILED)
749                 munmap(buf, page_size);
750         if (fd != -1)
751                 close(fd);
752         return rc;
753 }
754
755 static int remote_tst(int tc, char *mnt)
756 {
757         int rc = 0;
758         switch (tc) {
759         case 3:
760                 rc = remote_tst3(mnt);
761                 break;
762         case 4:
763                 rc = remote_tst4(mnt);
764                 break;
765         default:
766                 fprintf(stderr, "wrong test case number %d\n", tc);
767                 rc = -EINVAL;
768                 break;
769         }
770         return rc;
771 }
772
773 struct test_case {
774         int     tc;                     /* test case number */
775         char    *desc;                  /* test description */
776         int     (*test_fn)(char *mnt);  /* test function */
777         int     node_cnt;               /* node count */
778         int     skipped;                /* skipped by caller */
779 };
780
781 struct test_case tests[] = {
782         { 1, "mmap test1: basic mmap operation", mmap_tst1, 1 },
783         { 2, "mmap test2: MAP_PRIVATE not write back", mmap_tst2, 1 },
784         { 3, "mmap test3: concurrent mmap ops on two nodes", mmap_tst3, 2 },
785         { 4, "mmap test4: c1 write to f1 from mmapped f2, "
786              "c2 write to f1 from mmapped f1", mmap_tst4, 2 },
787         { 5, "mmap test5: read/write file to/from the buffer "
788              "which mmapped to just this file", mmap_tst5, 1 },
789         { 6, "mmap test6: check mmap write/read content on two nodes",
790                 mmap_tst6, 2 },
791         { 7, "mmap test7: file i/o with an unmapped buffer", mmap_tst7, 1},
792         { 8, "mmap test8: SIGBUS for beyond file size", mmap_tst8, 1 },
793         { 0, NULL, 0, 0 }
794 };
795
796 int main(int argc, char **argv)
797 {
798         struct test_case *test;
799         int nr_cases = sizeof(tests)/sizeof(*test);
800         int c, rc = 0;
801
802         while ((c = getopt(argc, argv, "d:m:e:")) != -1) {
803                 switch (c) {
804                 case 'd':
805                         dir = optarg;
806                         break;
807                 case 'm':
808                         dir2 = optarg;
809                         break;
810                 case 'e': {
811                         char *endptr = NULL;
812                         rc = strtol(optarg, &endptr, 10);
813                         if (endptr != NULL && *endptr != '\0')
814                                 usage();
815                         if (rc > 0 && rc < nr_cases)
816                                 tests[rc - 1].skipped = 1;
817                         break;
818                 }
819                 default:
820                         usage();
821                         break;
822                 }
823         }
824
825         if (dir == NULL)
826                 usage();
827
828         if (mmap_initialize(argv[0]) != 0) {
829                 fprintf(stderr, "mmap_initialize failed!\n");
830                 return -EINVAL;
831         }
832
833         rc = 0;
834         for (test = tests; test->tc; test++) {
835                 double duration = 0.0;
836                 char *rs = "SKIPPED";
837
838                 if (!test->skipped && (test->node_cnt == 1 || dir2 != NULL)) {
839                         struct timeval start, end;
840
841                         gettimeofday(&start, NULL);
842                         rc = test->test_fn(dir);
843                         gettimeofday(&end, NULL);
844
845                         duration = (double)(end.tv_sec - start.tv_sec) +
846                                 (double)(end.tv_usec - start.tv_usec) / 1000000;
847                         rs = rc ? "FAIL" : "PASS";
848                 }
849
850                 fprintf(stderr, "%s (%s, %.5gs)\n", test->desc, rs, duration);
851                 if (rc)
852                         break;
853         }
854
855         mmap_finalize();
856         return -rc;
857 }