1 /* 2 * Copyright (c) 2022 Apple Inc. All rights reserved. 3 */ 4 5 #define PRIVATE 1 /* Needed for some F_OFD_* definitions */ 6 #include <fcntl.h> 7 #include <sys/fcntl.h> 8 #include <errno.h> 9 #include <darwintest.h> 10 #include <darwintest_utils.h> 11 #include <stdatomic.h> 12 13 #ifndef O_CLOFORK 14 #define O_CLOFORK 0x08000000 15 #endif 16 17 T_GLOBAL_META( 18 T_META_NAMESPACE("xnu.locks"), 19 T_META_RADAR_COMPONENT_NAME("xnu"), 20 T_META_RADAR_COMPONENT_VERSION("locks"), 21 T_META_OWNER("tim_marsland"), // with thanks to Peter Rutenbar 22 T_META_RUN_CONCURRENTLY(TRUE)); 23 24 enum lock_flags { 25 EXCL = 1, 26 WAIT = 2, 27 UNLOCK = 4, 28 }; 29 30 enum lock_type { 31 TYPE_FLOCK = 0, 32 TYPE_POSIX = 1, 33 TYPE_OFD = 2, 34 }; 35 36 static int 37 ofd_get(struct flock *fl, 38 int fd, off_t start, off_t len, pid_t pid, uint32_t flags) 39 { 40 fl->l_start = start; 41 fl->l_len = len; 42 fl->l_pid = pid; 43 fl->l_type = (flags & EXCL) ? F_WRLCK : F_RDLCK; 44 fl->l_whence = SEEK_SET; 45 return fcntl(fd, (pid == -1) ? F_OFD_GETLK : F_OFD_GETLKPID, fl); 46 } 47 48 static int 49 posix_get(struct flock *fl, 50 int fd, off_t start, off_t len, pid_t pid, uint32_t flags) 51 { 52 fl->l_start = start; 53 fl->l_len = len; 54 fl->l_pid = pid; 55 fl->l_type = (flags & EXCL) ? F_WRLCK : F_RDLCK; 56 fl->l_whence = SEEK_SET; 57 return fcntl(fd, (pid == -1) ? F_GETLK : F_GETLKPID, fl); 58 } 59 60 static int 61 posix_lock(int fd, off_t start, off_t len, uint32_t flags) 62 { 63 struct flock fl = { 64 .l_start = start, 65 .l_len = len, 66 .l_pid = -1, 67 .l_type = (flags & EXCL) ? F_WRLCK : F_RDLCK, 68 .l_whence = SEEK_SET, 69 }; 70 return fcntl(fd, (flags & WAIT) ? F_SETLKW : F_SETLK, &fl); 71 } 72 73 static int 74 ofd_lock(int fd, off_t start, off_t len, uint32_t flags) 75 { 76 struct flock fl = { 77 .l_start = start, 78 .l_len = len, 79 .l_pid = -1, 80 .l_type = (flags & EXCL) ? F_WRLCK : F_RDLCK, 81 .l_whence = SEEK_SET, 82 }; 83 return fcntl(fd, (flags & WAIT) ? F_OFD_SETLKW : F_OFD_SETLK, &fl); 84 } 85 86 static int 87 posix_unlock(int fd, off_t start, off_t len) 88 { 89 struct flock fl = { 90 .l_start = start, 91 .l_len = len, 92 .l_pid = -1, 93 .l_type = F_UNLCK, 94 .l_whence = SEEK_SET, 95 }; 96 return fcntl(fd, F_SETLK, &fl); 97 } 98 99 static int 100 ofd_unlock(int fd, off_t start, off_t len) 101 { 102 struct flock fl = { 103 .l_start = start, 104 .l_len = len, 105 .l_pid = -1, 106 .l_type = F_UNLCK, 107 .l_whence = SEEK_SET, 108 }; 109 return fcntl(fd, F_OFD_SETLK, &fl); 110 } 111 112 /* Is the given flock equal to the given arguments */ 113 static bool 114 flequal(struct flock *fl, off_t start, off_t len, pid_t pid, int flags) 115 { 116 if (start == fl->l_start && len == fl->l_len && pid == fl->l_pid) { 117 if (flags == EXCL && fl->l_type == F_WRLCK) { 118 return true; 119 } else if (flags == UNLOCK && fl->l_type == F_UNLCK) { 120 return true; 121 } else if (flags == 0 && fl->l_type == F_RDLCK) { 122 return true; 123 } 124 } 125 T_LOG("flequal: %lld %lld %d %x %x\n", 126 fl->l_start, fl->l_len, fl->l_pid, fl->l_type, fl->l_whence); 127 return false; 128 } 129 130 typedef struct { 131 pthread_t thread; 132 int fd, err; 133 bool complete; 134 enum lock_type lock_type; 135 uint32_t flags; 136 off_t start, end; 137 } lock_thread_state_t; 138 139 static void * 140 lock_thread( 141 void *arg) 142 { 143 lock_thread_state_t *lts = (lock_thread_state_t *)arg; 144 145 switch (lts->lock_type) { 146 case TYPE_FLOCK: { 147 int op = (lts->flags & EXCL) ? LOCK_EX : LOCK_SH; 148 op |= (lts->flags & WAIT) ? 0 : LOCK_NB; 149 lts->err = flock(lts->fd, op) ? errno : 0; 150 break; 151 } 152 case TYPE_POSIX: 153 lts->err = posix_lock(lts->fd, 154 lts->start, lts->end, lts->flags) ? errno : 0; 155 break; 156 case TYPE_OFD: 157 lts->err = ofd_lock(lts->fd, 158 lts->start, lts->end, lts->flags) ? errno : 0; 159 break; 160 } 161 162 atomic_thread_fence(memory_order_acquire); 163 lts->complete = true; 164 atomic_thread_fence(memory_order_release); 165 166 return NULL; 167 } 168 169 static bool 170 has_completed(lock_thread_state_t *lts) 171 { 172 atomic_thread_fence(memory_order_acquire); 173 const bool r = lts->complete; 174 atomic_thread_fence(memory_order_release); 175 return r; 176 } 177 178 static void 179 start_lock_thread(enum lock_type type, lock_thread_state_t *lts, 180 int fd, off_t start, off_t end, uint32_t flags) 181 { 182 lts->fd = fd; 183 lts->err = 0; 184 lts->complete = false; 185 lts->lock_type = type; 186 lts->flags = flags; 187 lts->start = start; 188 lts->end = end; 189 190 pthread_create(<s->thread, NULL, lock_thread, lts); 191 } 192 193 static void 194 random_pause(void) 195 { 196 const useconds_t usec = rand() & (16384 - 1); 197 usleep(usec + 1); 198 } 199 200 #define GET_CHECK( \ 201 str, fd, \ 202 get_start, get_len, get_flags, get_type, \ 203 chk_start, chk_len, chk_flags, chk_pid) \ 204 do { \ 205 struct flock _fl; \ 206 if (get_type == TYPE_OFD) { \ 207 T_ASSERT_POSIX_SUCCESS(ofd_get(&_fl, fd, get_start, get_len, -1, get_flags), str " (ofd_get)"); \ 208 } else { \ 209 T_ASSERT_POSIX_SUCCESS(posix_get(&_fl, fd, get_start, get_len, -1, get_flags), str " (posix_get)"); \ 210 } \ 211 T_ASSERT_TRUE(flequal(&_fl, chk_start, chk_len, chk_pid, chk_flags), str " (flequal)"); \ 212 } while (0) 213 214 #define LOCK_AND_CHECK( \ 215 str, fd, \ 216 lck_start, lck_len, lck_flags, lock_type, \ 217 get_start, get_len, get_flags, get_type, \ 218 chk_start, chk_len, chk_flags, chk_pid) \ 219 do { \ 220 if (lock_type == TYPE_OFD) { \ 221 T_ASSERT_POSIX_SUCCESS(ofd_lock(fd, lck_start, lck_len, lck_flags), str " (ofd_lock)"); \ 222 } else { \ 223 T_ASSERT_POSIX_SUCCESS(posix_lock(fd, lck_start, lck_len, lck_flags), str " (posix_lock)"); \ 224 } \ 225 GET_CHECK(str, fd, get_start, get_len, get_flags, get_type, chk_start, chk_len, chk_flags, chk_pid); \ 226 } while (0) 227 228 #define UNLOCK_AND_CHECK( \ 229 str, fd, \ 230 lck_start, lck_len, unlock_type, \ 231 get_start, get_len, get_flags, get_type, \ 232 chk_start, chk_len, chk_flags, chk_pid) \ 233 do { \ 234 if (unlock_type == TYPE_OFD) { \ 235 T_ASSERT_POSIX_SUCCESS(ofd_unlock(fd, lck_start, lck_len), str " (ofd_unlock)"); \ 236 } else { \ 237 T_ASSERT_POSIX_SUCCESS(posix_unlock(fd, lck_start, lck_len), str " (posix_unlock)"); \ 238 } \ 239 GET_CHECK(str, fd, get_start, get_len, get_flags, get_type, chk_start, chk_len, chk_flags, chk_pid); \ 240 } while (0) 241 242 #define A_PATH "basic_lockf_a" 243 #define B_PATH "basic_lockf_b" 244 245 T_DECL(lockf_basic, 246 "Basic test of flock/POSIX/OFD advisory file locks", 247 T_META_CHECK_LEAKS(false), T_META_TAG_VM_PREFERRED) 248 { 249 const char *tmpdir = dt_tmpdir(); 250 lock_thread_state_t lts[4]; 251 pid_t pid = getpid(); 252 int a, a_confined, b, b_confined; 253 const off_t file_len = 0x10000; 254 T_SETUPBEGIN; 255 256 /* random sleeping to hunt for races */ 257 258 unsigned seed = (unsigned)pid; 259 const char *p = getenv("LOCKF_BASIC_SRAND_SEED"); 260 if (p) { 261 seed = (unsigned)atol(p); 262 } 263 srand(seed); 264 265 /* Create two test files, a and b */ 266 T_ASSERT_POSIX_SUCCESS(chdir(tmpdir), "chdir(%s)", tmpdir); 267 T_ASSERT_POSIX_SUCCESS((a = open(A_PATH, O_CREAT | O_RDWR, 0666)), "open(a)"); 268 T_ASSERT_POSIX_SUCCESS((b = open(B_PATH, O_CREAT | O_RDWR, 0666)), "open(b)"); 269 270 /* Give both files 64KiB */ 271 T_ASSERT_POSIX_SUCCESS(ftruncate(a, file_len), "truncate a"); 272 T_ASSERT_POSIX_SUCCESS(ftruncate(b, 0x10000), "truncate b"); 273 274 /* Open a/b again, but CONFINED this time */ 275 T_ASSERT_POSIX_SUCCESS((a_confined = open(A_PATH, O_CLOFORK | O_RDWR)), "open(a, O_CLOFORK)"); 276 T_ASSERT_POSIX_SUCCESS(fcntl(a_confined, F_SETCONFINED, 1), "F_SETCONFINED"); 277 T_ASSERT_POSIX_SUCCESS((b_confined = open(B_PATH, O_CLOFORK | O_RDWR)), "open(b, O_CLOFORK)"); 278 T_ASSERT_POSIX_SUCCESS(fcntl(b_confined, F_SETCONFINED, 1), "F_SETCONFINED"); 279 280 T_SETUPEND; 281 282 /* Test all coalescence cases (non-upgrade/downgrade) */ 283 284 /* 285 * [ ] 286 * + [ ] 287 * = [ ] 288 */ 289 T_ASSERT_POSIX_SUCCESS(posix_lock(a, 130, 20, 0), "Coalesce: initial posix lock"); 290 LOCK_AND_CHECK("Coalesce: equal", a, 291 130, 20, 0, TYPE_POSIX, /* POSIX-lock a, shared, from [100..199] */ 292 0, 0, EXCL, TYPE_OFD, /* OFD-get the entire file, exclusively */ 293 130, 20, 0, pid); /* The result should be: [100..199] is locked shared by our PID */ 294 295 /* 296 * [ ] 297 * + [ ] 298 * = [ ] 299 */ 300 LOCK_AND_CHECK("Coalesce: adjacent high", a, 301 150, 25, 0, TYPE_POSIX, 302 0, 0, EXCL, TYPE_OFD, 303 130, 45, 0, pid); 304 305 /* 306 * [ ] 307 * + [ ] 308 * = [ ] 309 */ 310 LOCK_AND_CHECK("Coalesce: adjacent low", a, 311 125, 5, 0, TYPE_POSIX, 312 0, 0, EXCL, TYPE_OFD, 313 125, 50, 0, pid); 314 315 /* 316 * [ ] 317 * + [ ] 318 * = [ ] 319 */ 320 LOCK_AND_CHECK("Coalesce: subsume smaller", a, 321 150, 10, 0, TYPE_POSIX, 322 0, 0, EXCL, TYPE_OFD, 323 125, 50, 0, pid); 324 325 /* 326 * [ ] 327 * + [ ] 328 * = [ ] 329 */ 330 LOCK_AND_CHECK("Coalesce: subsume larger", a, 331 100, 100, 0, TYPE_POSIX, 332 0, 0, EXCL, TYPE_OFD, 333 100, 100, 0, pid); 334 335 /* 336 * [ ] 337 * + [ ] 338 * = [ ] 339 */ 340 LOCK_AND_CHECK("Coalesce: extend high", a, 341 100, 125, 0, TYPE_POSIX, 342 0, 0, EXCL, TYPE_OFD, 343 100, 125, 0, pid); 344 345 /* 346 * [ ] 347 * + [ ] 348 * = [ ] 349 */ 350 LOCK_AND_CHECK("Coalesce: extend low", a, 351 75, 150, 0, TYPE_POSIX, 352 0, 0, EXCL, TYPE_OFD, 353 75, 150, 0, pid); 354 355 /* 356 * [ ] 357 * + [ ] 358 * = [ ] 359 */ 360 LOCK_AND_CHECK("Coalesce: overlap start", a, 361 50, 100, 0, TYPE_POSIX, 362 0, 0, EXCL, TYPE_OFD, 363 50, 175, 0, pid); 364 365 /* 366 * [ ] 367 * + [ ] 368 * = [ ] 369 */ 370 LOCK_AND_CHECK("Coalesce: overlap end", a, 371 150, 100, 0, TYPE_POSIX, 372 0, 0, EXCL, TYPE_OFD, 373 50, 200, 0, pid); 374 375 /* Test all upgrade cases */ 376 377 /* 378 * [ R ] 379 * + [ W ] 380 * = [ R | W ] 381 */ 382 LOCK_AND_CHECK("Upgrade: adjacent high not-coalesced", a, 383 250, 50, EXCL, TYPE_POSIX, /* Take the posix lock exclusively */ 384 50, 250, 0, TYPE_OFD, /* and OFD-get shared */ 385 250, 50, EXCL, pid); 386 387 /* 388 * [ R | W ] 389 * + [ W ] 390 * = [ W | R | W ] 391 */ 392 LOCK_AND_CHECK("Upgrade: adjacent low not-coalesced", a, 393 25, 25, EXCL, TYPE_POSIX, 394 25, 225, 0, TYPE_OFD, 395 25, 25, EXCL, pid); 396 397 /* 398 * 25 50 250 300 399 * [ W | R | W ] 400 * + [ W ] 401 * [ W | R | W ] 402 * 25 50 225 300 403 */ 404 LOCK_AND_CHECK("Upgrade: truncate shared-end, grow excl-start", a, 405 225, 50, EXCL, TYPE_POSIX, 406 50, 250, 0, TYPE_OFD, 407 225, 75, EXCL, pid); 408 409 /* 410 * 25 50 225 300 411 * [ W | R | W ] 412 * + [ W ] 413 * = [ W | R | W ] 414 * 25 60 225 300 415 */ 416 LOCK_AND_CHECK("Upgrade: truncate shared-start, grow excl-end", a, 417 40, 20, EXCL, TYPE_POSIX, 418 0, 225, 0, TYPE_OFD, 419 25, 35, EXCL, pid); 420 421 /* 422 * 25 60 225 300 423 * [ W | R | W ] 424 * + [ W ] 425 * = [ W | | W | | W ] 426 * 25 60 100 150 225 300 427 */ 428 LOCK_AND_CHECK("Upgrade: 3-way split", a, 429 100, 50, EXCL, TYPE_POSIX, 430 60, 165, 0, TYPE_OFD, 431 100, 50, EXCL, pid); 432 433 /* 434 * 25 60 100 150 225 300 435 * [ W | | W | | W ] 436 * + [ W ] 437 * = [ W ] 438 * 25 300 439 */ 440 LOCK_AND_CHECK("Upgrade: subsume multiple locks", a, 441 25, 275, EXCL, TYPE_POSIX, 442 0, 0, 0, TYPE_OFD, 443 25, 275, EXCL, pid); 444 445 /* Unlock / waiter-wakeup cases */ 446 447 /* 448 * 25 300 449 * [ W ] 450 * + [W (wait)] 451 * 290 310 452 */ 453 start_lock_thread(TYPE_OFD, <s[0], a, 290, 20, EXCL | WAIT); /* Wait on this lock in another thread */ 454 random_pause(); 455 T_ASSERT_FALSE(has_completed(<s[0]), "Unlock: created waiting lock"); 456 457 /* 458 * 25 300 459 * [ W ] 460 * - [ - ] 461 * = [ W ] 462 * 25 300 463 */ 464 UNLOCK_AND_CHECK("Unlock: region with no locks", a, 465 300, 100, TYPE_POSIX, 466 0, 0, 0, TYPE_OFD, 467 25, 275, EXCL, pid); 468 T_ASSERT_FALSE(has_completed(<s[0]), "Unlock: waiter still waiting"); 469 470 /* 471 * 25 300 472 * [ W ] 473 * - [ - ] 474 * = [ W ] 475 * 25 250 476 */ 477 UNLOCK_AND_CHECK("Unlock: overlap end", a, 478 250, 100, TYPE_POSIX, 479 25, 1, 0, TYPE_OFD, 480 25, 225, EXCL, pid); 481 482 /* 483 * 25 250 484 * [ W ] 485 * [ W ] 486 * 290 310 487 */ 488 489 while (!has_completed(<s[0])) { 490 random_pause(); 491 } 492 T_ASSERT_TRUE(has_completed(<s[0]), "Unlock: waiter woke up"); 493 T_ASSERT_POSIX_ZERO(lts[0].err, "Unlock: no err granting waiter"); 494 GET_CHECK("Unlock: waiter granted confirmation", a, 495 250, 100, 0, TYPE_POSIX, 496 290, 20, EXCL, -1); /* -1 because it's a non-CONFINED OFD lock (with no PID) */ 497 498 T_ASSERT_POSIX_SUCCESS(ofd_unlock(a, 290, 20), "Unlock: unlock that now-granted waiter"); 499 500 /* 501 * 25 250 502 * [ W ] 290 310 503 * [ W ] 504 * - [ - ] 505 * = [ W ] 506 * 25 250 507 */ 508 UNLOCK_AND_CHECK("Unlock: equal range", a, 509 290, 20, TYPE_POSIX, 510 0, 0, 0, TYPE_OFD, 511 25, 225, EXCL, pid); 512 513 /* 514 * 25 250 515 * [ W ] 516 * - [ - ] 517 * = [ W ] 518 * 50 250 519 */ 520 UNLOCK_AND_CHECK("Unlock: overlap start", a, 521 0, 50, TYPE_POSIX, 522 0, 0, 0, TYPE_OFD, 523 50, 200, EXCL, pid); 524 525 /* 526 * 50 250 527 * [ W ] 528 * - [ ] 529 * = [ W ] 530 * 100 250 531 */ 532 UNLOCK_AND_CHECK("Unlock: start-aligned", a, 533 50, 50, TYPE_POSIX, 534 0, 0, 0, TYPE_OFD, 535 100, 150, EXCL, pid); 536 537 /* 538 * 100 250 539 * [ W ] 540 * - [ ] 541 * = [ W ] 542 * 100 200 543 */ 544 UNLOCK_AND_CHECK("Unlock: end-aligned", a, 545 200, 50, TYPE_POSIX, 546 0, 0, 0, TYPE_OFD, 547 100, 100, EXCL, pid); 548 549 /* 550 * 100 200 551 * [ W ] 552 * - [ ] 553 * = [ W ] [ W ] 554 * 100 125 175 200 555 */ 556 UNLOCK_AND_CHECK("Unlock: split", a, 557 125, 50, TYPE_POSIX, 558 0, 150, 0, TYPE_OFD, 559 100, 25, EXCL, pid); 560 561 /* Check the tail-fragment too */ 562 GET_CHECK("Unlock: split (tail-check)", a, 563 125, 200, 0, TYPE_OFD, 564 175, 25, EXCL, pid); 565 566 /* 567 * 100 125 175 200 568 * [ W ] [ W ] 569 * - [ ] 570 * = (no locks) 571 */ 572 UNLOCK_AND_CHECK("Unlock: multiple locks", a, 573 0, 0, TYPE_POSIX, 574 0, 0, 0, TYPE_OFD, 575 0, 0, UNLOCK, -1); 576 577 578 /* 579 * Downgrade / waiter-wakeup test: 580 * The only interesting different between this and upgrade is that waiting locks 581 * may now be granted. So let's test that... 582 */ 583 584 /* 585 * Create this lock layout 586 * 0 500 587 * [ W ] 588 * 589 * 0 100 200 300 400 600 590 * [R(wait)] [R(wait)] [ R(wait) ] 591 * 592 * 50 450 593 * [ R(wait) ] 594 */ 595 596 LOCK_AND_CHECK("Downgrade: first lock", a, 597 0, 500, EXCL, TYPE_POSIX, 598 0, 0, 0, TYPE_OFD, 599 0, 500, EXCL, pid); 600 601 start_lock_thread(TYPE_OFD, <s[0], a, 0, 100, WAIT); 602 start_lock_thread(TYPE_OFD, <s[1], a, 200, 100, WAIT); 603 start_lock_thread(TYPE_OFD, <s[2], a, 400, 200, WAIT); 604 start_lock_thread(TYPE_OFD, <s[3], a, 50, 400, WAIT); 605 606 random_pause(); /* wait a bit to allow the lock threads to run */ 607 608 T_ASSERT_FALSE(has_completed(<s[0]), "Downgrade: waiter 0 waiting");; 609 T_ASSERT_FALSE(has_completed(<s[1]), "Downgrade: waiter 1 waiting"); 610 T_ASSERT_FALSE(has_completed(<s[2]), "Downgrade: waiter 2 waiting"); 611 T_ASSERT_FALSE(has_completed(<s[3]), "Downgrade: waiter 3 waiting"); 612 613 /* Open a gap just wide enough for the [200..300] lock to be granted */ 614 T_ASSERT_POSIX_SUCCESS(posix_lock(a, 200, 100, 0), "Downgrade [200..300]"); 615 616 /* 617 * (We can't use LOCK_AND_CHECK here, because there may be two shared locks 618 * with that range (our downgrade, plus waiter #1) - so ofd_get() could return 619 * either one non-deterministically.) 620 */ 621 622 while (!has_completed(<s[1])) { 623 random_pause(); /* wait for waiter #1 to complete */ 624 } 625 T_ASSERT_FALSE(has_completed(<s[0]), "Downgrade: waiter 0 waiting"); 626 T_ASSERT_TRUE(has_completed(<s[1]), "Downgrade: waiter 1 awoken"); 627 T_ASSERT_FALSE(has_completed(<s[2]), "Downgrade: waiter 2 waiting"); 628 T_ASSERT_FALSE(has_completed(<s[3]), "Downgrade: waiter 3 waiting"); 629 630 /* Open a gap just wide enough for the [400..600] lock to be granted */ 631 T_ASSERT_POSIX_SUCCESS(posix_lock(a, 400, 100, 0), "Downgrade [400..500]"); 632 633 while (!has_completed(<s[2])) { 634 random_pause(); /* wait for waiter #2 to complete */ 635 } 636 T_ASSERT_FALSE(has_completed(<s[0]), "Downgrade: waiter 0 waiting"); 637 T_ASSERT_TRUE(has_completed(<s[2]), "Downgrade: waiter 2 awoken"); 638 T_ASSERT_FALSE(has_completed(<s[3]), "Downgrade: waiter 3 waiting"); 639 640 /* Downgrade the remaining chunks */ 641 T_ASSERT_POSIX_SUCCESS(posix_lock(a, 0, 500, 0), "Downgrade [0..500]"); 642 643 while (!has_completed(<s[0]) || !has_completed(<s[3])) { 644 random_pause(); /* wait for waiters #0 and #3 to complete */ 645 } 646 T_ASSERT_TRUE(has_completed(<s[0]), "Downgrade: waiter 0 awoken"); 647 T_ASSERT_TRUE(has_completed(<s[3]), "Downgrade: waiter 3 awoken"); 648 649 /* Unlock the remaining OFD shared locks */ 650 UNLOCK_AND_CHECK("Downgrade: cleanup (unlock all shared OFD locks)", a, 651 0, 0, TYPE_OFD, 652 0, 0, 0, TYPE_POSIX, 653 0, 0, UNLOCK, -1); 654 655 /* Unlock the remaining POSIX lock [0..500] */ 656 UNLOCK_AND_CHECK("Downgrade: cleanup (unlock all shared posix locks)", a, 657 0, 500, TYPE_POSIX, 658 0, 0, 0, TYPE_OFD, 659 0, 0, UNLOCK, -1); 660 661 662 /* Test SEEK_END flock range decoding */ 663 664 { 665 /* SEEK_END start=-10 len=10 */ 666 struct flock fl = {.l_start = -10, .l_len = 10, .l_type = F_WRLCK, .l_whence = SEEK_END}; 667 T_ASSERT_POSIX_SUCCESS(fcntl(a, F_SETLK, &fl), "SEEK_END: -10, 10"); 668 GET_CHECK("SEEK_END: -10, 10 ", a, 669 0, 0, EXCL, TYPE_OFD, 670 file_len - 10, 10, EXCL, pid); 671 } 672 673 { 674 /* SEEK_END start=-10 len=10 */ 675 struct flock fl = {.l_start = -file_len, .l_len = file_len, .l_type = F_WRLCK, .l_whence = SEEK_END}; 676 T_ASSERT_POSIX_SUCCESS(fcntl(a, F_SETLK, &fl), "SEEK_END: -file_len, file_len"); 677 GET_CHECK("SEEK_END: -file_len, file_len", a, 678 0, 0, EXCL, TYPE_OFD, 679 0, file_len, EXCL, pid); 680 } 681 682 { 683 /* Negative case: SEEK_END start=-(file_len + 10) len=20 */ 684 struct flock fl = {.l_start = -(file_len + 10), .l_len = 20, .l_type = F_WRLCK, .l_whence = SEEK_END}; 685 T_EXPECT_TRUE(fcntl(a, F_SETLK, &fl) && errno == EINVAL, "SEEK_END: -(file_len + 10), 20 => EINVAL"); 686 } 687 688 T_ASSERT_POSIX_SUCCESS(posix_unlock(a, 0, 0), "SEEK_END: cleanup (release locks)"); 689 690 /* Test interactions between all 3 lock types */ 691 692 T_ASSERT_POSIX_SUCCESS(flock(a, LOCK_SH | LOCK_NB), "Interaction: flock(a, shared)"); 693 694 /* Take (waiting) exclusive locks in all 3 types on a_confined */ 695 start_lock_thread(TYPE_FLOCK, <s[0], a_confined, 0, 0, EXCL | WAIT); 696 start_lock_thread(TYPE_POSIX, <s[1], a_confined, 0, 0, EXCL | WAIT); 697 start_lock_thread(TYPE_OFD, <s[2], a_confined, 0, 0, EXCL | WAIT); 698 699 /* Yuck. Allow time for these threads to run -and- block on the flock lock */ 700 sleep(1); 701 702 /* Take shared locks in the remaining 2 types (posix/ofd) on a */ 703 704 T_ASSERT_POSIX_SUCCESS(posix_lock(a, 0, 0, 0), "Interaction: posix_lock(a, 0, 0, 0)"); 705 T_ASSERT_POSIX_SUCCESS(ofd_lock(a, 0, 0, 0), "Interaction: ofd_lock(a, 0, 0, 0)"); 706 707 T_ASSERT_FALSE(has_completed(<s[0]), "Interaction: flock-waiter is starting or waiting"); 708 T_ASSERT_FALSE(has_completed(<s[1]), "Interaction: posix-waiter is starting or waiting"); 709 T_ASSERT_FALSE(has_completed(<s[2]), "Interaction: ofd-waiter is starting or waiting"); 710 711 T_EXPECT_POSIX_FAILURE(flock(a, LOCK_EX | LOCK_NB), EAGAIN, "Interaction: can't flock-upgrade"); 712 T_EXPECT_POSIX_FAILURE(posix_lock(a, 0, 0, EXCL), EAGAIN, "Interaction: can't posix-upgrade"); 713 T_EXPECT_POSIX_FAILURE(ofd_lock(a, 0, 0, EXCL), EAGAIN, "Interaction: can't ofd-upgrade"); 714 715 /* 716 * At this point: 717 * - 'a' owns a shared flock && a shared OFD lock 718 * - this process owns a shared POSIX lock 719 * - three threads are (hopefully) blocked waiting to take exclusive locks 720 * on 'a_confined.' 721 * 722 * Drop the POSIX lock ... 723 */ 724 T_ASSERT_POSIX_SUCCESS(posix_unlock(a, 0, 0), "Interaction: Unlock posix"); 725 726 random_pause(); /* wait a bit to see if there are consequences for the waiting locks */ 727 728 T_ASSERT_FALSE(has_completed(<s[0]), "Interaction: flock-waiter is still starting or waiting"); 729 T_ASSERT_FALSE(has_completed(<s[1]), "Interaction: posix-waiter is still starting or waiting"); 730 T_ASSERT_FALSE(has_completed(<s[2]), "Interaction: ofd-waiter is still starting or waiting"); 731 732 /* 733 * and drop the flock lock ... 734 */ 735 T_ASSERT_POSIX_SUCCESS(flock(a, LOCK_UN), "Interaction: Unlock flock"); 736 737 random_pause(); /* wait a bit to see if there are consequences for the waiting locks */ 738 739 // Check that rdar://102160410 remains fixed. 740 // Before that, the LOCK_UN above would release the OFD lock too 741 742 T_ASSERT_FALSE(has_completed(<s[0]), "Interaction: flock-waiter is still starting or waiting"); 743 T_ASSERT_FALSE(has_completed(<s[1]), "Interaction: posix-waiter is still starting or waiting"); 744 T_ASSERT_FALSE(has_completed(<s[2]), "Interaction: ofd-waiter is still starting or waiting"); 745 746 /* 747 * and finally drop the OFD lock, which should let one of the blocked threads 748 * acquire an exclusive lock. Work through them turn by turn. 749 */ 750 T_ASSERT_POSIX_SUCCESS(ofd_unlock(a, 0, 0), "Interaction: Unlock ofd"); 751 752 bool unlocked[3] = { 753 false, false, false 754 }; 755 756 for (uint32_t waiters = 3; waiters > 0; waiters--) { 757 uint32_t num_waiting = 0; 758 do { 759 random_pause(); /* wait for consequences for the waiting locks */ 760 num_waiting = !has_completed(<s[0]) + !has_completed(<s[1]) + !has_completed(<s[2]); 761 } while (num_waiting == waiters); 762 763 T_ASSERT_EQ(num_waiting, waiters - 1, "Interaction: 1 waiter awoke"); 764 765 if (has_completed(<s[0]) && !unlocked[0]) { 766 T_ASSERT_POSIX_SUCCESS(flock(a_confined, LOCK_UN), "Interaction: Flock awoke, unlocking"); 767 unlocked[0] = true; 768 } else if (has_completed(<s[1]) && !unlocked[1]) { 769 T_ASSERT_POSIX_SUCCESS(posix_unlock(a_confined, 0, 0), "Interaction: posix awoke, unlocking"); 770 unlocked[1] = true; 771 } else if (has_completed(<s[2]) && !unlocked[2]) { 772 T_ASSERT_POSIX_SUCCESS(ofd_unlock(a_confined, 0, 0), "Interaction: ofd awoke, unlocking"); 773 unlocked[2] = true; 774 } 775 } 776 777 T_ASSERT_TRUE(has_completed(<s[0]), "Interaction: flock-waiter has completed"); 778 T_ASSERT_TRUE(unlocked[0], "Interaction: flock-waiter was unlocked"); 779 780 T_ASSERT_TRUE(has_completed(<s[1]), "Interaction: posix-waiter has completed"); 781 T_ASSERT_TRUE(unlocked[1], "Interaction: posix-waiter was unlocked"); 782 783 T_ASSERT_TRUE(has_completed(<s[2]), "Interaction: ofd-waiter has completed"); 784 T_ASSERT_TRUE(unlocked[2], "Interaction: ofd-waiter was unlocked"); 785 } 786