git: 0b6b1c285920 - main - Add rangelock_may_recurse(9)
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 28 Aug 2024 14:49:57 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=0b6b1c285920563ba0c119d3190ac25af4731d02
commit 0b6b1c285920563ba0c119d3190ac25af4731d02
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2024-08-27 21:33:38 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2024-08-28 14:33:58 +0000
Add rangelock_may_recurse(9)
Reviewed by: markj
Tested by: lwhsu
Sponsored by: The FreeBSD Foundation
Differential revision: https://reviews.freebsd.org/D46465
---
sys/kern/kern_rangelock.c | 41 +++++++++++++++++++++++++++++++++++++++++
sys/sys/rangelock.h | 1 +
2 files changed, 42 insertions(+)
diff --git a/sys/kern/kern_rangelock.c b/sys/kern/kern_rangelock.c
index 0e62b91b4ee7..4d74c02302e7 100644
--- a/sys/kern/kern_rangelock.c
+++ b/sys/kern/kern_rangelock.c
@@ -752,6 +752,47 @@ rangelock_trywlock(struct rangelock *lock, vm_ooffset_t start, vm_ooffset_t end)
return (rangelock_lock_int(lock, true, start, end, RL_LOCK_WRITE));
}
+/*
+ * If the caller asserts that it can obtain the range locks on the
+ * same lock simultaneously, switch to the non-cheat mode. Cheat mode
+ * cannot handle it, hanging in drain or trylock retries.
+ */
+void
+rangelock_may_recurse(struct rangelock *lock)
+{
+ uintptr_t v, x;
+
+ v = atomic_load_ptr(&lock->head);
+ if ((v & RL_CHEAT_CHEATING) == 0)
+ return;
+
+ sleepq_lock(&lock->head);
+ for (;;) {
+ if ((v & RL_CHEAT_CHEATING) == 0) {
+ sleepq_release(&lock->head);
+ return;
+ }
+
+ /* Cheating and locked, drain. */
+ if ((v & RL_CHEAT_WLOCKED) != 0 ||
+ (v & ~RL_CHEAT_MASK) >= RL_CHEAT_READER) {
+ x = v | RL_CHEAT_DRAINING;
+ if (atomic_fcmpset_ptr(&lock->head, &v, x) != 0) {
+ rangelock_cheat_drain(lock);
+ return;
+ }
+ continue;
+ }
+
+ /* Cheating and unlocked, clear RL_CHEAT_CHEATING. */
+ x = 0;
+ if (atomic_fcmpset_ptr(&lock->head, &v, x) != 0) {
+ sleepq_release(&lock->head);
+ return;
+ }
+ }
+}
+
#ifdef INVARIANT_SUPPORT
void
_rangelock_cookie_assert(void *cookie, int what, const char *file, int line)
diff --git a/sys/sys/rangelock.h b/sys/sys/rangelock.h
index 127f101ddc2e..accf33d7296b 100644
--- a/sys/sys/rangelock.h
+++ b/sys/sys/rangelock.h
@@ -66,6 +66,7 @@ void *rangelock_wlock(struct rangelock *lock, vm_ooffset_t start,
void *rangelock_trywlock(struct rangelock *lock, vm_ooffset_t start,
vm_ooffset_t end);
void rangelock_entry_free(struct rl_q_entry *e);
+void rangelock_may_recurse(struct rangelock *lock);
#if defined(INVARIANTS) || defined(INVARIANT_SUPPORT)
void _rangelock_cookie_assert(void *cookie, int what, const char *file,
int line);