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