aboutsummaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2024-08-27 21:33:38 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2024-08-28 14:33:58 +0000
commit0b6b1c285920563ba0c119d3190ac25af4731d02 (patch)
tree0e3c1e020a0715001ac3fcb0922bab0185c99100 /sys/kern
parent75447afca868f82f1c53c5be32dccd777813ec1a (diff)
downloadsrc-0b6b1c285920563ba0c119d3190ac25af4731d02.tar.gz
src-0b6b1c285920563ba0c119d3190ac25af4731d02.zip
Add rangelock_may_recurse(9)
Reviewed by: markj Tested by: lwhsu Sponsored by: The FreeBSD Foundation Differential revision: https://reviews.freebsd.org/D46465
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_rangelock.c41
1 files changed, 41 insertions, 0 deletions
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)