git: 68f28dd1ccc3 - main - ip_mroute: do not sleep when lock is taken

From: Wojciech Macek <wma_at_FreeBSD.org>
Date: Tue, 11 Jan 2022 10:19:44 UTC
The branch main has been updated by wma:

URL: https://cgit.FreeBSD.org/src/commit/?id=68f28dd1ccc33b870bb8c0509694df4ed5e05ee7

commit 68f28dd1ccc33b870bb8c0509694df4ed5e05ee7
Author:     Wojciech Macek <wma@FreeBSD.org>
AuthorDate: 2022-01-10 07:09:16 +0000
Commit:     Wojciech Macek <wma@FreeBSD.org>
CommitDate: 2022-01-11 10:19:32 +0000

    ip_mroute: do not sleep when lock is taken
    
    Kthread initialization calls uma_alloc which can sleep.
    Modify the code to use deferred work instead.
---
 sys/netinet/ip_mroute.c | 64 ++++++++++++++++++++++---------------------------
 1 file changed, 29 insertions(+), 35 deletions(-)

diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
index 02738616d56e..a380a9b864b8 100644
--- a/sys/netinet/ip_mroute.c
+++ b/sys/netinet/ip_mroute.c
@@ -99,6 +99,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
 #include <sys/systm.h>
+#include <sys/taskqueue.h>
 #include <sys/time.h>
 #include <sys/counter.h>
 #include <machine/atomic.h>
@@ -177,6 +178,10 @@ VNET_DEFINE_STATIC(u_char *, nexpire);		/* 0..mfchashsize-1 */
 #define	V_nexpire		VNET(nexpire)
 VNET_DEFINE_STATIC(LIST_HEAD(mfchashhdr, mfc)*, mfchashtbl);
 #define	V_mfchashtbl		VNET(mfchashtbl)
+VNET_DEFINE_STATIC(struct taskqueue *, task_queue);
+#define	V_task_queue		VNET(task_queue)
+VNET_DEFINE_STATIC(struct task, task);
+#define	V_task		VNET(task)
 
 VNET_DEFINE_STATIC(vifi_t, numvifs);
 #define	V_numvifs		VNET(numvifs)
@@ -232,8 +237,6 @@ SYSCTL_ULONG(_net_inet_pim, OID_AUTO, squelch_wholepkt, CTLFLAG_RW,
     &pim_squelch_wholepkt, 0,
     "Disable IGMP_WHOLEPKT notifications if rendezvous point is unspecified");
 
-static volatile int upcall_thread_shutdown = 0;
-
 static const struct encaptab *pim_encap_cookie;
 static int pim_encapcheck(const struct mbuf *, int, int, void *);
 static int pim_input(struct mbuf *, int, int, void *);
@@ -660,22 +663,15 @@ if_detached_event(void *arg __unused, struct ifnet *ifp)
 }
 
 static void
-ip_mrouter_upcall_thread(void *arg)
+ip_mrouter_upcall_thread(void *arg, int pending __unused)
 {
 	CURVNET_SET((struct vnet *) arg);
 
-	while (upcall_thread_shutdown == 0) {
-		/* START: Event loop */
-
-		/* END: Event loop */
-		mtx_lock(&V_upcall_thread_mtx);
-		cv_timedwait(&V_upcall_thread_cv, &V_upcall_thread_mtx, hz);
-		mtx_unlock(&V_upcall_thread_mtx);
-	}
+	MRW_WLOCK();
+	bw_upcalls_send();
+	MRW_WUNLOCK();
 
-	upcall_thread_shutdown = 0;
 	CURVNET_RESTORE();
-	kthread_exit();
 }
 
 /*
@@ -718,12 +714,9 @@ ip_mrouter_init(struct socket *so, int version)
 	return (ENOMEM);
     }
 
-    /* Create upcall thread */
-    upcall_thread_shutdown = 0;
-    mtx_init(&V_upcall_thread_mtx, "ip_mroute upcall thread mtx", NULL, MTX_DEF);
-    cv_init(&V_upcall_thread_cv, "ip_mroute upcall cv");
-    kthread_add(ip_mrouter_upcall_thread, curvnet,
-        NULL, NULL, 0, 0, "ip_mroute upcall thread");
+    TASK_INIT(&V_task, 0, ip_mrouter_upcall_thread, curvnet);
+    taskqueue_cancel(V_task_queue, &V_task, NULL);
+    taskqueue_unblock(V_task_queue);
 
     callout_reset(&V_expire_upcalls_ch, EXPIRE_TIMEOUT, expire_upcalls,
 	curvnet);
