Whamcloud - gitweb
LU-2177 ldlm: flock completion fixes.
[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.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2011, Intel Corporation.
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 <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <pthread.h>
44 #include <sys/file.h>
45 #include <sys/wait.h>
46 #include <stdarg.h>
47
48 #define MAX_PATH_LENGTH 4096
49 /**
50  * helper functions
51  */
52 int t_fcntl(int fd, int cmd, ...)
53 {
54         va_list ap;
55         long arg;
56         struct flock *lock;
57         int rc = -1;
58
59         va_start(ap, cmd);
60         switch (cmd) {
61         case F_GETFL:
62                 va_end(ap);
63                 rc = fcntl(fd, cmd);
64                 if (rc == -1) {
65                         rc = -errno;
66                         fprintf(stderr, "fcntl GETFL failed: %s\n",
67                                 strerror(errno));
68                         return rc;
69                 }
70                 break;
71         case F_SETFL:
72                 arg = va_arg(ap, long);
73                 va_end(ap);
74                 rc = fcntl(fd, cmd, arg);
75                 if (rc == -1) {
76                         rc = -errno;
77                         fprintf(stderr, "fcntl SETFL %ld failed: %s\n",
78                                 arg, strerror(errno));
79                         return rc ;
80                 }
81                 break;
82         case F_GETLK:
83         case F_SETLK:
84         case F_SETLKW:
85                 lock = va_arg(ap, struct flock *);
86                 va_end(ap);
87                 rc = fcntl(fd, cmd, lock);
88                 if (rc == -1) {
89                         rc = -errno;
90                         fprintf(stderr, "fcntl cmd %d failed: %s\n",
91                                 cmd, strerror(errno));
92                         return rc ;
93                 }
94                 break;
95         case F_DUPFD:
96                 arg = va_arg(ap, long);
97                 va_end(ap);
98                 rc = fcntl(fd, cmd, arg);
99                 if (rc == -1) {
100                         rc = -errno;
101                         fprintf(stderr, "fcntl F_DUPFD %d failed: %s\n",
102                                 (int)arg, strerror(errno));
103                         return rc;
104                 }
105                 break;
106         default:
107                 va_end(ap);
108                 fprintf(stderr, "fcntl cmd %d not supported\n", cmd);
109                 return rc;
110         }
111         return rc;
112 }
113
114 int t_unlink(const char *path)
115 {
116         int rc;
117
118         rc = unlink(path);
119         if (rc)
120                 fprintf(stderr, "unlink(%s) error: %s\n", path, strerror(errno));
121         return rc;
122 }
123
124 /** =================================================================
125  * test number 1
126  * 
127  * normal flock test
128  */
129 void t1_usage(void)
130 {
131         fprintf(stderr, "usage: ./flocks_test 1 on|off -c|-f|-l /path/to/file\n");
132 }
133
134 int t1(int argc, char *argv[])
135 {
136         int fd;
137         int mount_with_flock = 0;
138         int error = 0;
139         int rc = 0;
140
141         if (argc != 5) {
142                 t1_usage();
143                 return EXIT_FAILURE;
144         }
145
146         if (!strncmp(argv[2], "on", 3)) {
147                 mount_with_flock = 1;
148         } else if (!strncmp(argv[2], "off", 4)) {
149                 mount_with_flock = 0;
150         } else {
151                 t1_usage();
152                 return EXIT_FAILURE;
153         }
154
155         if ((fd = open(argv[4], O_RDWR)) < 0) {
156                 fprintf(stderr, "Couldn't open file: %s\n", argv[4]);
157                 return EXIT_FAILURE;
158         }
159
160         if (!strncmp(argv[3], "-c", 3)) {
161                 struct flock fl;
162
163                 fl.l_type = F_RDLCK;
164                 fl.l_whence = SEEK_SET;
165                 fl.l_start = 0;
166                 fl.l_len = 1;
167
168                 error = fcntl(fd, F_SETLK, &fl);
169         } else if (!strncmp(argv[3], "-l", 3)) {
170                 error = lockf(fd, F_LOCK, 1);
171         } else if (!strncmp(argv[3], "-f", 3)) {
172                 error = flock(fd, LOCK_EX);
173         } else {
174                 t1_usage();
175                 rc = EXIT_FAILURE;
176                 goto out;
177         }
178
179         if (mount_with_flock)
180                 rc = ((error == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
181         else
182                 rc = ((error == 0) ? EXIT_FAILURE : EXIT_SUCCESS);
183
184 out:
185         if (fd >= 0)
186                 close(fd);
187         return rc;
188 }
189
190 /** ===============================================================
191  * test number 2
192  * 
193  * 2 threads flock ops interweave
194  */
195 typedef struct {
196         struct flock* lock;
197         int fd;
198 } th_data;
199
200 void* t2_thread1(void *arg)
201 {
202         struct flock *lock = ((th_data *)arg)->lock;
203         int fd             = ((th_data *)arg)->fd;
204
205         printf("thread 1: set write lock (blocking)\n");
206         lock->l_type = F_WRLCK;
207         t_fcntl(fd, F_SETLKW, lock);
208         printf("thread 1: set write lock done\n");
209         t_fcntl(fd, F_GETLK, lock);
210         printf("thread 1: unlock\n");
211         lock->l_type = F_UNLCK;
212         t_fcntl(fd, F_SETLK, lock);
213         printf("thread 1: unlock done\n");
214         return 0;
215 }
216
217 void* t2_thread2(void *arg)
218 {
219         struct flock *lock = ((th_data *)arg)->lock;
220         int fd             = ((th_data *)arg)->fd;
221
222         sleep(2);
223         printf("thread 2: unlock\n");
224         lock->l_type = F_UNLCK;
225         t_fcntl(fd, F_SETLK, lock);
226         printf("thread 2: unlock done\n");
227         printf("thread 2: set write lock (non-blocking)\n");
228         lock->l_type = F_WRLCK;
229         t_fcntl(fd, F_SETLK, lock);
230         printf("thread 2: set write lock done\n");
231         t_fcntl(fd, F_GETLK, lock);
232         return 0;
233 }
234
235 int t2(int argc, char* argv[])
236 {
237         struct flock lock = {
238                 .l_type = F_RDLCK,
239                 .l_whence = SEEK_SET,
240         };
241         char file[MAX_PATH_LENGTH] = "";
242         int  fd, rc;
243         pthread_t th1, th2;
244         th_data   ta;
245
246         snprintf(file, MAX_PATH_LENGTH, "%s/test_t2_file", argv[2]);
247
248         fd = open(file, O_RDWR|O_CREAT, (mode_t)0666);
249         if (fd < 0) {
250                 fprintf(stderr, "error open file: %s\n", file);
251                 return EXIT_FAILURE;
252         }
253
254         t_fcntl(fd, F_SETFL, O_APPEND);
255         rc = t_fcntl(fd, F_GETFL);
256         if ((rc < 0) || (rc & O_APPEND) == 0) {
257                 fprintf(stderr, "error get flag: ret %x\n", rc);
258                 rc = EXIT_FAILURE;
259                 goto out;
260         }
261
262         ta.lock = &lock;
263         ta.fd   = fd;
264         rc = pthread_create(&th1, NULL, t2_thread1, &ta);
265         if (rc) {
266                 fprintf(stderr, "error create thread 1\n");
267                 rc = EXIT_FAILURE;
268                 goto out;
269         }
270         rc = pthread_create(&th2, NULL, t2_thread2, &ta);
271         if (rc) {
272                 fprintf(stderr, "error create thread 2\n");
273                 rc = EXIT_FAILURE;
274                 goto out;
275         }
276         (void)pthread_join(th1, NULL);
277         (void)pthread_join(th2, NULL);
278 out:
279         t_unlink(file);
280         close(fd);
281         return rc;
282 }
283
284 /** =================================================================
285  * test number 3
286  *
287  * Bug 24040: Two conflicting flocks from same process different fds should fail
288  *            two conflicting flocks from different processes but same fs
289  *            should succeed.
290  */
291 int t3(int argc, char *argv[])
292 {
293         int fd, fd2;
294         int pid;
295         int rc = EXIT_SUCCESS;
296
297         if (argc != 3) {
298                 fprintf(stderr, "Usage: ./flocks_test 3 filename\n");
299                 return EXIT_FAILURE;
300         }
301
302         if ((fd = open(argv[2], O_RDWR)) < 0) {
303                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
304                 return EXIT_FAILURE;
305         }
306         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
307                 perror("first flock failed");
308                 rc = EXIT_FAILURE;
309                 goto out;
310         }
311         if ((fd2 = open(argv[2], O_RDWR)) < 0) {
312                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
313                 rc = EXIT_FAILURE;
314                 goto out;
315         }
316         if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
317                 fprintf(stderr, "Second flock succeeded - FAIL\n");
318                 rc = EXIT_FAILURE;
319                 close(fd2);
320                 goto out;
321         }
322
323         close(fd2);
324
325         pid = fork();
326         if (pid == -1) {
327                 perror("fork");
328                 rc = EXIT_FAILURE;
329                 goto out;
330         }
331
332         if (pid == 0) {
333                 if ((fd2 = open(argv[2], O_RDWR)) < 0) {
334                         fprintf(stderr, "Couldn't open file: %s\n", argv[1]);
335                         rc = EXIT_FAILURE;
336                         exit(rc);
337                 }
338                 if (flock(fd2, LOCK_EX | LOCK_NB) >= 0) {
339                         fprintf(stderr, "Second flock succeeded - FAIL\n");
340                         rc = EXIT_FAILURE;
341                         goto out_child;
342                 }
343                 if (flock(fd, LOCK_UN) == -1) {
344                         fprintf(stderr, "Child unlock on parent fd failed\n");
345                         rc = EXIT_FAILURE;
346                         goto out_child;
347                 }
348                 if (flock(fd2, LOCK_EX | LOCK_NB) == -1) {
349                         fprintf(stderr, "Relock after parent unlock failed!\n");
350                         rc = EXIT_FAILURE;
351                         goto out_child;
352                 }
353         out_child:
354                 close(fd2);
355                 exit(rc);
356         }
357
358         waitpid(pid, &rc, 0);
359 out:
360         close(fd);
361         return rc;
362 }
363
364 int t4(int argc, char *argv[])
365 {
366         struct flock lock = {
367                 .l_type = F_WRLCK,
368                 .l_whence = SEEK_SET,
369                 .l_start = 0,
370                 .l_len = 10,
371         };
372
373         int fd, fd2;
374         int pid;
375         int rc = EXIT_SUCCESS;
376
377         if (argc != 4) {
378                 fprintf(stderr, "Usage: ./flocks_test 4 file1 file2\n");
379                 return EXIT_FAILURE;
380         }
381
382         if ((fd = open(argv[2], O_RDWR)) < 0) {
383                 fprintf(stderr, "Couldn't open file: %s\n", argv[2]);
384                 return EXIT_FAILURE;
385         }
386         if ((fd2 = open(argv[3], O_RDWR)) < 0) {
387                 fprintf(stderr, "Couldn't open file: %s\n", argv[3]);
388                 rc = EXIT_FAILURE;
389                 goto out;
390         }
391
392         pid = fork();
393         if (pid == -1) {
394                 perror("fork");
395                 rc = EXIT_FAILURE;
396                 goto out;
397         }
398
399         if (pid == 0) {
400                 printf("%d: get lock1\n", getpid());
401                 fflush(stdout);
402                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
403                         perror("first flock failed");
404                         rc = EXIT_FAILURE;
405                         goto out_child;
406                 }
407                 printf("%d: done\n", getpid());
408                 sleep(3);
409                 printf("%d: get lock2\n", getpid());
410                 fflush(stdout);
411                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
412                         perror("first flock failed");
413                         rc = EXIT_FAILURE;
414                         goto out_child;
415                 }
416                 printf("%d: done\n", getpid());
417 out_child:
418                 printf("%d: exit rc=%d\n", getpid(), rc);
419                 exit(rc);
420         } else {
421                 printf("%d: get lock2\n", getpid());
422                 fflush(stdout);
423                 if (t_fcntl(fd2, F_SETLKW, &lock) < 0) {
424                         perror("first flock failed");
425                         rc = EXIT_FAILURE;
426                         goto out;
427                 }
428                 printf("%d: done\n", getpid());
429                 sleep(3);
430                 printf("%d: get lock1\n", getpid());
431                 fflush(stdout);
432                 if (t_fcntl(fd, F_SETLKW, &lock) < 0) {
433                         perror("first flock failed");
434                         rc = EXIT_FAILURE;
435                         goto out;
436                 }
437                 printf("%d: done\n", getpid());
438         }
439
440 out:
441         printf("%d: exit rc=%d\n", getpid(), rc);
442         return rc;
443 }
444
445 #define T5_USAGE                                                              \
446         "Usage: ./flocks_test 5 set|get|unlock [read|write] [sleep N] file1\n"\
447 "       set: F_SETLKW F_WRLCK\n"                                              \
448 "       get: F_GETLK F_WRLCK  (conflict)\n"                                   \
449 "       unlock: F_SETLKW F_UNLCK\n"                                           \
450 "       read|write: lock mode, write by default\n"                            \
451 "       sleep N: sleep for N secs after fcntl\n"                              \
452 "       file1: fcntl is called for this file\n"
453
454 int t5(int argc, char *argv[])
455 {
456         struct flock lock = {
457                 .l_type = F_WRLCK,
458                 .l_whence = SEEK_SET,
459         };
460
461         int setlk = 0, getlk = 0, unlk = 0, secs = 0;
462         int pos;
463         int fd;
464         int rc = 0;
465
466         if (argc < 4 || argc > 7) {
467                 fprintf(stderr, T5_USAGE);
468                 return EXIT_FAILURE;
469         }
470
471         if (!strncmp(argv[2], "set", 4))
472                 setlk = 1;
473         else if (!strncmp(argv[2], "get", 4))
474                 getlk = 1;
475         else if (!strncmp(argv[2], "unlock", 7))
476                 unlk = 1;
477         else {
478                 fprintf(stderr, "Wrong 2nd argument: %s\n", argv[2]);
479                 return EXIT_FAILURE;
480         }
481
482         pos = 3;
483
484         if (!strncmp(argv[pos], "read", 5)) {
485                 lock.l_type = F_RDLCK;
486                 pos++;
487         } else if (!strncmp(argv[pos], "write", 6)) {
488                 lock.l_type = F_WRLCK;
489                 pos++;
490         }
491
492         if (!strncmp(argv[pos], "sleep", 6)) {
493                 secs = atoi(argv[pos + 1]);
494                 if (secs < 0 || secs > 10) {
495                         fprintf(stderr, "Sleep argument is wrong: %s\n",
496                                 argv[pos + 1]);
497                         return EXIT_FAILURE;
498                 }
499                 pos += 2;
500         }
501
502         fd = open(argv[pos], O_RDWR);
503         if (fd < 0) {
504                 fprintf(stderr, "Couldn't open file: %s\n", argv[pos]);
505                 return EXIT_FAILURE;
506         }
507
508         fprintf(stderr, "\nFLOCKS_TEST 5: %s %s flock\n",
509                 setlk ? "SET" : getlk ? "GET" : "UNLOCK",
510                 lock.l_type == F_WRLCK ? "write" : "read");
511
512         if (setlk) {
513                 rc = t_fcntl(fd, F_SETLKW, &lock);
514         } else if (getlk) {
515                 rc = t_fcntl(fd, F_GETLK, &lock);
516         } else if (unlk) {
517                 lock.l_type = F_UNLCK;
518                 rc = t_fcntl(fd, F_SETLKW, &lock);
519         }
520
521         if (secs)
522                 sleep(secs);
523
524         close(fd);
525         return rc < 0 ? -rc : 0;
526
527 }
528
529 /** ==============================================================
530  * program entry
531  */
532 void usage(void)
533 {
534         fprintf(stderr, "usage: ./flocks_test test# [corresponding arguments]\n");
535 }
536
537 int main(int argc, char* argv[])
538 {
539         int test_no;
540         int rc = EXIT_SUCCESS;
541
542         if (argc < 1) {
543                 usage();
544                 exit(EXIT_FAILURE);
545         }
546         test_no = atoi(argv[1]);
547
548         switch(test_no) {
549         case 1:
550                 rc = t1(argc, argv);
551                 break;
552         case 2:
553                 rc = t2(argc, argv);
554                 break;
555         case 3:
556                 rc = t3(argc, argv);
557                 break;
558         case 4:
559                 rc = t4(argc, argv);
560                 break;
561         case 5:
562                 rc = t5(argc, argv);
563                 break;
564         default:
565                 fprintf(stderr, "unknow test number %s\n", argv[1]);
566                 break;
567         }
568         return rc;
569 }