Whamcloud - gitweb
LU-1146 build: batch update copyright messages
[fs/lustre-release.git] / lustre / tests / mmap_sanity.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
31  *
32  * Copyright (c) 2012, Whamcloud, Inc.
33  */
34 /*
35  * This file is part of Lustre, http://www.lustre.org/
36  * Lustre is a trademark of Sun Microsystems, Inc.
37  */
38
39 #include <stdio.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <sys/mman.h>
43 #include <errno.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <fcntl.h>
47 #include <netinet/in.h>
48 #include <sys/socket.h>
49 #include <netdb.h>
50 #include <string.h>
51 #include <sys/wait.h>
52
53 char *dir = NULL, *dir2 = NULL;
54 long page_size;
55 char mmap_sanity[256];
56
57
58 static void usage(void)
59 {
60         printf("Usage: mmap_sanity -d dir [-m dir2]\n");
61         printf("       dir      lustre mount point\n");
62         printf("       dir2     another mount point\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, "ls /proc/fs/lustre/ldlm/namespaces/*-%s-*/lru_size", prefix);
462         else
463                 sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*/lru_size");
464
465         file = popen(cmd, "r");
466         if (file == NULL) {
467                 perror("popen()");
468                 return errno;
469         }
470
471         while (fgets(line, len, file)) {
472                 FILE *f;
473
474                 if (!strlen(line))
475                         continue;
476                 /* trim newline character */
477                 *(line + strlen(line) - 1) = '\0';
478                 f = fopen(line, "w");
479                 if (f == NULL) {
480                         perror("fopen()");
481                         rc = errno;
482                         break;
483                 }
484                 rc = fwrite("clear", strlen("clear") + 1, 1, f);
485                 if (rc < 1) {
486                         perror("fwrite()");
487                         rc = errno;
488                         fclose(f);
489                         break;
490                 }
491                 fclose(f);
492         }
493
494         pclose(file);
495         _exit(rc);
496 }
497
498 /* don't dead lock while read/write file to/from the buffer which
499  * mmaped to just this file */
500 static int mmap_tst5(char *mnt)
501 {
502         char *ptr, mmap_file[256];
503         int region, fd, off, rc = 0;
504
505         region = page_size * 40;
506         off = page_size * 10;
507         sprintf(mmap_file, "%s/%s", mnt, "mmap_file5");
508
509         if (unlink(mmap_file) && errno != ENOENT) {
510                 perror("unlink()");
511                 return errno;
512         }
513
514         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
515         if (fd < 0) {
516                 perror(mmap_file);
517                 return errno;
518         }
519         if (ftruncate(fd, region) < 0) {
520                 perror("ftruncate()");
521                 rc = errno;
522                 goto out_close;
523         }
524
525         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
526         if (ptr == MAP_FAILED) {
527                 perror("mmap()");
528                 rc = errno;
529                 goto out_close;
530         }
531         memset(ptr, 'a', region);
532
533         /* cancel unused locks */
534         rc = cancel_lru_locks("osc");
535         if (rc)
536                 goto out_unmap;
537
538         /* read/write region of file and buffer should be overlap */
539         rc = read(fd, ptr + off, off * 2);
540         if (rc != off * 2) {
541                 perror("read()");
542                 rc = errno;
543                 goto out_unmap;
544         }
545         rc = write(fd, ptr + off, off * 2);
546         if (rc != off * 2) {
547                 perror("write()");
548                 rc = errno;
549         }
550         rc = 0;
551 out_unmap:
552         munmap(ptr, region);
553 out_close:
554         close(fd);
555         unlink(mmap_file);
556         return rc;
557 }
558
559 /* mmap write to a file form client1 then mmap read from client2 */
560 static int mmap_tst6(char *mnt)
561 {
562         char mmap_file[256], mmap_file2[256];
563         char *ptr = NULL, *ptr2 = NULL;
564         int fd = 0, fd2 = 0, rc = 0;
565
566         sprintf(mmap_file, "%s/%s", mnt, "mmap_file6");
567         sprintf(mmap_file2, "%s/%s", dir2, "mmap_file6");
568         if (unlink(mmap_file) && errno != ENOENT) {
569                 perror("unlink()");
570                 return errno;
571         }
572
573         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
574         if (fd < 0) {
575                 perror(mmap_file);
576                 return errno;
577         }
578         if (ftruncate(fd, page_size) < 0) {
579                 perror("ftruncate()");
580                 rc = errno;
581                 goto out;
582         }
583
584         fd2 = open(mmap_file2, O_RDWR, 0600);
585         if (fd2 < 0) {
586                 perror(mmap_file2);
587                 goto out;
588         }
589
590         ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
591         if (ptr == MAP_FAILED) {
592                 perror("mmap()");
593                 rc = errno;
594                 goto out;
595         }
596         
597         ptr2 = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, 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",
641                      mnt, (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         fprintf(stderr, "%s returned %zd, errno = %d\n",
672                 (rw == 0)?"read":"write", bytes, errno);
673         rc = EIO;
674 out:
675         if (buf != MAP_FAILED)
676                 munmap(buf, page_size);
677         if (fd != -1)
678                 close(fd);
679         return rc;
680 }
681
682 static int mmap_tst7(char *mnt)
683 {
684         int rc;
685
686         rc = mmap_tst7_func(mnt, 0);
687         if (rc != 0)
688                 return rc;
689         rc = mmap_tst7_func(mnt, 1);
690         return rc;
691 }
692
693 static int mmap_tst8(char *mnt)
694 {
695         char  fname[256];
696         char *buf = MAP_FAILED;
697         int fd = -1;
698         int rc = 0;
699         pid_t pid;
700         char xyz[page_size * 2];
701
702         if (snprintf(fname, 256, "%s/mmap_tst8", mnt) >= 256) {
703                 fprintf(stderr, "dir name too long\n");
704                 rc = ENAMETOOLONG;
705                 goto out;
706         }
707         fd = open(fname, O_RDWR | O_CREAT, 0644);
708         if (fd == -1) {
709                 perror("open");
710                 rc = errno;
711                 goto out;
712         }
713         if (ftruncate(fd, page_size) == -1) {
714                 perror("truncate");
715                 rc = errno;
716                 goto out;
717         }
718         buf = mmap(NULL, page_size * 2,
719                    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
720         if (buf == MAP_FAILED) {
721                 perror("mmap");
722                 rc = errno;
723                 goto out;
724         }
725
726         pid = fork();
727         if (pid == 0) { /* child */
728                 memcpy(xyz, buf, page_size * 2);
729                 /* shouldn't reach here. */
730                 exit(0);
731         } else if (pid > 0) { /* parent */
732                 int status = 0;
733                 pid = waitpid(pid, &status, 0);
734                 if (pid < 0) {
735                         perror("wait");
736                         rc = errno;
737                         goto out;
738                 }
739
740                 rc = EFAULT;
741                 if (WIFSIGNALED(status) && SIGBUS == WTERMSIG(status))
742                         rc = 0;
743         } else {
744                 perror("fork");
745                 rc = errno;
746         }
747
748 out:
749         if (buf != MAP_FAILED)
750                 munmap(buf, page_size);
751         if (fd != -1)
752                 close(fd);
753         return rc;
754 }
755
756 static int remote_tst(int tc, char *mnt)
757 {
758         int rc = 0;
759         switch(tc) {
760         case 3:
761                 rc = remote_tst3(mnt);
762                 break;
763         case 4:
764                 rc = remote_tst4(mnt);
765                 break;
766         default:
767                 fprintf(stderr, "wrong test case number %d\n", tc);
768                 rc = EINVAL;
769                 break;
770         }
771         return rc;
772 }
773
774 struct test_case {
775         int     tc;                     /* test case number */
776         char    *desc;                  /* test description */
777         int     (* test_fn)(char *mnt); /* test function */
778         int     node_cnt;               /* node count */
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         extern char *optarg;
799         struct test_case *test;
800         int c, rc = 0;
801
802         for(;;) {
803                 c = getopt(argc, argv, "d:m:");
804                 if ( c == -1 )
805                         break;
806
807                 switch(c) {
808                         case 'd':
809                                 dir = optarg;
810                                 break;
811                         case 'm':
812                                 dir2 = optarg;
813                                 break;
814                         default:
815                         case '?':
816                                 usage();
817                                 break;
818                 }
819         }
820
821         if (dir == NULL)
822                 usage();
823
824         if (mmap_initialize(argv[0]) != 0) {
825                 fprintf(stderr, "mmap_initialize failed!\n");
826                 return EINVAL;
827         }
828
829         for (test = tests; test->tc; test++) {
830                 char *rs = "skip";
831                 rc = 0;
832                 if (test->node_cnt == 1 || dir2 != NULL) {
833                         rc = test->test_fn(dir);
834                         rs = rc ? "fail" : "pass";
835                 }
836                 fprintf(stderr, "%s (%s)\n", test->desc, rs);
837                 if (rc)
838                         break;
839         }
840
841         mmap_finalize();
842         return rc;
843 }