@@ -766,17 +759,14 @@ X_ip_mrouter_done(void)
 
     MROUTER_WAIT();
 
-    MRW_WLOCK();
-
-    upcall_thread_shutdown = 1;
-    mtx_lock(&V_upcall_thread_mtx);
-    cv_signal(&V_upcall_thread_cv);
-    mtx_unlock(&V_upcall_thread_mtx);
-
-    /* Wait for thread shutdown */
-    while (upcall_thread_shutdown == 1) {};
+    /* Stop and drain task queue */
+    taskqueue_block(V_task_queue);
+    while (taskqueue_cancel(V_task_queue, &V_task, NULL)) {
+    	taskqueue_drain(V_task_queue, &V_task);
+    }
 
-    mtx_destroy(&V_upcall_thread_mtx);
+    MRW_WLOCK();
+    taskqueue_cancel(V_task_queue, &V_task, NULL);
 
     /* Destroy upcall ring */
     while ((bu = buf_ring_dequeue_mc(V_bw_upcalls_ring)) != NULL) {
@@ -1848,9 +1838,7 @@ expire_bw_meter_leq(void *arg)
 	}
 
 	/* Send all upcalls that are pending delivery */
-	mtx_lock(&V_upcall_thread_mtx);
-	cv_signal(&V_upcall_thread_cv);
-	mtx_unlock(&V_upcall_thread_mtx);
+	taskqueue_enqueue(V_task_queue, &V_task);
 
 	/* Reset counters */
 	x->bm_start_time = now;
@@ -2154,9 +2142,7 @@ bw_meter_prepare_upcall(struct bw_meter *x, struct timeval *nowp)
 	if (buf_ring_enqueue(V_bw_upcalls_ring, u))
 		log(LOG_WARNING, "bw_meter_prepare_upcall: cannot enqueue upcall\n");
 	if (buf_ring_count(V_bw_upcalls_ring) > (BW_UPCALLS_MAX / 2)) {
-		mtx_lock(&V_upcall_thread_mtx);
-		cv_signal(&V_upcall_thread_cv);
-		mtx_unlock(&V_upcall_thread_mtx);
+		taskqueue_enqueue(V_task_queue, &V_task);
 	}
 }
 /*
@@ -2753,6 +2739,11 @@ vnet_mroute_init(const void *unused __unused)
 
 	callout_init_rw(&V_expire_upcalls_ch, &mrouter_mtx, 0);
 	callout_init_rw(&V_bw_upcalls_ch, &mrouter_mtx, 0);
+
+	/* Prepare taskqueue */
+	V_task_queue = taskqueue_create_fast("ip_mroute_tskq", M_NOWAIT,
+		    taskqueue_thread_enqueue, &V_task_queue);
+	taskqueue_start_threads(&V_task_queue, 1, PI_NET, "ip_mroute_tskq task");
 }
 
 VNET_SYSINIT(vnet_mroute_init, SI_SUB_PROTO_MC, SI_ORDER_ANY, vnet_mroute_init,
@@ -2762,6 +2753,9 @@ static void
 vnet_mroute_uninit(const void *unused __unused)
 {
 
+	/* Taskqueue should be cancelled and drained before freeing */
+	taskqueue_free(V_task_queue);
+
 	free(V_viftable, M_MRTABLE);
 	free(V_nexpire, M_MRTABLE);
 	V_nexpire = NULL;