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