git: 2c52eba4f46e - stable/13 - linker_kldload_busy(): allow recursion

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sun, 05 Dec 2021 01:03:25 UTC
The branch stable/13 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=2c52eba4f46e2cc9a4fda3a9e6e81e06fb8daf57

commit 2c52eba4f46e2cc9a4fda3a9e6e81e06fb8daf57
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2021-11-12 19:45:06 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2021-12-05 01:02:57 +0000

    linker_kldload_busy(): allow recursion
    
    PR:     259748
    
    (cherry picked from commit 4f924a786ae08af496dfe55230f8fe1e2ca16150)
---
 sys/kern/kern_linker.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index cb5d587bfbbc..a10c9a0cea7e 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -106,7 +106,8 @@ MALLOC_DEFINE(M_LINKER, "linker", "kernel linker");
 linker_file_t linker_kernel_file;
 
 static struct sx kld_sx;	/* kernel linker lock */
-static bool kld_busy;
+static u_int kld_busy;
+static struct thread *kld_busy_owner;
 
 /*
  * Load counter used by clients to determine if a linker file has been
@@ -1065,7 +1066,9 @@ linker_kldload_busy(int flags)
 
 	if ((flags & LINKER_UB_LOCKED) == 0)
 		sx_xlock(&kld_sx);
-	while (kld_busy) {
+	while (kld_busy > 0) {
+		if (kld_busy_owner == curthread)
+			break;
 		error = sx_sleep(&kld_busy, &kld_sx,
 		    (flags & LINKER_UB_PCATCH) != 0 ? PCATCH : 0,
 		    "kldbusy", 0);
@@ -1075,7 +1078,8 @@ linker_kldload_busy(int flags)
 			return (error);
 		}
 	}
-	kld_busy = true;
+	kld_busy++;
+	kld_busy_owner = curthread;
 	if ((flags & LINKER_UB_UNLOCK) != 0)
 		sx_xunlock(&kld_sx);
 	return (0);
@@ -1090,9 +1094,15 @@ linker_kldload_unbusy(int flags)
 
 	if ((flags & LINKER_UB_LOCKED) == 0)
 		sx_xlock(&kld_sx);
-	MPASS(kld_busy);
-	kld_busy = false;
-	wakeup(&kld_busy);
+	MPASS(kld_busy > 0);
+	if (kld_busy_owner != curthread)
+		panic("linker_kldload_unbusy done by not owning thread %p",
+		    kld_busy_owner);
+	kld_busy--;
+	if (kld_busy == 0) {
+		kld_busy_owner = NULL;
+		wakeup(&kld_busy);
+	}
 	sx_xunlock(&kld_sx);
 }