1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2017 Red Hat Inc. All Rights Reserved.
4 * Author: Xiong Zhou <xzhou@redhat.com>
5 *
6 * This is testing OFD locks racing with POSIX locks:
7 *
8 * OFD read lock vs OFD write lock
9 * OFD read lock vs POSIX write lock
10 * OFD write lock vs POSIX write lock
11 * OFD write lock vs POSIX read lock
12 * OFD write lock vs OFD write lock
13 *
14 * OFD r/w locks vs POSIX write locks
15 * OFD r/w locks vs POSIX read locks
16 *
17 * For example:
18 *
19 * Init an file with preset values.
20 *
21 * Threads acquire OFD READ locks to read a 4k section start from 0;
22 * checking data read back, there should not be any surprise
23 * values and data should be consistent in a 1k block.
24 *
25 * Threads acquire OFD WRITE locks to write a 4k section start from 1k,
26 * writing different values in different threads.
27 *
28 * Check file data after racing, there should not be any surprise values
29 * and data should be consistent in a 1k block.
30 */
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <fcntl.h>
38 #include <pthread.h>
39 #include <sched.h>
40 #include <errno.h>
41
42 #include "lapi/fcntl.h"
43 #include "tst_safe_pthread.h"
44 #include "tst_test.h"
45 #include "fcntl_common.h"
46
47 static int thread_cnt;
48 static int fail_flag = 0;
49 static volatile int loop_flag = 1;
50 static const int max_thread_cnt = 32;
51 static const char fname[] = "tst_ofd_posix_locks";
52 static const long write_size = 4096;
53 static pthread_barrier_t barrier;
54
55 struct param {
56 long offset;
57 long length;
58 long cnt;
59 };
60
setup(void)61 static void setup(void)
62 {
63 thread_cnt = tst_ncpus_conf() * 3;
64 if (thread_cnt > max_thread_cnt)
65 thread_cnt = max_thread_cnt;
66 }
67
68 /* OFD write lock writing data*/
fn_ofd_w(void * arg)69 static void *fn_ofd_w(void *arg)
70 {
71 struct param *pa = arg;
72 unsigned char buf[pa->length];
73 int fd = SAFE_OPEN(fname, O_RDWR);
74 long wt = pa->cnt;
75
76 struct flock64 lck = {
77 .l_whence = SEEK_SET,
78 .l_start = pa->offset,
79 .l_len = pa->length,
80 .l_pid = 0,
81 };
82
83 do {
84
85 memset(buf, wt, pa->length);
86
87 lck.l_type = F_WRLCK;
88 my_fcntl(fd, F_OFD_SETLKW, &lck);
89
90 SAFE_LSEEK(fd, pa->offset, SEEK_SET);
91 SAFE_WRITE(1, fd, buf, pa->length);
92
93 lck.l_type = F_UNLCK;
94 my_fcntl(fd, F_OFD_SETLKW, &lck);
95
96 wt++;
97 if (wt >= 255)
98 wt = pa->cnt;
99
100 sched_yield();
101 } while (loop_flag);
102
103 pthread_barrier_wait(&barrier);
104 SAFE_CLOSE(fd);
105 return NULL;
106 }
107
108 /* POSIX write lock writing data*/
fn_posix_w(void * arg)109 static void *fn_posix_w(void *arg)
110 {
111 struct param *pa = arg;
112 unsigned char buf[pa->length];
113 int fd = SAFE_OPEN(fname, O_RDWR);
114 long wt = pa->cnt;
115
116 struct flock lck = {
117 .l_whence = SEEK_SET,
118 .l_start = pa->offset,
119 .l_len = pa->length,
120 };
121
122 do {
123
124 memset(buf, wt, pa->length);
125
126 lck.l_type = F_WRLCK;
127 SAFE_FCNTL(fd, F_SETLKW, &lck);
128
129 SAFE_LSEEK(fd, pa->offset, SEEK_SET);
130 SAFE_WRITE(1, fd, buf, pa->length);
131
132 lck.l_type = F_UNLCK;
133 SAFE_FCNTL(fd, F_SETLKW, &lck);
134
135 wt++;
136 if (wt >= 255)
137 wt = pa->cnt;
138
139 sched_yield();
140 } while (loop_flag);
141
142 pthread_barrier_wait(&barrier);
143 SAFE_CLOSE(fd);
144 return NULL;
145 }
146
147 /* OFD read lock reading data*/
fn_ofd_r(void * arg)148 static void *fn_ofd_r(void *arg)
149 {
150 struct param *pa = arg;
151 unsigned char buf[pa->length];
152 int i;
153 int fd = SAFE_OPEN(fname, O_RDWR);
154
155 struct flock64 lck = {
156 .l_whence = SEEK_SET,
157 .l_start = pa->offset,
158 .l_len = pa->length,
159 .l_pid = 0,
160 };
161
162 while (loop_flag) {
163
164 memset(buf, 0, pa->length);
165
166 lck.l_type = F_RDLCK;
167 my_fcntl(fd, F_OFD_SETLKW, &lck);
168
169 /* rlock acquired */
170 SAFE_LSEEK(fd, pa->offset, SEEK_SET);
171 SAFE_READ(1, fd, buf, pa->length);
172
173 /* Verifying data read */
174 for (i = 0; i < pa->length; i++) {
175
176 if (buf[i] < 1 || buf[i] > 254) {
177
178 tst_res(TFAIL, "Unexpected data "
179 "offset %ld value %d",
180 pa->offset + i, buf[i]);
181 fail_flag = 1;
182 break;
183 }
184
185 int j = (i / (pa->length/4)) * pa->length/4;
186
187 if (buf[i] != buf[j]) {
188
189 tst_res(TFAIL, "Unexpected data "
190 "offset %ld value %d",
191 pa->offset + i, buf[i]);
192 fail_flag = 1;
193 break;
194 }
195 }
196
197 lck.l_type = F_UNLCK;
198 my_fcntl(fd, F_OFD_SETLK, &lck);
199
200 sched_yield();
201 }
202
203 pthread_barrier_wait(&barrier);
204 SAFE_CLOSE(fd);
205 return NULL;
206 }
207
208 /* POSIX read lock reading data */
fn_posix_r(void * arg)209 static void *fn_posix_r(void *arg)
210 {
211 struct param *pa = arg;
212 unsigned char buf[pa->length];
213 int i;
214 int fd = SAFE_OPEN(fname, O_RDWR);
215
216 struct flock lck = {
217 .l_whence = SEEK_SET,
218 .l_start = pa->offset,
219 .l_len = pa->length,
220 };
221
222 while (loop_flag) {
223
224 memset(buf, 0, pa->length);
225
226 lck.l_type = F_RDLCK;
227 SAFE_FCNTL(fd, F_SETLKW, &lck);
228
229 /* rlock acquired */
230 SAFE_LSEEK(fd, pa->offset, SEEK_SET);
231 SAFE_READ(1, fd, buf, pa->length);
232
233 /* Verifying data read */
234 for (i = 0; i < pa->length; i++) {
235
236 if (buf[i] < 1 || buf[i] > 254) {
237
238 tst_res(TFAIL, "Unexpected data "
239 "offset %ld value %d",
240 pa->offset + i, buf[i]);
241 fail_flag = 1;
242 break;
243 }
244
245 int j = (i / (pa->length/4)) * pa->length/4;
246
247 if (buf[i] != buf[j]) {
248
249 tst_res(TFAIL, "Unexpected data "
250 "offset %ld value %d",
251 pa->offset + i, buf[i]);
252 fail_flag = 1;
253 break;
254 }
255 }
256
257 lck.l_type = F_UNLCK;
258 SAFE_FCNTL(fd, F_SETLK, &lck);
259
260 sched_yield();
261 }
262
263 pthread_barrier_wait(&barrier);
264 SAFE_CLOSE(fd);
265 return NULL;
266 }
267
fn_dummy(void * arg)268 static void *fn_dummy(void *arg)
269 {
270 arg = NULL;
271
272 pthread_barrier_wait(&barrier);
273 return arg;
274 }
275
276 /* Test different functions and verify data */
test_fn(void * f0 (void *),void * f1 (void *),void * f2 (void *),const char * msg)277 static void test_fn(void *f0(void *), void *f1(void *),
278 void *f2(void *), const char *msg)
279 {
280 int i, k, fd;
281 pthread_t id0[thread_cnt];
282 pthread_t id1[thread_cnt];
283 pthread_t id2[thread_cnt];
284 struct param p0[thread_cnt];
285 struct param p1[thread_cnt];
286 struct param p2[thread_cnt];
287 unsigned char buf[write_size];
288
289 tst_res(TINFO, "%s", msg);
290
291 if (tst_fill_file(fname, 1, write_size, thread_cnt + 1))
292 tst_brk(TBROK, "Failed to create tst file");
293
294 if (pthread_barrier_init(&barrier, NULL, thread_cnt*3) != 0)
295 tst_brk(TBROK, "Failed to init pthread barrier");
296
297 for (i = 0; i < thread_cnt; i++) {
298
299 p0[i].offset = i * write_size;
300 p0[i].length = write_size;
301 p0[i].cnt = i + 2;
302
303 p1[i].offset = i * write_size + write_size / 4;
304 p1[i].length = write_size;
305 p1[i].cnt = i + 2;
306
307 p2[i].offset = i * write_size + write_size / 2;
308 p2[i].length = write_size;
309 p2[i].cnt = i + 2;
310 }
311
312 fail_flag = 0;
313 loop_flag = 1;
314
315 for (i = 0; i < thread_cnt; i++) {
316
317 SAFE_PTHREAD_CREATE(id0 + i, NULL, f0, (void *)&p0[i]);
318 SAFE_PTHREAD_CREATE(id1 + i, NULL, f1, (void *)&p1[i]);
319 SAFE_PTHREAD_CREATE(id2 + i, NULL, f2, (void *)&p2[i]);
320 }
321
322 sleep(1);
323 loop_flag = 0;
324
325 for (i = 0; i < thread_cnt; i++) {
326
327 SAFE_PTHREAD_JOIN(id0[i], NULL);
328 SAFE_PTHREAD_JOIN(id1[i], NULL);
329 SAFE_PTHREAD_JOIN(id2[i], NULL);
330 }
331
332 fd = SAFE_OPEN(fname, O_RDONLY);
333
334 for (i = 0; i < thread_cnt * 4; i++) {
335
336 SAFE_READ(1, fd, buf, write_size/4);
337
338 for (k = 0; k < write_size/4; k++) {
339
340 if (buf[k] < 2 || buf[k] > 254) {
341
342 if (i < 3 && buf[k] == 1)
343 continue;
344 tst_res(TFAIL, "Unexpected data "
345 "offset %ld value %d",
346 i * write_size / 4 + k, buf[k]);
347 SAFE_CLOSE(fd);
348 return;
349 }
350 }
351
352 for (k = 1; k < write_size/4; k++) {
353
354 if (buf[k] != buf[0]) {
355 tst_res(TFAIL, "Unexpected block read");
356 SAFE_CLOSE(fd);
357 return;
358 }
359 }
360 }
361
362 if (pthread_barrier_destroy(&barrier) != 0)
363 tst_brk(TBROK, "Failed to destroy pthread barrier");
364
365 SAFE_CLOSE(fd);
366 if (fail_flag == 0)
367 tst_res(TPASS, "Access between threads synchronized");
368 }
369
370 static struct tcase {
371 void *(*fn0)(void *);
372 void *(*fn1)(void *);
373 void *(*fn2)(void *);
374 const char *desc;
375 } tcases[] = {
376 {fn_ofd_r, fn_ofd_w, fn_dummy, "OFD read lock vs OFD write lock"},
377 {fn_ofd_w, fn_posix_w, fn_dummy, "OFD write lock vs POSIX write lock"},
378 {fn_ofd_r, fn_posix_w, fn_dummy, "OFD read lock vs POSIX write lock"},
379 {fn_ofd_w, fn_posix_r, fn_dummy, "OFD write lock vs POSIX read lock"},
380 {fn_ofd_w, fn_ofd_w, fn_dummy, "OFD write lock vs OFD write lock"},
381 {fn_ofd_r, fn_ofd_w, fn_posix_w, "OFD r/w lock vs POSIX write lock"},
382 {fn_ofd_r, fn_ofd_w, fn_posix_r, "OFD r/w lock vs POSIX read lock"},
383 };
384
tests(unsigned int i)385 static void tests(unsigned int i)
386 {
387 test_fn(tcases[i].fn0, tcases[i].fn1, tcases[i].fn2, tcases[i].desc);
388 }
389
390 static struct tst_test test = {
391 .min_kver = "3.15",
392 .needs_tmpdir = 1,
393 .test = tests,
394 .tcnt = ARRAY_SIZE(tcases),
395 .setup = setup
396 };
397