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