Home | History | Annotate | Download | only in fcntl
      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 (at) 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 
     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*/
     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*/
    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*/
    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 */
    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 
    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 */
    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 
    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