Whamcloud - gitweb
b=16488
[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  2008 Sun Microsystems, Inc. All rights reserved
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  */
36
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <sys/mman.h>
41 #include <errno.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <netinet/in.h>
46 #include <sys/socket.h>
47 #include <netdb.h>
48 #include <string.h>
49 #include <sys/wait.h>
50
51 char *dir = NULL, *dir2 = NULL;
52 long page_size;
53 char mmap_sanity[256];
54
55
56 static void usage(void)
57 {
58         printf("Usage: mmap_sanity -d dir [-m dir2]\n");
59         printf("       dir      lustre mount point\n");
60         printf("       dir2     another mount point\n");
61         exit(127);
62 }
63
64 static int remote_tst(int tc, char *mnt);
65 static int mmap_run(int tc)
66 {
67         pid_t child;
68         int rc = 0;
69
70         child = fork();
71         if (child < 0)
72                 return errno;
73         else if (child)
74                 return 0;
75
76         if (dir2 != NULL) {
77                 rc = remote_tst(tc, dir2);
78         } else {
79                 rc = EINVAL;
80                 fprintf(stderr, "invalid argument!\n");
81         }
82         _exit(rc);
83 }
84
85 static int mmap_initialize(char *myself)
86 {
87         char buf[1024], *file;
88         int fdr, fdw, count, rc = 0;
89         
90         page_size = sysconf(_SC_PAGESIZE);
91         if (page_size == -1) {
92                 perror("sysconf(_SC_PAGESIZE)");
93                 return errno;
94         }
95
96         /* copy myself to lustre for another client */
97         fdr = open(myself, O_RDONLY);
98         if (fdr < 0) {
99                 perror(myself);
100                 return EINVAL;
101         }
102         file = strrchr(myself, '/');
103         if (file == NULL) {
104                 fprintf(stderr, "can't get test filename\n");
105                 close(fdr);
106                 return EINVAL;
107         }
108         file++;
109         sprintf(mmap_sanity, "%s/%s", dir, file);
110
111         fdw = open(mmap_sanity, O_CREAT|O_WRONLY, 0777);
112         if (fdw < 0) {
113                 perror(mmap_sanity);
114                 close(fdr);
115                 return EINVAL;
116         }
117         while ((count = read(fdr, buf, sizeof(buf))) != 0) {
118                 int writes;
119
120                 if (count < 0) {
121                         perror("read()");
122                         rc = errno;
123                         break;
124                 }
125                 writes = write(fdw, buf, count);
126                 if (writes != count) {
127                         perror("write()");
128                         rc = errno;
129                         break;
130                 }
131         }
132         close(fdr);
133         close(fdw);
134         return rc;
135 }
136
137 static void mmap_finalize()
138 {
139         unlink(mmap_sanity);
140 }
141
142 /* basic mmap operation on single node */
143 static int mmap_tst1(char *mnt)
144 {
145         char *ptr, mmap_file[256];
146         int region, fd, rc = 0;
147
148         region = page_size * 10;
149         sprintf(mmap_file, "%s/%s", mnt, "mmap_file1");
150         
151         if (unlink(mmap_file) && errno != ENOENT) {
152                 perror("unlink()");
153                 return errno;
154         }
155
156         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
157         if (fd < 0) {
158                 perror(mmap_file);
159                 return errno;
160         }
161         ftruncate(fd, region);
162
163         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
164         if (ptr == MAP_FAILED) {
165                 perror("mmap()");
166                 rc = errno;
167                 goto out_close;
168         }
169         memset(ptr, 'a', region);
170
171         munmap(ptr, region);
172 out_close:
173         close(fd);
174         unlink(mmap_file);
175         return rc;
176 }
177
178 /* MAP_PRIVATE create a copy-on-write mmap */
179 static int mmap_tst2(char *mnt)
180 {
181         char *ptr, mmap_file[256], buf[256];
182         int fd, rc = 0;
183
184         sprintf(mmap_file, "%s/%s", mnt, "mmap_file2");
185
186         if (unlink(mmap_file) && errno != ENOENT) {
187                 perror("unlink()");
188                 return errno;
189         }
190
191         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
192         if (fd < 0) {
193                 perror(mmap_file);
194                 return errno;
195         }
196         ftruncate(fd, page_size);
197
198         ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
199         if (ptr == MAP_FAILED) {
200                 perror("mmap()");
201                 rc = errno;
202                 goto out_close;
203         }
204         memcpy(ptr, "blah", strlen("blah"));
205
206         munmap(ptr, page_size);
207 out_close:
208         close(fd);
209         if (rc)
210                 return rc;
211
212         fd = open(mmap_file, O_RDONLY);
213         if (fd < 0) {
214                 perror(mmap_file);
215                 return errno;
216         }
217         rc = read(fd, buf, sizeof(buf));
218         if (rc < 0) {
219                 perror("read()");
220                 rc = errno;
221                 goto out_close;
222         }
223         rc = 0;
224         
225         if (strncmp("blah", buf, strlen("blah")) == 0) {
226                 fprintf(stderr, "mmap write back with MAP_PRIVATE!\n");
227                 rc = EFAULT;
228         }
229         close(fd);
230         unlink(mmap_file);
231         return rc;
232 }
233
234 /* concurrent mmap operations on two nodes */
235 static int mmap_tst3(char *mnt)
236 {
237         char *ptr, mmap_file[256];
238         int region, fd, rc = 0;
239
240         region = page_size * 100;
241         sprintf(mmap_file, "%s/%s", mnt, "mmap_file3");
242         
243         if (unlink(mmap_file) && errno != ENOENT) {
244                 perror("unlink()");
245                 return errno;
246         }
247
248         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
249         if (fd < 0) {
250                 perror(mmap_file);
251                 return errno;
252         }
253         ftruncate(fd, region);
254
255         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
256         if (ptr == MAP_FAILED) {
257                 perror("mmap()");
258                 rc = errno;
259                 goto out_close;
260         }
261
262         rc = mmap_run(3);
263         if (rc)
264                 goto out_unmap;
265         
266         memset(ptr, 'a', region);
267         sleep(2);       /* wait for remote test finish */
268 out_unmap:
269         munmap(ptr, region);
270 out_close:
271         close(fd);
272         unlink(mmap_file);
273         return rc;
274 }       
275
276 static int remote_tst3(char *mnt)
277 {
278         char *ptr, mmap_file[256];
279         int region, fd, rc = 0;
280
281         region = page_size * 100;
282         sprintf(mmap_file, "%s/%s", mnt, "mmap_file3");
283
284         fd = open(mmap_file, O_RDWR, 0600);
285         if (fd < 0) {
286                 perror(mmap_file);
287                 return errno;
288         }
289
290         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
291         if (ptr == MAP_FAILED) {
292                 perror("mmap()");
293                 rc = errno;
294                 goto out_close;
295         }
296         memset(ptr, 'b', region);
297         memset(ptr, 'c', region);
298         
299         munmap(ptr, region);
300 out_close:
301         close(fd);
302         return rc;
303 }
304
305 /* client1 write to file_4a from mmap()ed file_4b;
306  * client2 write to file_4b from mmap()ed file_4a. */
307 static int mmap_tst4(char *mnt)
308 {
309         char *ptr, filea[256], fileb[256];
310         int region, fdr, fdw, rc = 0;
311
312         region = page_size * 100;
313         sprintf(filea, "%s/%s", mnt, "mmap_file_4a");
314         sprintf(fileb, "%s/%s", mnt, "mmap_file_4b");
315
316         if (unlink(filea) && errno != ENOENT) {
317                 perror("unlink()");
318                 return errno;
319         }
320         if (unlink(fileb) && errno != ENOENT) {
321                 perror("unlink()");
322                 return errno;
323         }
324
325         fdr = fdw = -1;
326         fdr = open(fileb, O_CREAT|O_RDWR, 0600);
327         if (fdr < 0) {
328                 perror(fileb);
329                 return errno;
330         }
331         ftruncate(fdr, region);
332         fdw = open(filea, O_CREAT|O_RDWR, 0600);
333         if (fdw < 0) {
334                 perror(filea);
335                 rc = errno;
336                 goto out_close;
337         }
338         ftruncate(fdw, region);
339         
340         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fdr, 0);
341         if (ptr == MAP_FAILED) {
342                 perror("mmap()");
343                 rc = errno;
344                 goto out_close;
345         }
346
347         rc = mmap_run(4);
348         if (rc)
349                 goto out_unmap;
350         
351         memset(ptr, '1', region);
352         
353         rc = write(fdw, ptr, region);
354         if (rc <= 0) {
355                 perror("write()");
356                 rc = errno;
357         } else
358                 rc = 0;
359
360         sleep(2);       /* wait for remote test finish */
361 out_unmap:
362         munmap(ptr, region);
363 out_close:
364         if (fdr >= 0)
365                 close(fdr);
366         if (fdw >= 0)
367                 close(fdw);
368         unlink(filea);
369         unlink(fileb);
370         return rc;
371 }
372
373 static int remote_tst4(char *mnt)
374 {
375         char *ptr, filea[256], fileb[256];
376         int region, fdr, fdw, rc = 0;
377
378         region = page_size * 100;
379         sprintf(filea, "%s/%s", mnt, "mmap_file_4a");
380         sprintf(fileb, "%s/%s", mnt, "mmap_file_4b");
381
382         fdr = fdw = -1;
383         fdr = open(filea, O_RDWR, 0600);
384         if (fdr < 0) {
385                 perror(filea);
386                 return errno;
387         }
388         fdw = open(fileb, O_RDWR, 0600);
389         if (fdw < 0) {
390                 perror(fileb);
391                 rc = errno;
392                 goto out_close;
393         }
394
395         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fdr, 0);
396         if (ptr == MAP_FAILED) {
397                 perror("mmap()");
398                 rc = errno;
399                 goto out_close;
400         }
401
402         memset(ptr, '2', region);
403
404         rc = write(fdw, ptr, region);
405         if (rc <= 0) {
406                 perror("write()");
407                 rc = errno;
408         } else
409                 rc = 0;
410      
411         munmap(ptr, region);
412 out_close:
413         if (fdr >= 0)
414                 close(fdr);
415         if (fdw >= 0)
416                 close(fdw);
417         return rc;
418 }
419
420 static int cancel_lru_locks(char *prefix)
421 {
422         char cmd[256], line[1024];
423         FILE *file;
424         pid_t child;
425         int len = 1024, rc = 0;
426
427         child = fork();
428         if (child < 0)
429                 return errno;
430         else if (child) {
431                 int status;
432                 rc = waitpid(child, &status, WNOHANG);
433                 if (rc == child)
434                         rc = 0;
435                 return rc;
436         }
437
438         if (prefix)
439                 sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*-%s-*/lru_size", prefix);
440         else
441                 sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*/lru_size");
442
443         file = popen(cmd, "r");
444         if (file == NULL) {
445                 perror("popen()");
446                 return errno;
447         }
448
449         while (fgets(line, len, file)) {
450                 FILE *f;
451
452                 if (!strlen(line))
453                         continue;
454                 /* trim newline character */
455                 *(line + strlen(line) - 1) = '\0';
456                 f = fopen(line, "w");
457                 if (f == NULL) {
458                         perror("fopen()");
459                         rc = errno;
460                         break;
461                 }
462                 rc = fwrite("clear", strlen("clear") + 1, 1, f);
463                 if (rc < 1) {
464                         perror("fwrite()");
465                         rc = errno;
466                         fclose(f);
467                         break;
468                 }
469                 fclose(f);
470         }
471
472         pclose(file);
473         _exit(rc);
474 }
475
476 /* don't dead lock while read/write file to/from the buffer which
477  * mmaped to just this file */
478 static int mmap_tst5(char *mnt)
479 {
480         char *ptr, mmap_file[256];
481         int region, fd, off, rc = 0;
482
483         region = page_size * 40;
484         off = page_size * 10;
485         sprintf(mmap_file, "%s/%s", mnt, "mmap_file5");
486
487         if (unlink(mmap_file) && errno != ENOENT) {
488                 perror("unlink()");
489                 return errno;
490         }
491
492         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
493         if (fd < 0) {
494                 perror(mmap_file);
495                 return errno;
496         }
497         ftruncate(fd, region);
498
499         ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
500         if (ptr == MAP_FAILED) {
501                 perror("mmap()");
502                 rc = errno;
503                 goto out_close;
504         }
505         memset(ptr, 'a', region);
506
507         /* cancel unused locks */
508         rc = cancel_lru_locks("osc");
509         if (rc)
510                 goto out_unmap;
511
512         /* read/write region of file and buffer should be overlap */
513         rc = read(fd, ptr + off, off * 2);
514         if (rc != off * 2) {
515                 perror("read()");
516                 rc = errno;
517                 goto out_unmap;
518         }
519         rc = write(fd, ptr + off, off * 2);
520         if (rc != off * 2) {
521                 perror("write()");
522                 rc = errno;
523         }
524         rc = 0;
525 out_unmap:
526         munmap(ptr, region);
527 out_close:
528         close(fd);
529         unlink(mmap_file);
530         return rc;
531 }
532
533 /* mmap write to a file form client1 then mmap read from client2 */
534 static int mmap_tst6(char *mnt)
535 {
536         char mmap_file[256], mmap_file2[256];
537         char *ptr = NULL, *ptr2 = NULL;
538         int fd = 0, fd2 = 0, rc = 0;
539
540         sprintf(mmap_file, "%s/%s", mnt, "mmap_file6");
541         sprintf(mmap_file2, "%s/%s", dir2, "mmap_file6");
542         if (unlink(mmap_file) && errno != ENOENT) {
543                 perror("unlink()");
544                 return errno;
545         }
546
547         fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
548         if (fd < 0) {
549                 perror(mmap_file);
550                 return errno;
551         }
552         ftruncate(fd, page_size);
553
554         fd2 = open(mmap_file2, O_RDWR, 0600);
555         if (fd2 < 0) {
556                 perror(mmap_file2);
557                 goto out;
558         }
559
560         ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
561         if (ptr == MAP_FAILED) {
562                 perror("mmap()");
563                 rc = errno;
564                 goto out;
565         }
566         
567         ptr2 = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
568         if (ptr2 == MAP_FAILED) {
569                 perror("mmap()");
570                 rc = errno;
571                 goto out;
572         }
573
574         rc = cancel_lru_locks("osc");
575         if (rc)
576                 goto out;
577
578         memcpy(ptr, "blah", strlen("blah"));
579         if (strncmp(ptr, ptr2, strlen("blah"))) {
580                 fprintf(stderr, "client2 mmap mismatch!\n");
581                 rc = EFAULT;
582                 goto out;
583         }
584         memcpy(ptr2, "foo", strlen("foo"));
585         if (strncmp(ptr, ptr2, strlen("foo"))) {
586                 fprintf(stderr, "client1 mmap mismatch!\n");
587                 rc = EFAULT;
588         }
589 out:
590         if (ptr2)
591                 munmap(ptr2, page_size);
592         if (ptr)
593                 munmap(ptr, page_size);
594         if (fd2 > 0)
595                 close(fd2);
596         if (fd > 0)
597                 close(fd);
598         unlink(mmap_file);
599         return rc;
600 }
601
602 static int remote_tst(int tc, char *mnt)
603 {
604         int rc = 0;
605         switch(tc) {
606         case 3:
607                 rc = remote_tst3(mnt);
608                 break;
609         case 4:
610                 rc = remote_tst4(mnt);
611                 break;
612         default:
613                 fprintf(stderr, "wrong test case number %d\n", tc);
614                 rc = EINVAL;
615                 break;
616         }
617         return rc;
618 }
619         
620 struct test_case {
621         int     tc;                     /* test case number */
622         char    *desc;                  /* test description */
623         int     (* test_fn)(char *mnt); /* test function */
624         int     node_cnt;               /* node count */
625 };
626
627 struct test_case tests[] = {
628         { 1, "mmap test1: basic mmap operation", mmap_tst1, 1 },
629         { 2, "mmap test2: MAP_PRIVATE not write back", mmap_tst2, 1 },
630         { 3, "mmap test3: concurrent mmap ops on two nodes", mmap_tst3, 2 },
631         { 4, "mmap test4: c1 write to f1 from mmapped f2, " 
632              "c2 write to f1 from mmapped f1", mmap_tst4, 2 },
633         { 5, "mmap test5: read/write file to/from the buffer "
634              "which mmapped to just this file", mmap_tst5, 1 },
635         { 6, "mmap test6: check mmap write/read content on two nodes", 
636                 mmap_tst6, 2 },
637         { 0, NULL, 0, 0 }
638 };
639
640 int main(int argc, char **argv)
641 {
642         extern char *optarg;
643         struct test_case *test;
644         int c, rc = 0;
645
646         for(;;) {
647                 c = getopt(argc, argv, "d:m:");
648                 if ( c == -1 )
649                         break;
650
651                 switch(c) {
652                         case 'd':
653                                 dir = optarg;
654                                 break;
655                         case 'm':
656                                 dir2 = optarg;
657                                 break;
658                         default:
659                         case '?':
660                                 usage();
661                                 break;
662                 }
663         }
664
665         if (dir == NULL)
666                 usage();
667
668         if (mmap_initialize(argv[0]) != 0) {
669                 fprintf(stderr, "mmap_initialize failed!\n");
670                 return EINVAL;
671         }
672
673         for (test = tests; test->tc; test++) {
674                 char *rs = "skip";
675                 rc = 0;
676                 if (test->node_cnt == 1 || dir2 != NULL) {
677                         rc = test->test_fn(dir);
678                         rs = rc ? "fail" : "pass";
679                 }
680                 fprintf(stderr, "%s (%s)\n", test->desc, rs);
681                 if (rc)
682                         break;
683         }
684
685         mmap_finalize();
686         return rc;
687 }