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