git: 40431ec4da1d - main - cuse: Improve server cleanup

From: Christos Margiolis <christos_at_FreeBSD.org>
Date: Wed, 30 Sep 2026 09:51:25 UTC
The branch main has been updated by christos:

URL: https://cgit.FreeBSD.org/src/commit/?id=40431ec4da1d14032462ea6327ef695c2f58a3b1

commit 40431ec4da1d14032462ea6327ef695c2f58a3b1
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2026-09-30 09:49:52 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2026-09-30 09:49:52 +0000

    cuse: Improve server cleanup
    
    Move cuse_server_unref()'s device cleanup loop into a new
    cuse_server_free_devs_locked(), and call it from cuse_server_free()
    instead. The cdevpriv destructor now destroys the server's devices
    before dropping its reference, which closes the clients using them, so
    that the destructor is always the one that takes the last reference.
    
    By the time cuse_server_unref() frees the server, the device list should
    be empty, so assert this.
    
    In cuse_kern_uninit(), delete the infinite loop which waits for all open
    /dev/cuse instances to exit, and instead call destroy_dev() directly,
    which runs their cdevpriv destructor.
    
    MFC after:      1 week
    Sponsored by:   The FreeBSD Foundation
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D59872
---
 sys/fs/cuse/cuse.c | 52 ++++++++++++++++++++++------------------------------
 1 file changed, 22 insertions(+), 30 deletions(-)

diff --git a/sys/fs/cuse/cuse.c b/sys/fs/cuse/cuse.c
index 37b5f0494b38..1b562221fce3 100644
--- a/sys/fs/cuse/cuse.c
+++ b/sys/fs/cuse/cuse.c
@@ -290,25 +290,12 @@ cuse_kern_init(void)
 static void
 cuse_kern_uninit(void)
 {
-	void *ptr;
-
-	while (1) {
-		printf("Cuse: Please exit all /dev/cuse instances "
-		    "and processes which have used this device.\n");
-
-		pause("DRAIN", 2 * hz);
-
-		cuse_global_lock();
-		ptr = TAILQ_FIRST(&cuse_server_head);
-		cuse_global_unlock();
-
-		if (ptr == NULL)
-			break;
-	}
-
+	/* destroy_dev() runs the cdevpriv destructor of every open instance. */
 	if (cuse_dev != NULL)
 		destroy_dev(cuse_dev);
 
+	MPASS(TAILQ_EMPTY(&cuse_server_head));
+
 	mtx_destroy(&cuse_global_mtx);
 }
 
@@ -672,9 +659,21 @@ cuse_server_free_dev(struct cuse_server_dev *pcsd)
 }
 
 static void
-cuse_server_unref(struct cuse_server *pcs)
+cuse_server_free_devs_locked(struct cuse_server *pcs)
 {
 	struct cuse_server_dev *pcsd;
+
+	while ((pcsd = TAILQ_FIRST(&pcs->hdev)) != NULL) {
+		TAILQ_REMOVE(&pcs->hdev, pcsd, entry);
+		cuse_server_unlock(pcs);
+		cuse_server_free_dev(pcsd);
+		cuse_server_lock(pcs);
+	}
+}
+
+static void
+cuse_server_unref(struct cuse_server *pcs)
+{
 	struct cuse_memory *mem;
 
 	cuse_server_lock(pcs);
@@ -690,12 +689,8 @@ cuse_server_unref(struct cuse_server *pcs)
 	TAILQ_REMOVE(&cuse_server_head, pcs, entry);
 	cuse_global_unlock();
 
-	while ((pcsd = TAILQ_FIRST(&pcs->hdev)) != NULL) {
-		TAILQ_REMOVE(&pcs->hdev, pcsd, entry);
-		cuse_server_unlock(pcs);
-		cuse_server_free_dev(pcsd);
-		cuse_server_lock(pcs);
-	}
+	/* The cdevpriv destructor destroys the devices before unreffing. */
+	MPASS(TAILQ_EMPTY(&pcs->hdev));
 
 	cuse_free_unit_by_id_locked(pcs, -1);
 
@@ -743,13 +738,10 @@ cuse_server_free(void *arg)
 {
 	struct cuse_server *pcs = arg;
 
-	/*
-	 * The final server unref should be done by the server thread
-	 * to prevent deadlock in the client cdevpriv destructor,
-	 * which cannot destroy itself.
-	 */
-	while (cuse_server_do_close(pcs) != 1)
-		pause("W", hz);
+	cuse_server_lock(pcs);
+	cuse_server_is_closing(pcs);
+	cuse_server_free_devs_locked(pcs);
+	cuse_server_unlock(pcs);
 
 	/* drop final refcount */
 	cuse_server_unref(pcs);