Whamcloud - gitweb
LU-14487 lustre: remove references to Sun Trademark.
[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  */
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/wait.h>
35 #include <sys/time.h>
36 #include <sys/mman.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <fcntl.h>
42 #include <getopt.h>
43 #include <string.h>
44 #include <errno.h>
45
46 #include <libcfs/util/param.h>
47
48 char *dir = NULL, *dir2 = NULL;
49 long page_size;
50 char mmap_sanity[256];
51
52 static void usage(void)
53 {
54         printf("Usage: mmap_sanity -d dir [-m dir2] [-e <test cases>]\n");
55         printf("       -d dir        lustre mount point\n");
56         printf("       -m dir2       another mount point\n");
57         printf("       -e testcases  skipped test cases, -e 1 -e 2 to exclude test cases 1 and 2.\n");
58         exit(127);
59 }
60
61 static int remote_tst(int tc, char *mnt);
62 static int mmap_run(int tc)
63 {
64         pid_t child;
65         int rc = 0;
66
67         child = fork();
68         if (child < 0)
69                 return -errno;
70         else if (child)
71                 return 0;
72
73         if (dir2) {
74                 rc = remote_tst(tc, dir2);
75         } else {
76                 rc = -EINVAL;
77                 fprintf(stderr, "invalid argument!\n");
78         }
79         _exit(rc);
80 }
81
82 static int mmap_initialize(char *myself)
83 {
84         char buf[1024], *file;
85         int fdr, fdw, count, rc = 0;
86
87         page_size = sysconf(_SC_PAGESIZE);
88         if (page_size == -1) {
89                 perror("sysconf(_SC_PAGESIZE)");
90                 return -errno;
91         }
92
93         /* copy myself to lustre for another client */
94         fdr = open(myself, O_RDONLY);
95         if (fdr < 0) {
96                 perror(myself);
97                 return -EINVAL;
98         }
99         file = strrchr(myself, '/');
100         if (!file) {
101                 fprintf(stderr, "can't get test filename\n");
102                 close(fdr);
103                 return -EINVAL;
104         }
105         file++;
106         sprintf(mmap_sanity, "%s/%s", dir, file);
107
108         fdw = open(mmap_sanity, O_CREAT | O_WRONLY, 0777);
109         if (fdw < 0) {
110                 perror(mmap_sanity);
111                 close(fdr);
112                 return -EINVAL;
113         }
114         while ((count = read(fdr, buf, sizeof(buf))) != 0) {
115                 int writes;
116
117                 if (count < 0) {
118                         perror("read()");
119                         rc = -errno;
120                         break;
121                 }
122                 writes = write(fdw, buf, count);
123                 if (writes != count) {
124                         perror("write()");
125                         rc = -errno;
126                         break;
127                 }
128         }
129         close(fdr);
130         close(fdw);
131         return rc;
132 }
133
134 static void mmap_finalize()
135 {
136         unlink(mmap_sanity);
137 }
138
139 /* basic mmap operation on single node */
140 static int mmap_tst1(char *mnt)
141 {
142         char *ptr, mmap_file[256];
143         int region, fd, rc = 0;
144
145         region = page_size * 10;
146         sprintf(mmap_file, "%s/%s", mnt, "mmap_file1");
147
148         if (unlink(mmap_file) && errno != ENOENT) {
149                 perror("unlink()");
150                 return -errno;
151         }
152
153         fd = open(mmap_file, O_CREAT | O_RDWR, 0600);
154         if (fd < 0) {
155                 perror(mmap_file);
156                 return -errno;
157         }
158         if (ftruncate(fd, region) < 0) {
159                 perror("ftruncate()");
160                 rc = -errno;
161                 goto out_close;
162         }
163
164         ptr = mmap(NULL, region, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
165         if (ptr == MAP_FAILED) {
166                 perror("mmap()");
167                 rc = -errno;
168                 goto out_close;
169         }
170         memset(ptr, 'a', region);
171
172         munmap(ptr, region);
173 out_close:
174         close(fd);
175         unlink(mmap_file);
176         return rc;
177 }
178
179 /* MAP_PRIVATE create a copy-on-write mmap */
180 static int mmap_tst2(char *mnt)
181 {
182         char *ptr, mmap_file[256], buf[256];
183         int fd, rc = 0;
184
185         sprintf(mmap_file, "%s/%s", mnt, "mmap_file2");
186
187         if (unlink(mmap_file) && errno != ENOENT) {
188                 perror("unlink()");
189                 return -errno;
190         }
191
192         fd = open(mmap_file, O_CREAT | O_RDWR, 0600);
193         if (fd < 0) {
194                 perror(mmap_file);
195                 return -errno;
196         }
197         if (ftruncate(fd, page_size) < 0) {
198                 perror("ftruncate()");
199                 rc = -errno;
200                 goto out_close;
201         }
202
203         ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
204         if (ptr == MAP_FAILED) {
205                 perror("mmap()");
206                 rc = -errno;
207                 goto out_close;
208         }
209         memcpy(ptr, "blah", strlen("blah"));
210
211         munmap(ptr, page_size);
212 out_close:
213         close(fd);
214         if (rc)
215                 return -rc;
216
217         fd = open(mmap_file, O_RDONLY);
218         if (fd < 0) {
219                 perror(mmap_file);
220                 return -errno;
221         }
222         rc = read(fd, buf, sizeof(buf));
223         if (rc < 0) {
224                 perror("read()");
225                 rc = -errno;
226                 goto out_close;
227         }
228         rc = 0;
229
230         if (strncmp("blah", buf, strlen("blah")) == 0) {
231                 fprintf(stderr, "mmap write back with MAP_PRIVATE!\n");
232                 rc = -EFAULT;
233         }
234         close(fd);
235         unlink(mmap_file);
236         return rc;
237 }
238
239 /* concurrent mmap operations on two nodes */
240 static int mmap_tst3(char *mnt)
241 {
242         char *ptr, mmap_file[256];
243         int region, fd, rc = 0;
244
245         region = page_size * 100;
246         sprintf(mmap_file, "%s/%s", mnt, "mmap_file3");
247
248         if (unlink(mmap_file) && errno != ENOENT) {
249                 perror("unlink()");
250                 return -errno;
251         }
252
253         fd = open(mmap_file, O_CREAT | O_RDWR, 0600);
254         if (fd < 0) {
255                 perror(mmap_file);
256                 return -errno;
257         }
258         if (ftruncate(fd, region) < 0) {
259                 perror("ftruncate()");
260                 rc = -errno;
261                 goto out_close;
262         }
263
264         ptr = mmap(NULL, region, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
265         if (ptr == MAP_FAILED) {
266                 perror("mmap()");
267                 rc = -errno;
268                 goto out_close;
269         }
270
271         rc = mmap_run(3);
272         if (rc)
273                 goto out_unmap;
274
275         memset(ptr, 'a', region);
276         sleep(2);       /* wait for remote test finish */
277 out_unmap:
278         munmap(ptr, region);
279 out_close:
280         close(fd);
281         unlink(mmap_file);
282         return rc;
283 }
284
285 static int remote_tst3(char *mnt)
286 {
287         char *ptr, mmap_file[256];
288         int region, fd, rc = 0;
289
290         region = page_size * 100;
291         sprintf(mmap_file, "%s/%s", mnt, "mmap_file3");
292
293         fd = open(mmap_file, O_RDWR, 0600);
294         if (fd < 0) {
295                 perror(mmap_file);
296                 return -errno;
297         }
298
299         ptr = mmap(NULL, region, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
300         if (ptr == MAP_FAILED) {
301                 perror("mmap()");
302                 rc = -errno;
303                 goto out_close;
304         }
305         memset(ptr, 'b', region);
306         memset(ptr, 'c', region);
307
308         munmap(ptr, region);
309 out_close:
310         close(fd);
311         return rc;
312 }
313
314 /*
315  * client1 write to file_4a from mmap()ed file_4b;
316  * client2 write to file_4b from mmap()ed file_4a.
317  */
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
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
432         munmap(ptr, region);
433 out_close:
434         if (fdr >= 0)
435                 close(fdr);
436         if (fdw >= 0)
437                 close(fdw);
438         return rc;
439 }
440
441 static int cancel_lru_locks(char *filter)
442 {
443         glob_t paths;
444         pid_t child;
445         int rc, i;
446
447         child = fork();
448         if (child < 0) {
449                 return -errno;
450         } else if (child) {
451                 int status;
452
453                 rc = waitpid(child, &status, WNOHANG);
454                 if (rc == child)
455                         rc = 0;
456                 return rc;
457         }
458
459         if (filter)
460                 rc = cfs_get_param_paths(&paths,
461                                          "ldlm/namespaces/*-%s-*/lru_size",
462                                          filter);
463         else
464                 rc = cfs_get_param_paths(&paths,
465                                          "ldlm/namespaces/*/lru_size");
466         if (rc != 0)
467                 return -EINVAL;
468
469         for (i = 0; i < paths.gl_pathc; i++) {
470                 FILE *f = fopen(paths.gl_pathv[i], "r");
471
472                 if (!f) {
473                         rc = -errno;
474                         fprintf(stderr, "cannot open '%s': %s\n",
475                                 paths.gl_pathv[i], strerror(errno));
476                         break;
477                 }
478
479                 rc = fwrite("clear", strlen("clear") + 1, 1, f);
480                 if (rc < 1) {
481                         rc = -errno;
482                         fprintf(stderr, "fwrite failed for '%s': %s\n",
483                                 paths.gl_pathv[i], strerror(errno));
484                         fclose(f);
485                         break;
486                 }
487                 fclose(f);
488         }
489
490         cfs_free_param_data(&paths);
491         _exit(rc);
492 }
493
494 /*
495  * don't dead lock while read/write file to/from the buffer which
496  * mmaped to just this file
497  */
498 static int mmap_tst5(char *mnt)
499 {
500         char *ptr, mmap_file[256];
501         int region, fd, off, rc = 0;
502
503         region = page_size * 40;
504         off = page_size * 10;
505         sprintf(mmap_file, "%s/%s", mnt, "mmap_file5");
506
507         if (unlink(mmap_file) && errno != ENOENT) {
508                 perror("unlink()");
509                 return -errno;
510         }
511
512         fd = open(mmap_file, O_CREAT | O_RDWR, 0600);
513         if (fd < 0) {
514                 perror(mmap_file);
515                 return -errno;
516         }
517         if (ftruncate(fd, region) < 0) {
518                 perror("ftruncate()");
519                 rc = -errno;
520                 goto out_close;
521         }
522
523         ptr = mmap(NULL, region, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
524         if (ptr == MAP_FAILED) {
525                 perror("mmap()");
526                 rc = -errno;
527                 goto out_close;
528         }
529         memset(ptr, 'a', region);
530
531         /* cancel unused locks */
532         rc = cancel_lru_locks("osc");
533         if (rc)
534                 goto out_unmap;
535
536         /* read/write region of file and buffer should be overlap */
537         rc = read(fd, ptr + off, off * 2);
538         if (rc != off * 2) {
539                 perror("read()");
540                 rc = -errno;
541                 goto out_unmap;
542         }
543         rc = write(fd, ptr + off, off * 2);
544         if (rc != off * 2) {
545                 perror("write()");
546                 rc = -errno;
547         }
548         rc = 0;
549 out_unmap:
550         munmap(ptr, region);
551 out_close:
552         close(fd);
553         unlink(mmap_file);
554         return rc;
555 }
556
557 /* mmap write to a file form client1 then mmap read from client2 */
558 static int mmap_tst6(char *mnt)
559 {
560         char mmap_file[256], mmap_file2[256];
561         char *ptr = NULL, *ptr2 = NULL;
562         int fd = 0, fd2 = 0, rc = 0;
563
564         sprintf(mmap_file, "%s/%s", mnt, "mmap_file6");
565         sprintf(mmap_file2, "%s/%s", dir2, "mmap_file6");
566         if (unlink(mmap_file) && errno != ENOENT) {
567                 perror("unlink()");
568                 return -errno;
569         }
570
571         fd = open(mmap_file, O_CREAT | O_RDWR, 0600);
572         if (fd < 0) {
573                 perror(mmap_file);
574                 return -errno;
575         }
576         if (ftruncate(fd, page_size) < 0) {
577                 perror("ftruncate()");
578                 rc = -errno;
579                 goto out;
580         }
581
582         fd2 = open(mmap_file2, O_RDWR, 0600);
583         if (fd2 < 0) {
584                 perror(mmap_file2);
585                 rc = -errno;
586                 goto out;
587         }
588
589         ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
590         if (ptr == MAP_FAILED) {
591                 perror("mmap()");
592                 rc = -errno;
593                 goto out;
594         }
595
596         ptr2 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
597                     fd2, 0);
598         if (ptr2 == MAP_FAILED) {
599                 perror("mmap()");
600                 rc = -errno;
601                 goto out;
602         }
603
604         rc = cancel_lru_locks("osc");
605         if (rc)
606                 goto out;
607
608         memcpy(ptr, "blah", strlen("blah"));
609         if (strncmp(ptr, ptr2, strlen("blah"))) {
610                 fprintf(stderr, "client2 mmap mismatch!\n");
611                 rc = -EFAULT;
612                 goto out;
613         }
614         memcpy(ptr2, "foo", strlen("foo"));
615         if (strncmp(ptr, ptr2, strlen("foo"))) {
616                 fprintf(stderr, "client1 mmap mismatch!\n");
617                 rc = -EFAULT;
618         }
619 out:
620         if (ptr2)
621                 munmap(ptr2, page_size);
622         if (ptr)
623                 munmap(ptr, page_size);
624         if (fd2 > 0)
625                 close(fd2);
626         if (fd > 0)
627                 close(fd);
628         unlink(mmap_file);
629         return rc;
630 }
631
632 static int mmap_tst7_func(char *mnt, int rw)
633 {
634         char  fname[256];
635         char *buf = MAP_FAILED;
636         ssize_t bytes;
637         int fd = -1;
638         int rc = 0;
639
640         if (snprintf(fname, 256, "%s/mmap_tst7.%s", mnt,
641                      (rw == 0) ? "read" : "write") >= 256) {
642                 fprintf(stderr, "dir name too long\n");
643                 rc = -ENAMETOOLONG;
644                 goto out;
645         }
646         fd = open(fname, O_RDWR | O_DIRECT | O_CREAT, 0644);
647         if (fd == -1) {
648                 perror("open");
649                 rc = -errno;
650                 goto out;
651         }
652         if (ftruncate(fd, 2 * page_size) == -1) {
653                 perror("truncate");
654                 rc = -errno;
655                 goto out;
656         }
657         buf = mmap(NULL, page_size * 2,
658                    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
659         if (buf == MAP_FAILED) {
660                 perror("mmap");
661                 rc = -errno;
662                 goto out;
663         }
664         /* ensure the second page isn't mapped */
665         munmap(buf + page_size, page_size);
666         bytes = (rw == 0) ? read(fd, buf, 2 * page_size) :
667                 write(fd, buf, 2 * page_size);
668         /* Expected behavior */
669         if (bytes == page_size)
670                 goto out;
671
672         fprintf(stderr, "%s returned %zd, errno = %d\n",
673                 (rw == 0) ? "read" : "write", bytes, errno);
674         rc = -EIO;
675 out:
676         if (buf != MAP_FAILED)
677                 munmap(buf, page_size);
678         if (fd != -1)
679                 close(fd);
680         return rc;
681 }
682
683 static int mmap_tst7(char *mnt)
684 {
685         int rc;
686
687         rc = mmap_tst7_func(mnt, 0);
688         if (rc != 0)
689                 return rc;
690         rc = mmap_tst7_func(mnt, 1);
691         return rc;
692 }
693
694 static int mmap_tst8(char *mnt)
695 {
696         char  fname[256];
697         char *buf = MAP_FAILED;
698         int fd = -1;
699         int rc = 0;
700         pid_t pid;
701         char xyz[page_size * 2];
702
703         if (snprintf(fname, 256, "%s/mmap_tst8", mnt) >= 256) {
704                 fprintf(stderr, "dir name too long\n");
705                 rc = -ENAMETOOLONG;
706                 goto out;
707         }
708         fd = open(fname, O_RDWR | O_CREAT, 0644);
709         if (fd == -1) {
710                 perror("open");
711                 rc = -errno;
712                 goto out;
713         }
714         if (ftruncate(fd, page_size) == -1) {
715                 perror("truncate");
716                 rc = -errno;
717                 goto out;
718         }
719         buf = mmap(NULL, page_size * 2,
720                    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
721         if (buf == MAP_FAILED) {
722                 perror("mmap");
723                 rc = -errno;
724                 goto out;
725         }
726
727         pid = fork();
728         if (pid == 0) { /* child */
729                 memcpy(xyz, buf, page_size * 2);
730                 /* shouldn't reach here. */
731                 exit(0);
732         } else if (pid > 0) { /* parent */
733                 int status = 0;
734
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 mmap_tst9(char *mnt)
759 {
760         char  fname[256];
761         char *buf = MAP_FAILED;
762         int fd = -1;
763         int rc = 0;
764
765         if (snprintf(fname, 256, "%s/mmap_tst9", mnt) >= 256) {
766                 fprintf(stderr, "dir name too long\n");
767                 rc = -ENAMETOOLONG;
768                 goto out;
769         }
770         if (unlink(fname) == -1 && errno != ENOENT) {
771                 perror("unlink");
772                 rc = -errno;
773                 goto out;
774         }
775         fd = open(fname, O_RDWR | O_CREAT, 0644);
776         if (fd == -1) {
777                 perror("open");
778                 rc = -errno;
779                 goto out;
780         }
781         buf = mmap(NULL, page_size * 2,
782                    PROT_READ, MAP_PRIVATE, fd, (loff_t)(-10 * page_size));
783         if (buf == MAP_FAILED) {
784                 perror("mmap");
785                 rc = -errno;
786                 goto out;
787         }
788         rc = write(STDOUT_FILENO, buf, 2 * page_size);
789         if (rc != -1) {
790                 fprintf(stderr, "write succeded with %d instead of failing\n",
791                         rc);
792                 rc = -EINVAL;
793                 goto out;
794         } else if (errno != EFAULT) {
795                 fprintf(stderr, "write failed with %d instead of EFAULT(%d)\n",
796                         errno, EFAULT);
797                 rc = -errno;
798                 goto out;
799         }
800         rc = 0;
801 out:
802         if (buf != MAP_FAILED)
803                 munmap(buf, page_size * 2);
804         if (fd != -1)
805                 close(fd);
806         return rc;
807 }
808
809 static int remote_tst(int tc, char *mnt)
810 {
811         int rc = 0;
812
813         switch (tc) {
814         case 3:
815                 rc = remote_tst3(mnt);
816                 break;
817         case 4:
818                 rc = remote_tst4(mnt);
819                 break;
820         default:
821                 fprintf(stderr, "wrong test case number %d\n", tc);
822                 rc = -EINVAL;
823                 break;
824         }
825         return rc;
826 }
827
828 struct test_case {
829         int tc;                     /* test case number */
830         char *desc;                 /* test description */
831         int (*test_fn)(char *mnt);  /* test function */
832         int node_cnt;               /* node count */
833         int skipped;                /* skipped by caller */
834 };
835
836 struct test_case tests[] = {
837         {
838                 .tc             = 1,
839                 .desc           = "mmap test1: basic mmap operation",
840                 .test_fn        = mmap_tst1,
841                 .node_cnt       = 1
842         },
843         {
844                 .tc             = 2,
845                 .desc           = "mmap test2: MAP_PRIVATE not write back",
846                 .test_fn        = mmap_tst2,
847                 .node_cnt       = 1
848         },
849         {
850                 .tc             = 3,
851                 .desc           = "mmap test3: concurrent mmap ops on two nodes",
852                 .test_fn        = mmap_tst3,
853                 .node_cnt       = 2
854         },
855         {
856                 .tc             = 4,
857                 .desc           = "mmap test4: c1 write to f1 from mmapped f2, c2 write to f1 from mmapped f1",
858                 .test_fn        = mmap_tst4,
859                 .node_cnt       = 2
860         },
861         {
862                 .tc             = 5,
863                 .desc           = "mmap test5: read/write file to/from the buffer which mmapped to just this file",
864                 .test_fn        = mmap_tst5,
865                 .node_cnt       = 1
866         },
867         {
868                 .tc             = 6,
869                 .desc           = "mmap test6: check mmap write/read content on two nodes",
870                 .test_fn        = mmap_tst6,
871                 .node_cnt       = 2
872         },
873         {
874                 .tc             = 7,
875                 .desc           = "mmap test7: file i/o with an unmapped buffer",
876                 .test_fn        = mmap_tst7,
877                 .node_cnt       = 1
878         },
879         {
880                 .tc             = 8,
881                 .desc           = "mmap test8: SIGBUS for beyond file size",
882                 .test_fn        = mmap_tst8,
883                 .node_cnt       = 1
884         },
885         {
886                 .tc             = 9,
887                 .desc           = "mmap test9: SIGBUS for negative file offset",
888                 .test_fn        = mmap_tst9,
889                 .node_cnt       = 1
890         },
891         {
892                 .tc             = 0
893         }
894 };
895
896 int main(int argc, char **argv)
897 {
898         struct test_case *test;
899         int nr_cases = sizeof(tests) / sizeof(*test);
900         int c, rc = 0;
901
902         while ((c = getopt(argc, argv, "d:m:e:")) != -1) {
903                 switch (c) {
904                 case 'd':
905                         dir = optarg;
906                         break;
907                 case 'm':
908                         dir2 = optarg;
909                         break;
910                 case 'e': {
911                         char *endptr = NULL;
912
913                         rc = strtol(optarg, &endptr, 10);
914                         if (endptr && *endptr != '\0')
915                                 usage();
916                         if (rc > 0 && rc < nr_cases)
917                                 tests[rc - 1].skipped = 1;
918                         break;
919                 }
920                 default:
921                         usage();
922                         break;
923                 }
924         }
925
926         if (!dir)
927                 usage();
928
929         if (mmap_initialize(argv[0]) != 0) {
930                 fprintf(stderr, "mmap_initialize failed!\n");
931                 return -EINVAL;
932         }
933
934         rc = 0;
935         for (test = tests; test->tc; test++) {
936                 double duration = 0.0;
937                 char *rs = "SKIPPED";
938
939                 if (!test->skipped && (test->node_cnt == 1 || dir2)) {
940                         struct timeval start, end;
941
942                         gettimeofday(&start, NULL);
943                         rc = test->test_fn(dir);
944                         gettimeofday(&end, NULL);
945
946                         duration = (double)(end.tv_sec - start.tv_sec) +
947                                 (double)(end.tv_usec - start.tv_usec) / 1000000;
948                         rs = rc ? "FAIL" : "PASS";
949                 }
950
951                 fprintf(stderr, "%s (%s, %.5gs)\n", test->desc, rs, duration);
952                 if (rc)
953                         break;
954         }
955
956         mmap_finalize();
957         return -rc;
958 }