git: 59aad2819456 - stable/13 - destroy_dev_sched*: Don't hold Giant for all deferred destroy_dev.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Fri, 13 May 2022 23:47:53 UTC
The branch stable/13 has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=59aad28194568efbcb34c6054bcb62fbc0524e5b

commit 59aad28194568efbcb34c6054bcb62fbc0524e5b
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-04-18 19:04:30 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-05-13 22:55:03 +0000

    destroy_dev_sched*: Don't hold Giant for all deferred destroy_dev.
    
    Rather than using taskqueue_swi_giant which holds Giant for all
    deferred destroy_dev calls, create a separate queue for destroyed
    devices with D_NEEDGIANT set in the corresponding cdevsw.  The task
    for this queue holds Giant whild destroying deferred devices while the
    task for the default queue does not hold Giant.
    
    In addition, switch to taskqueue_thread for destroy_dev_sched.
    Deferred destroy_dev requests don't need to run at an SWI priority.
    
    Reviewed by:    imp, markj
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D34915
    
    (cherry picked from commit d139909d6e8d540c0b832554d1b77fef0cf215b6)
---
 sys/kern/kern_conf.c | 34 ++++++++++++++++++++++++++++------
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c
index cb575114571a..f6724095c762 100644
--- a/sys/kern/kern_conf.c
+++ b/sys/kern/kern_conf.c
@@ -58,6 +58,7 @@ static void destroy_devl(struct cdev *dev);
 static int destroy_dev_sched_cbl(struct cdev *dev,
     void (*cb)(void *), void *arg);
 static void destroy_dev_tq(void *ctx, int pending);
+static void destroy_dev_tq_giant(void *ctx, int pending);
 static int make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw,
     int unit, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
     va_list ap);
@@ -1428,23 +1429,28 @@ clone_cleanup(struct clonedevs **cdp)
 
 static TAILQ_HEAD(, cdev_priv) dev_ddtr =
 	TAILQ_HEAD_INITIALIZER(dev_ddtr);
-static struct task dev_dtr_task = TASK_INITIALIZER(0, destroy_dev_tq, NULL);
+static TAILQ_HEAD(, cdev_priv) dev_ddtr_giant =
+	TAILQ_HEAD_INITIALIZER(dev_ddtr_giant);
+static struct task dev_dtr_task = TASK_INITIALIZER(0, destroy_dev_tq, &dev_ddtr);
+static struct task dev_dtr_task_giant = TASK_INITIALIZER(0, destroy_dev_tq_giant,
+    &dev_ddtr_giant);
 
 static void
 destroy_dev_tq(void *ctx, int pending)
 {
+	TAILQ_HEAD(, cdev_priv) *ddtr = ctx;
 	struct cdev_priv *cp;
 	struct cdev *dev;
 	void (*cb)(void *);
 	void *cb_arg;
 
 	dev_lock();
-	while (!TAILQ_EMPTY(&dev_ddtr)) {
-		cp = TAILQ_FIRST(&dev_ddtr);
+	while (!TAILQ_EMPTY(ddtr)) {
+		cp = TAILQ_FIRST(ddtr);
 		dev = &cp->cdp_c;
 		KASSERT(cp->cdp_flags & CDP_SCHED_DTR,
 		    ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp));
-		TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list);
+		TAILQ_REMOVE(ddtr, cp, cdp_dtr_list);
 		cb = cp->cdp_dtr_cb;
 		cb_arg = cp->cdp_dtr_cb_arg;
 		destroy_devl(dev);
@@ -1457,6 +1463,14 @@ destroy_dev_tq(void *ctx, int pending)
 	dev_unlock();
 }
 
+static void
+destroy_dev_tq_giant(void *ctx, int pending)
+{
+	mtx_lock(&Giant);
+	destroy_dev_tq(ctx, pending);
+	mtx_unlock(&Giant);
+}
+
 /*
  * devmtx shall be locked on entry. devmtx will be unlocked after
  * function return.
@@ -1465,6 +1479,7 @@ static int
 destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg)
 {
 	struct cdev_priv *cp;
+	bool need_giant;
 
 	dev_lock_assert_locked();
 	cp = cdev2priv(dev);
@@ -1476,9 +1491,16 @@ destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg)
 	cp->cdp_flags |= CDP_SCHED_DTR;
 	cp->cdp_dtr_cb = cb;
 	cp->cdp_dtr_cb_arg = arg;
-	TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list);
+	need_giant = (dev->si_devsw->d_flags & D_NEEDGIANT) != 0;
+	if (need_giant)
+		TAILQ_INSERT_TAIL(&dev_ddtr_giant, cp, cdp_dtr_list);
+	else
+		TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list);
 	dev_unlock();
-	taskqueue_enqueue(taskqueue_swi_giant, &dev_dtr_task);
+	if (need_giant)
+		taskqueue_enqueue(taskqueue_thread, &dev_dtr_task_giant);
+	else
+		taskqueue_enqueue(taskqueue_thread, &dev_dtr_task);
 	return (1);
 }