Whamcloud - gitweb
LU-17705 ptlrpc: replace synchronize_rcu() with rcu_barrier()
[fs/lustre-release.git] / lustre / tests / flocks_test.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) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2011, 2014, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  */
31
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <pthread.h>
39 #include <sys/file.h>
40 #include <sys/wait.h>
41 #include <sys/time.h>
42 #include <stdarg.h>
43 #include <ctype.h>
44
45 #define MAX_PATH_LENGTH 4096
46
47
48 static double now(void)
49 {
50         struct timeval tv;
51
52         gettimeofday(&tv, NULL);
53         return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
54 }
55
56 /* helper functions */
57 static int t_fcntl(int fd, int cmd, ...)
58 {
59         va_list ap;
60         long arg;
61         struct flock *lock;
62         int rc = -1;
63
64         va_start(ap, cmd);
65         switch (cmd) {
66         case F_GETFL:
67                 va_end(ap);
68                 rc = fcntl(fd, cmd);
69                 if (rc == -1) {
70                         rc = -errno;
71                         fprintf(stderr, "fcntl GETFL failed: %s\n",
72                                 strerror(errno));
73                         return rc;
74                 }
75                 break;
76         case F_SETFL:
77                 arg = va_arg(ap, long);
78                 va_end(ap);
79                 rc = fcntl(fd, cmd, arg);
80                 if (rc == -1) {
81                         rc = -errno;
82                         fprintf(stderr, "fcntl SETFL %ld failed: %s\n",
83                                 arg, strerror(errno));
84                         return rc;
85                 }
86                 break;
87         case F_GETLK:
88         case F_SETLK:
89         case F_SETLKW:
90                 lock = va_arg(ap, struct flock *);
91                 va_end(ap);
92                 rc = fcntl(fd, cmd, lock);
93                 if (rc == -1) {
94                         rc = -errno;
95                         fprintf(stderr, "fcntl cmd %d failed: %s\n",
96                                 cmd, strerror(errno));
97                         return rc;
98                 }
99                 break;
100         case F_DUPFD:
101                 arg = va_arg(ap, long);
102                 va_end(ap);
103                 rc = fcntl(fd, cmd, arg);
104                 if (rc == -1) {
105                         rc = -errno;
106                         fprintf(stderr, "fcntl F_DUPFD %d failed: %s\n",
107                                 (int)arg, strerror(errno));
108                         return rc;
109                 }
110                 break;
111         default:
112                 va_end(ap);
113                 fprintf(stderr, "fcntl cmd %d not supported\n", cmd);
114                 return rc;
115         }
116         return rc;
117 }
118
119 static int t_unlink(const char *path)
120 {
121         int rc;
122
123         rc = unlink(path);
124         if (rc)
125                 fprintf(stderr,
126                         "unlink(%s) error: %s\n", path, strerror(errno));
127         return rc;
128 }
129
130 /*
131  * test number 1
132  * normal flock test
133  */
134 static void t1_usage(void)
135 {
136         fprintf(stderr,
137                 "usage: flocks_test 1 {on|off} {-c|-f|-l} /path/to/file\n");
138 }
139
140 static int t1(int argc, char *argv[])
141 {
142         int fd;
143         int mount_with_flock = 0;
144         int error = 0;
145         int rc = 0;
146
147         if (argc != 5) {
148                 t1_usage();
149                 return EXIT_FAILURE;
150         }
151
152         if (!strncmp(argv[2], "on", 3)) {
153                 mount_with_flock = 1;
154         } else if (!strncmp(argv[2], "off", 4)) {
155                 mount_with_flock = 0;
156         } else {
157                 t1_usage();
158                 return EXIT_FAILURE;
159         }
160
161         fd = open(argv[4], O_RDWR);
162         if (fd < 0) {
163                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[4],
164                         strerror(errno));
165                 return EXIT_FAILURE;
166         }
167
168         if (!strncmp(argv[3], "-c", 3)) {
169                 struct flock fl;
170
171                 fl.l_type = F_RDLCK;
172                 fl.l_whence = SEEK_SET;
173                 fl.l_start = 0;
174                 fl.l_len = 1;
175
176                 error = fcntl(fd, F_SETLK, &fl);
177         } else if (!strncmp(argv[3], "-l", 3)) {
178                 error = lockf(fd, F_LOCK, 1);
179         } else if (!strncmp(argv[3], "-f", 3)) {
180                 error = flock(fd, LOCK_EX);
181         } else {
182                 t1_usage();
183                 rc = EXIT_FAILURE;
184                 goto out;
185         }
186
187         if (mount_with_flock)
188                 rc = ((error == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
189         else
190                 rc = ((error == 0) ? EXIT_FAILURE : EXIT_SUCCESS);
191
192 out:
193         if (fd >= 0)
194                 close(fd);
195         return rc;
196 }
197
198 /*
199  * test number 2
200  * 2 threads flock ops interweave
201  */
202 struct thread_info {
203         struct flock *lock;
204         int fd;
205         int rc;
206 } th_data;
207
208 static void *t2_thread1(void *arg)
209 {
210         struct thread_info *ti = arg;
211         struct flock *lock = ti->lock;
212         int fd = ti->fd;
213
214         printf("thread 1: set write lock (blocking): rc = %d\n", ti->rc);
215         lock->l_type = F_WRLCK;
216         t_fcntl(fd, F_SETLKW, lock);
217         printf("thread 1: set write lock done: rc = %d\n", ti->rc);
218         (void)t_fcntl(fd, F_GETLK, lock); /* ignore this, operation will fail */
219         printf("thread 1: unlock: rc = %d\n", ti->rc);
220         lock->l_type = F_UNLCK;
221         ti->rc += t_fcntl(fd, F_SETLK, lock);
222         printf("thread 1: unlock done: rc = %d\n", ti->rc);
223
224         if (ti->rc)
225                 fprintf(stdout, "thread1 exiting with rc = %d\n", ti->rc);
226         return &ti->rc;
227 }
228
229 static void *t2_thread2(void *arg)
230 {
231         struct thread_info *ti = arg;
232         struct flock *lock = ti->lock;
233         int fd = ti->fd;
234
235         sleep(2);
236         printf("thread 2: unlock: rc = %d\n", ti->rc);
237         lock->l_type = F_UNLCK;
238         ti->rc += t_fcntl(fd, F_SETLK, lock);
239         printf("thread 2: unlock done: rc = %d\n", ti->rc);
240         printf("thread 2: set write lock (non-blocking): rc = %d\n", ti->rc);
241         lock->l_type = F_WRLCK;
242         ti->rc += t_fcntl(fd, F_SETLK, lock);
243         printf("thread 2: set write lock done: rc = %d\n", ti->rc);
244         (void)t_fcntl(fd, F_GETLK, lock); /* ignore this, operation will fail */
245
246         if (ti->rc)
247                 fprintf(stdout, "thread2 exiting with rc = %d\n", ti->rc);
248         return &ti->rc;
249 }
250
251 static int t2(int argc, char *argv[])
252 {
253         struct flock lock = {
254                 .l_type = F_RDLCK,
255                 .l_whence = SEEK_SET,
256         };
257         char file[MAX_PATH_LENGTH] = "";
258         int  fd, rc;
259         pthread_t th1, th2;
260         struct thread_info ti;
261
262         snprintf(file, MAX_PATH_LENGTH, "%s/test_t2_file", argv[2]);
263
264         fd = open(file, O_RDWR|O_CREAT, (mode_t)0666);
265         if (fd < 0) {
266                 fprintf(stderr, "error open file '%s': %s\n", file,
267                         strerror(errno));
268                 return EXIT_FAILURE;
269         }
270
271         t_fcntl(fd, F_SETFL, O_APPEND);
272         rc = t_fcntl(fd, F_GETFL);
273         if ((rc < 0) || (rc & O_APPEND) == 0) {
274                 fprintf(stderr, "error get flag: ret %x\n", rc);
275                 rc = EXIT_FAILURE;
276                 goto out;
277         }
278
279         ti.lock = &lock;
280         ti.fd   = fd;
281         ti.rc   = 0;
282         rc = pthread_create(&th1, NULL, t2_thread1, &ti);
283         if (rc) {
284                 fprintf(stderr, "error create thread 1\n");
285                 rc = EXIT_FAILURE;
286                 goto out;
287         }
288         rc = pthread_create(&th2, NULL, t2_thread2, &ti);
289         if (rc) {
290                 fprintf(stderr, "error create thread 2\n");
291                 rc = EXIT_FAILURE;
292                 goto out;
293         }
294         pthread_join(th1, NULL);
295         pthread_join(th2, NULL);
296         if (ti.rc)
297                 rc = EXIT_FAILURE;
298 out:
299         t_unlink(file);
300         close(fd);
301
302         return rc;
303 }
304
305 /*
306  * test number 3
307  *
308  * Bug 24040: Two conflicting flocks from same process different fds should fail
309  *            two conflicting flocks from different processes but same fs
310  *            should succeed.
311  */
312 static int t3(int argc, char *argv[])
313 {
314         int fd, fd2;
315         int pid;
316         int rc = EXIT_SUCCESS;
317
318         if (argc != 3) {
319                 fprintf(stderr, "usage: flocks_test 3 filename\n");
320                 return EXIT_FAILURE;
321         }
322
323         fd = open(argv[2], O_RDWR);
324         if (fd < 0) {
325                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[2],
326                         strerror(errno));
327                 return EXIT_FAILURE;
328         }
329         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
330                 perror("first flock failed");
331                 rc = EXIT_FAILURE;
332                 goto out;
333         }
334         fd2 = open(argv[2], O_RDWR);
335         if (fd2 < 0) {
336                 fprintf(stderr, "Couldn't open file '%s': %s\n", argv[2],
337                         strerror(errno));
338                 rc = EXIT_FAILURE;
339                 goto out;
340         }
341         if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
342                 fprintf(stderr, "Second flock succeeded - FAIL\n");
343                 rc = EXIT_FAILURE;
344                 close(fd2);
345                 goto out;
346         }
347
348         close(fd2);
349
350         pid = fork();
351         if (pid == -1) {
352                 perror("fork");
353                 rc = EXIT_FAILURE;
354                 goto out;
355         }
356
357         if (pid == 0) {
358                 fd2 = open(argv[2], O_RDWR);
359                 if (fd2 < 0) {
360                         fprintf(stderr, "Couldn't open file '%s': %s\n",
361                                 argv[1], strerror(errno));
362                         rc = EXIT_FAILURE;
363                         goto out;
364                 }
365                 if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
366                         fprintf(stderr, "Second flock succeeded - FAIL\n");
367                         rc = EXIT_FAILURE;
368                         goto out_child;
369                 }
370                 if (flock(fd, LOCK_UN) == -1) {
371                         fprintf(stderr, "Child unlock on parent fd failed\n");
372                         rc = EXIT_FAILURE;
373                         goto out_child;
374                 }
375                 if (flock(fd2, LOCK_EX | LOCK_NB) == -1) {
376                         fprintf(stderr, "Relock after parent unlock failed!\n");
377                         rc = EXIT_FAILURE;
378                         goto out_child;
379                 }
380         out_child:
381                 close(fd2);
382                 exit(rc);
383         }
384
385         waitpid(pid, &rc, 0);
386 out:
387         close(fd);
388         return rc;
389 }
390
391 static int t4(int argc, char *argv[])
392 {
393         struct flock lock = {
394                 .l_type = F_WRLCK,
395                 .l_whence = SEEK_SET,
396                 .l_start = 0,
397                 .l_len = 10,
398         };
399
400         int fd, fd2;
401         pid_t child_pid;
402         int child_status;
403         int rc = EXIT_SUCCESS;
404
405         if (argc != 4) {
406                 fprintf(stderr, "usage: flocks_test 4 file1 file2\n");
407                 return EXIT_FAILURE;
408         }
409
410         fd = open(argv[2], O_RDWR);
411         if (fd < 0) {
412                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
413                 return EXIT_FAILURE;
414         }
415         fd2 = open(argv[3], O_RDWR);
416         if (fd2 < 0) {
417                 fprintf(stderr, "Couldn't open file: %s\n", argv[3]);
418                 rc = EXIT_FAILURE;
419                 goto out;
420         }
421
422         child_pid = fork();
423         if (child_pid < 0) {
424                 perror("fork");
425                 rc = EXIT_FAILURE;
426                 goto out;
427         }
428
429         if (child_pid == 0) {
430                 printf("%d: get lock1\n", getpid());
431                 fflush(stdout);
432                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
433                         fprintf(stderr, "%d: cannot get lock1: %s\n",
434                                 getpid(), strerror(errno));
435                         rc = EXIT_FAILURE;
436                         goto out_child;
437                 }
438                 printf("%d: done\n", getpid());
439                 sleep(3);
440                 printf("%d: get lock2\n", getpid());
441                 fflush(stdout);
442                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
443                         fprintf(stderr, "%d: cannot get lock2: %s\n",
444                                 getpid(), strerror(errno));
445
446                         if (errno == EDEADLK)
447                                 rc = EXIT_SUCCESS;
448                         else
449                                 rc = EXIT_FAILURE;
450
451                         goto out_child;
452                 }
453                 printf("%d: done\n", getpid());
454 out_child:
455                 printf("%d: exit rc=%d\n", getpid(), rc);
456                 exit(rc);
457         } else {
458                 printf("%d: get lock2\n", getpid());
459                 fflush(stdout);
460                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
461                         fprintf(stderr, "%d: cannot get lock2: %s\n",
462                                 getpid(), strerror(errno));
463                         rc = EXIT_FAILURE;
464                         goto out;
465                 }
466                 printf("%d: done\n", getpid());
467                 sleep(3);
468                 printf("%d: get lock1\n", getpid());
469                 fflush(stdout);
470                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
471                         fprintf(stderr, "%d: cannot get lock1: %s\n",
472                                 getpid(), strerror(errno));
473
474                         if (errno != EDEADLK) {
475                                 rc = EXIT_FAILURE;
476                                 goto out;
477                         }
478                 }
479                 printf("%d: done\n", getpid());
480         }
481
482         sleep(1);
483
484         if (close(fd) < 0) {
485                 fprintf(stderr, "%d: error closing file1: %s\n",
486                         getpid(), strerror(errno));
487                 rc = EXIT_FAILURE;
488         }
489
490         if (close(fd2) < 0) {
491                 fprintf(stderr, "%d: error closing file2: %s\n",
492                         getpid(), strerror(errno));
493                 rc = EXIT_FAILURE;
494         }
495
496         if (waitpid(child_pid, &child_status, 0) < 0) {
497                 fprintf(stderr, "%d: cannot get termination status of %d: %s\n",
498                         getpid(), child_pid, strerror(errno));
499                 rc = EXIT_FAILURE;
500         } else if (!WIFEXITED(child_status)) {
501                 fprintf(stderr, "%d: child %d terminated with status %d\n",
502                         getpid(), child_pid, child_status);
503                 rc = EXIT_FAILURE;
504         } else {
505                 rc = WEXITSTATUS(child_status);
506         }
507
508 out:
509         printf("%d: exit rc=%d\n", getpid(), rc);
510         return rc;
511 }
512
513 #define T5_USAGE                                                              \
514 "usage: flocks_test 5 {set|get|unlock} [read|write] [sleep N] file1\n"        \
515 "       set: F_SETLKW F_WRLCK\n"                                              \
516 "       get: F_GETLK F_WRLCK  (conflict)\n"                                   \
517 "       unlock: F_SETLKW F_UNLCK\n"                                           \
518 "       read|write: lock mode, write by default\n"                            \
519 "       sleep N: sleep for N secs after fcntl\n"                              \
520 "       file1: fcntl is called for this file\n"
521
522 static int t5(int argc, char *argv[])
523 {
524         struct flock lock = {
525                 .l_type = F_WRLCK,
526                 .l_whence = SEEK_SET,
527         };
528
529         int setlk = 0, getlk = 0, unlk = 0, secs = 0;
530         int pos;
531         int fd;
532         int rc = 0;
533
534         if (argc < 4 || argc > 7) {
535                 fprintf(stderr, T5_USAGE);
536                 return EXIT_FAILURE;
537         }
538
539         if (!strncmp(argv[2], "set", 4))
540                 setlk = 1;
541         else if (!strncmp(argv[2], "get", 4))
542                 getlk = 1;
543         else if (!strncmp(argv[2], "unlock", 7))
544                 unlk = 1;
545         else {
546                 fprintf(stderr, "Wrong 2nd argument: %s\n", argv[2]);
547                 return EXIT_FAILURE;
548         }
549
550         pos = 3;
551
552         if (!strncmp(argv[pos], "read", 5)) {
553                 lock.l_type = F_RDLCK;
554                 pos++;
555         } else if (!strncmp(argv[pos], "write", 6)) {
556                 lock.l_type = F_WRLCK;
557                 pos++;
558         }
559
560         if (!strncmp(argv[pos], "sleep", 6)) {
561                 secs = atoi(argv[pos + 1]);
562                 if (secs < 0 || secs > 10) {
563                         fprintf(stderr, "Sleep argument is wrong: %s\n",
564                                 argv[pos + 1]);
565                         return EXIT_FAILURE;
566                 }
567                 pos += 2;
568         }
569
570         fd = open(argv[pos], O_RDWR);
571         if (fd < 0) {
572                 fprintf(stderr, "Couldn't open file: %s\n", argv[pos]);
573                 return EXIT_FAILURE;
574         }
575
576         fprintf(stderr, "\nFLOCKS_TEST 5: %s %s flock\n",
577                 setlk ? "SET" : getlk ? "GET" : "UNLOCK",
578                 lock.l_type == F_WRLCK ? "write" : "read");
579
580         if (setlk) {
581                 rc = t_fcntl(fd, F_SETLKW, &lock);
582         } else if (getlk) {
583                 rc = t_fcntl(fd, F_GETLK, &lock);
584         } else if (unlk) {
585                 lock.l_type = F_UNLCK;
586                 rc = t_fcntl(fd, F_SETLKW, &lock);
587         }
588
589         if (secs)
590                 sleep(secs);
591
592         close(fd);
593         return rc < 0 ? -rc : 0;
594
595 }
596
597 #define T6BUF_SIZE      200
598
599 int set_lock(struct flock *lock, char *buf)
600 {
601         int i, v;
602         struct tag_node {
603                 char    tag;
604                 int     mode;
605         } tags[] = {
606                 { 'W', F_WRLCK },
607                 { 'R', F_RDLCK },
608                 { 'U', F_UNLCK },
609                 { 0, 0 }
610         };
611
612         for (i = 0; isspace(buf[i]) && i < T6BUF_SIZE;)
613                 i++;
614         for (v = 0; tags[v].tag && i < T6BUF_SIZE; v++) {
615                 if (buf[i] == tags[v].tag) {
616                         char *head;
617
618                         head = buf + i + 1;
619                         for (; buf[i] != ','; i++)
620                                 if (i >= T6BUF_SIZE)
621                                         break;
622                         buf[i] = '\0';
623                         lock->l_start = atol(head);
624                         if (lock->l_start < 0)
625                                 break;
626                         for (; !isdigit(buf[i]); i++)
627                                 if (i >= T6BUF_SIZE)
628                                         break;
629                         lock->l_len = atol(buf + i);
630                         if (lock->l_len <= 0)
631                                 break;
632                         lock->l_type = tags[v].mode;
633                         return 1;
634                 }
635         }
636         fprintf(stderr, "Invalid line: %s\n", buf);
637         return 0;
638 }
639
640 /*
641  *      Read command from stdin then enqueue a lock
642  *
643  *      [W|R|U]sss,lll
644  *      W: write R: read U: unlock
645  *      sss: start of range
646  *      lll: length of range
647  *
648  *      for example:
649  *              W1,100          # add a write lock from 1 to 100
650  *              R100,100        # add a read lock from 100 to 199
651  */
652 static int t6(int argc, char *argv[])
653 {
654         struct flock lock = {
655                 .l_whence = SEEK_SET,
656         };
657
658         int fd, rc = 0;
659         char buf[T6BUF_SIZE+1];
660         double stime;
661
662         if (argc < 3) {
663                 fprintf(stderr, "usage: flocks_test 6 file\n");
664                 return EXIT_FAILURE;
665         }
666
667         fd = open(argv[2], O_RDWR);
668         if (fd < 0) {
669                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
670                 return EXIT_FAILURE;
671         }
672
673         memset(buf, '\0', T6BUF_SIZE + 1);
674         stime = now();
675         while (fgets(buf, T6BUF_SIZE, stdin)) {
676                 if (set_lock(&lock, buf)) {
677                         rc = t_fcntl(fd, F_SETLKW, &lock);
678                         if (rc != 0) {
679                                 fprintf(stderr, "%d: cannot set lock: %s\n",
680                                         getpid(), strerror(errno));
681                                 rc = EXIT_FAILURE;
682                                 break;
683                         }
684                 }
685         }
686         close(fd);
687         printf("Time for processing %.03lfs\n", now() - stime);
688         return rc;
689 }
690
691 static void usage(void)
692 {
693         fprintf(stderr,
694                 "usage: flocks_test test# [corresponding arguments]\n");
695 }
696
697 /* program entry */
698 int main(int argc, char *argv[])
699 {
700         int rc = EXIT_SUCCESS;
701
702         if (argc < 2) {
703                 usage();
704                 exit(EXIT_FAILURE);
705         }
706
707         switch (atoi(argv[1])) {
708         case 1:
709                 rc = t1(argc, argv);
710                 break;
711         case 2:
712                 rc = t2(argc, argv);
713                 break;
714         case 3:
715                 rc = t3(argc, argv);
716                 break;
717         case 4:
718                 rc = t4(argc, argv);
719                 break;
720         case 5:
721                 rc = t5(argc, argv);
722                 break;
723         case 6:
724                 rc = t6(argc, argv);
725                 break;
726         default:
727                 fprintf(stderr, "unknown test number '%s'\n", argv[1]);
728                 break;
729         }
730
731         if (rc)
732                 fprintf(stderr, "exiting with rc = %d\n", rc);
733         return rc;
734 }