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