Whamcloud - gitweb
LU-6142 tests: Fix style issues under lustre/tests
[fs/lustre-release.git] / lustre / tests / flock_deadlock.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 http://www.gnu.org/licenses
18  *
19  * Please  visit http://www.xyratex.com/contact if you need additional
20  * information or have any questions.
21  *
22  * GPL HEADER END
23  */
24
25 /*
26  * Copyright 2012 Xyratex Technology Limited
27  */
28
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/file.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <sys/sem.h>
41 #include <semaphore.h>
42 #include <sys/mman.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45
46 #define LOCK_LEN 100
47
48 struct flock flocks[4] = {
49         /* 1st region */
50         {
51                 .l_type         = F_WRLCK,
52                 .l_whence       = SEEK_SET,
53                 .l_start        = 0,
54                 .l_len          = LOCK_LEN,
55                 .l_pid          = 0,
56         },
57         /* 2nd region */
58         {
59                 .l_type         = F_WRLCK,
60                 .l_whence       = SEEK_SET,
61                 .l_start        = LOCK_LEN,
62                 .l_len          = LOCK_LEN,
63                 .l_pid          = 0,
64         },
65         /* 3rd region */
66         {
67                 .l_type         = F_WRLCK,
68                 .l_whence       = SEEK_SET,
69                 .l_start        = 2 * LOCK_LEN,
70                 .l_len          = LOCK_LEN,
71                 .l_pid          = 0,
72         },
73         /* 2nd & 3rd regions */
74         {
75                 .l_type         = F_WRLCK,
76                 .l_whence       = SEEK_SET,
77                 .l_start        = LOCK_LEN,
78                 .l_len          = 2 * LOCK_LEN,
79                 .l_pid          = 0,
80         },
81 };
82
83 enum {
84         FLOCK_GET       = 0,
85         FLOCK_PUT       = 1,
86 };
87
88 #define flock_call(fd, num, get, label)                                       \
89         flocks[num].l_type = get == FLOCK_GET ? F_WRLCK : F_UNLCK;            \
90         printf("%d: %s lock%d [%llu, %llu]\n", pid,                           \
91                 get == FLOCK_GET ? "taking" : "putting",                      \
92                 num, (unsigned long long)flocks[num].l_start,                 \
93                 (unsigned long long)flocks[num].l_start + flocks[num].l_len); \
94         rc = fcntl(fd, F_SETLKW, &flocks[num]);                               \
95         if (rc < 0) {                                                         \
96                 rc = errno;                                                   \
97                 fprintf(stderr, "%d: failed to %s lock%d, %s\n",              \
98                         pid, get == FLOCK_GET ? "take" : "put",               \
99                         num, strerror(errno));                                \
100                 goto label;                                                   \
101         } else {                                                              \
102                 printf("%d: done\n", pid);                                    \
103         }
104
105 static void catch_alarm(int i)
106 {
107         fprintf(stderr, "lock timeout\n");
108         exit(124);
109 }
110
111 int main(int argc, char *argv[])
112 {
113         struct sigaction act;
114         int status;
115         pid_t wpid = 0;
116         int fd, i, pid, num = 0, rc = 0;
117
118         if (argc != 2) {
119                 fprintf(stderr, "usage: %s <file>\n", argv[0]);
120                 return EXIT_FAILURE;
121         }
122         fd = open(argv[1], O_RDWR|O_CREAT, (mode_t)0666);
123         if (fd < 0) {
124                 fprintf(stderr, "error open file %s\n", argv[1]);
125                 return EXIT_FAILURE;
126         }
127
128         for (i = 0; i < 2; i++) {
129                 fflush(stdout);
130                 pid = fork();
131                 if (pid && i == 0)
132                         wpid = pid;
133                 if (pid == 0)
134                         wpid = 0;
135                 if (pid == 0 && i == 0) {
136                         pid = getpid();
137
138                         flock_call(fd, num, FLOCK_GET, err_lock0);
139
140                         printf("%d sleeping 1\n", pid);
141                         sleep(1);
142
143                         /* First of all, it should get blocked on flocks[1]
144                          * 2nd child. Later, should deadlock with flocks[2]
145                          * parent, after cancelling flocks[1] 2nd child.
146                          */
147                         printf("%d: taking lock3 [%llu, %llu]\n", pid,
148                                 (unsigned long long)flocks[3].l_start,
149                                 (unsigned long long)flocks[3].l_start +
150                                 flocks[3].l_len);
151                         memset(&act, 0, sizeof(act));
152                         act.sa_handler = catch_alarm;
153                         sigemptyset(&act.sa_mask);
154                         sigaddset(&act.sa_mask, SIGALRM);
155                         if (sigaction(SIGALRM, &act, NULL) < 0) {
156                                 fprintf(stderr,
157                                         "SIGALRM signal setup failed, errno: %d",
158                                         errno);
159                                 rc = 3;
160                                 goto err_lock1;
161                         }
162                         alarm(5);
163                         rc = fcntl(fd, F_SETLKW, &flocks[3]);
164                         if (rc >= 0) {
165                                 fprintf(stderr,
166                                         "%d: should not succeed to take lock3\n",
167                                         pid);
168
169                                 flock_call(fd, 3, FLOCK_PUT, err_lock1);
170                                 rc = EINVAL;
171                                 goto err_lock1;
172                         }
173                         if (errno != EDEADLK) {
174                                 rc = errno;
175                                 fprintf(stderr,
176                                         "%d: failed to take lock3: %s\n", pid,
177                                         strerror(errno));
178                                 goto err_lock1;
179                         }
180
181                         printf("%d: expected deadlock\n", pid);
182
183                         flock_call(fd, num, FLOCK_PUT, err_lock0);
184                         break;
185                 } else if (pid == 0 && i == 1) {
186                         pid = getpid();
187
188                         flock_call(fd, 1, FLOCK_GET, err_lock0);
189
190                         /* Let flocks[2] 2nd child get granted and
191                          * flocks[3] 1st child, flocks[0] parent get blocked.
192                          */
193                         printf("%d sleeping 2\n", pid);
194                         sleep(2);
195
196                         flock_call(fd, 1, FLOCK_PUT, err_lock0);
197                         break;
198                 } else if (pid && i == 1) {
199                         pid = getpid();
200                         num = 2;
201
202                         /* Let flocks[1] 2nd child get granted first */
203                         printf("%d: sleeping 1\n", pid);
204                         sleep(1);
205
206                         flock_call(fd, num, FLOCK_GET, err_lock0);
207
208                         /* Should get blocked on flocks[0], 1st child
209                          * and succeed later.
210                          */
211                         flock_call(fd, 0, FLOCK_GET, err_lock1);
212
213                         flock_call(fd, 0, FLOCK_PUT, err_lock1);
214                         flock_call(fd, num, FLOCK_PUT, err_lock0);
215                         break;
216                 }
217         }
218
219         if (pid == 0)
220                 sleep(2);
221         if (wpid) {
222                 waitpid(wpid, &status, 0);
223                 rc = WEXITSTATUS(status);
224         }
225         printf("%d Exit\n", pid);
226         close(fd);
227         return rc;
228
229 err_lock1:
230         flocks[num].l_type = F_UNLCK;
231         fcntl(fd, F_SETLKW, &flocks[num]);
232 err_lock0:
233         close(fd);
234         return rc;
235 }