* Wait for a wakeup or a signal. This does not guarantee that the count is * still zero on return. Callers wanting a precise answer should use * blockcount_wait() with an interlock. * * If there is no work to wait for, return 0. If the sleep was interrupted by a * signal, return EINTR or ERESTART, and return EAGAIN otherwise. */
| 408 | * signal, return EINTR or ERESTART, and return EAGAIN otherwise. |
| 409 | */ |
| 410 | int |
| 411 | _blockcount_sleep(blockcount_t *bc, struct lock_object *lock, const char *wmesg, |
| 412 | int prio) |
| 413 | { |
| 414 | void *wchan; |
| 415 | uintptr_t lock_state; |
| 416 | u_int old; |
| 417 | int ret; |
| 418 | bool catch, drop; |
| 419 | |
| 420 | KASSERT(lock != &Giant.lock_object, |
| 421 | ("%s: cannot use Giant as the interlock", __func__)); |
| 422 | |
| 423 | catch = (prio & PCATCH) != 0; |
| 424 | drop = (prio & PDROP) != 0; |
| 425 | prio &= PRIMASK; |
| 426 | |
| 427 | /* |
| 428 | * Synchronize with the fence in blockcount_release(). If we end up |
| 429 | * waiting, the sleepqueue lock acquisition will provide the required |
| 430 | * side effects. |
| 431 | * |
| 432 | * If there is no work to wait for, but waiters are present, try to put |
| 433 | * ourselves to sleep to avoid jumping ahead. |
| 434 | */ |
| 435 | if (atomic_load_acq_int(&bc->__count) == 0) { |
| 436 | if (lock != NULL && drop) |
| 437 | LOCK_CLASS(lock)->lc_unlock(lock); |
| 438 | return (0); |
| 439 | } |
| 440 | lock_state = 0; |
| 441 | wchan = bc; |
| 442 | sleepq_lock(wchan); |
| 443 | DROP_GIANT(); |
| 444 | if (lock != NULL) |
| 445 | lock_state = LOCK_CLASS(lock)->lc_unlock(lock); |
| 446 | old = blockcount_read(bc); |
| 447 | ret = 0; |
| 448 | do { |
| 449 | if (_BLOCKCOUNT_COUNT(old) == 0) { |
| 450 | sleepq_release(wchan); |
| 451 | goto out; |
| 452 | } |
| 453 | if (_BLOCKCOUNT_WAITERS(old)) |
| 454 | break; |
| 455 | } while (!atomic_fcmpset_int(&bc->__count, &old, |
| 456 | old | _BLOCKCOUNT_WAITERS_FLAG)); |
| 457 | sleepq_add(wchan, NULL, wmesg, catch ? SLEEPQ_INTERRUPTIBLE : 0, 0); |
| 458 | if (catch) |
| 459 | ret = sleepq_wait_sig(wchan, prio); |
| 460 | else |
| 461 | sleepq_wait(wchan, prio); |
| 462 | if (ret == 0) |
| 463 | ret = EAGAIN; |
| 464 | |
| 465 | out: |
| 466 | PICKUP_GIANT(); |
| 467 | if (lock != NULL && !drop) |
no test coverage detected