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
46static int thread_cnt;
47static int fail_flag = 0;
48static volatile int loop_flag = 1;
49static const int max_thread_cnt = 32;
50static const char fname[] = "tst_ofd_posix_locks";
51static const long write_size = 4096;
52static pthread_barrier_t barrier;
53
54struct param {
55	long offset;
56	long length;
57	long cnt;
58};
59
60static 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*/
68static 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 flock 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		FCNTL_COMPAT(fd, F_OFD_SETLKW, &lck);
88
89		SAFE_LSEEK(fd, pa->offset, SEEK_SET);
90		SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, pa->length);
91
92		lck.l_type = F_UNLCK;
93		FCNTL_COMPAT(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*/
108static 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(SAFE_WRITE_ALL, 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*/
147static 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 flock 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		FCNTL_COMPAT(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		FCNTL_COMPAT(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 */
208static 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
267static 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 */
276static 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
369static 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
384static void tests(unsigned int i)
385{
386	test_fn(tcases[i].fn0, tcases[i].fn1, tcases[i].fn2, tcases[i].desc);
387}
388
389static 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