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