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