Module Name:    src
Committed By:   riastradh
Date:           Sat Aug 20 11:09:24 UTC 2022

Modified Files:
        src/sys/net: if.c if.h

Log Message:
ifnet(9): Defer if_watchdog (a.k.a. if_slowtimo) to workqueue.

This is necessary to make mii_down and the *_init/stop routines that
call it to sleep waiting for MII callouts on other CPUs.

Mark the workqueue and callout MP-safe; only take the kernel lock
around the callback.

No kernel bump despite change to struct ifnet because the change is
ABI-compatible and using the callout outside net/if.c has never been
kosher.


To generate a diff of this commit:
cvs rdiff -u -r1.512 -r1.513 src/sys/net/if.c
cvs rdiff -u -r1.299 -r1.300 src/sys/net/if.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if.c
diff -u src/sys/net/if.c:1.512 src/sys/net/if.c:1.513
--- src/sys/net/if.c:1.512	Wed Aug 17 19:56:28 2022
+++ src/sys/net/if.c	Sat Aug 20 11:09:24 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.c,v 1.512 2022/08/17 19:56:28 rillig Exp $	*/
+/*	$NetBSD: if.c,v 1.513 2022/08/20 11:09:24 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc.
@@ -90,7 +90,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.512 2022/08/17 19:56:28 rillig Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.513 2022/08/20 11:09:24 riastradh Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_inet.h"
@@ -197,6 +197,8 @@ static struct psref_class	*ifnet_psref_c
 static pserialize_t		ifnet_psz;
 static struct workqueue		*ifnet_link_state_wq __read_mostly;
 
+static struct workqueue		*if_slowtimo_wq __read_mostly;
+
 static kmutex_t			if_clone_mtx;
 
 struct ifnet *lo0ifp;
@@ -221,7 +223,8 @@ static int doifioctl(struct socket *, u_
 static void if_detach_queues(struct ifnet *, struct ifqueue *);
 static void sysctl_sndq_setup(struct sysctllog **, const char *,
     struct ifaltq *);
-static void if_slowtimo(void *);
+static void if_slowtimo_intr(void *);
+static void if_slowtimo_work(struct work *, void *);
 static void if_attachdomain1(struct ifnet *);
 static int ifconf(u_long, void *);
 static int if_transmit(struct ifnet *, struct mbuf *);
@@ -255,6 +258,15 @@ static void if_deferred_start_softint(vo
 static void if_deferred_start_common(struct ifnet *);
 static void if_deferred_start_destroy(struct ifnet *);
 
+struct if_slowtimo_data {
+	kmutex_t		isd_lock;
+	struct callout		isd_ch;
+	struct work		isd_work;
+	struct ifnet		*isd_ifp;
+	bool			isd_queued;
+	bool			isd_dying;
+};
+
 #if defined(INET) || defined(INET6)
 static void sysctl_net_pktq_setup(struct sysctllog **, int);
 #endif
@@ -333,6 +345,10 @@ ifinit1(void)
 	KASSERT(error == 0);
 	PSLIST_INIT(&ifnet_pslist);
 
+	error = workqueue_create(&if_slowtimo_wq, "ifwdog",
+	    if_slowtimo_work, NULL, PRI_SOFTNET, IPL_SOFTCLOCK, WQ_MPSAFE);
+	KASSERTMSG(error == 0, "error=%d", error);
+
 	if_indexlim = 8;
 
 	if_pfil = pfil_head_create(PFIL_TYPE_IFNET, NULL);
@@ -779,11 +795,17 @@ if_register(ifnet_t *ifp)
 	rt_ifannouncemsg(ifp, IFAN_ARRIVAL);
 
 	if (ifp->if_slowtimo != NULL) {
-		ifp->if_slowtimo_ch =
-		    kmem_zalloc(sizeof(*ifp->if_slowtimo_ch), KM_SLEEP);
-		callout_init(ifp->if_slowtimo_ch, 0);
-		callout_setfunc(ifp->if_slowtimo_ch, if_slowtimo, ifp);
-		if_slowtimo(ifp);
+		struct if_slowtimo_data *isd;
+
+		isd = kmem_zalloc(sizeof(*isd), KM_SLEEP);
+		mutex_init(&isd->isd_lock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
+		callout_init(&isd->isd_ch, CALLOUT_MPSAFE);
+		callout_setfunc(&isd->isd_ch, if_slowtimo_intr, ifp);
+		isd->isd_ifp = ifp;
+
+		ifp->if_slowtimo_data = isd;
+
+		if_slowtimo_intr(ifp);
 	}
 
 	if (ifp->if_transmit == NULL || ifp->if_transmit == if_nulltransmit)
@@ -1350,11 +1372,20 @@ if_detach(struct ifnet *ifp)
 	pserialize_perform(ifnet_psz);
 	IFNET_GLOBAL_UNLOCK();
 
-	if (ifp->if_slowtimo != NULL && ifp->if_slowtimo_ch != NULL) {
-		ifp->if_slowtimo = NULL;
-		callout_halt(ifp->if_slowtimo_ch, NULL);
-		callout_destroy(ifp->if_slowtimo_ch);
-		kmem_free(ifp->if_slowtimo_ch, sizeof(*ifp->if_slowtimo_ch));
+	if (ifp->if_slowtimo != NULL) {
+		struct if_slowtimo_data *isd = ifp->if_slowtimo_data;
+
+		mutex_enter(&isd->isd_lock);
+		isd->isd_dying = true;
+		mutex_exit(&isd->isd_lock);
+		callout_halt(&isd->isd_ch, NULL);
+		workqueue_wait(if_slowtimo_wq, &isd->isd_work);
+		callout_destroy(&isd->isd_ch);
+		mutex_destroy(&isd->isd_lock);
+		kmem_free(isd, sizeof(*isd));
+
+		ifp->if_slowtimo_data = NULL; /* paraonia */
+		ifp->if_slowtimo = NULL;      /* paranoia */
 	}
 	if_deferred_start_destroy(ifp);
 
@@ -2651,24 +2682,42 @@ if_up_locked(struct ifnet *ifp)
  * call the appropriate interface routine on expiration.
  */
 static void
-if_slowtimo(void *arg)
+if_slowtimo_intr(void *arg)
 {
-	void (*slowtimo)(struct ifnet *);
 	struct ifnet *ifp = arg;
-	int s;
+	struct if_slowtimo_data *isd = ifp->if_slowtimo_data;
 
-	slowtimo = ifp->if_slowtimo;
-	if (__predict_false(slowtimo == NULL))
-		return;
+	mutex_enter(&isd->isd_lock);
+	if (!isd->isd_dying) {
+		if (ifp->if_timer != 0 &&
+		    --ifp->if_timer == 0 &&
+		    !isd->isd_queued) {
+			isd->isd_queued = true;
+			workqueue_enqueue(if_slowtimo_wq, &isd->isd_work,
+			    NULL);
+		} else {
+			callout_schedule(&isd->isd_ch, hz / IFNET_SLOWHZ);
+		}
+	}
+	mutex_exit(&isd->isd_lock);
+}
 
-	s = splnet();
-	if (ifp->if_timer != 0 && --ifp->if_timer == 0)
-		(*slowtimo)(ifp);
+static void
+if_slowtimo_work(struct work *work, void *arg)
+{
+	struct if_slowtimo_data *isd =
+	    container_of(work, struct if_slowtimo_data, isd_work);
+	struct ifnet *ifp = isd->isd_ifp;
 
-	splx(s);
+	KERNEL_LOCK(1, NULL);
+	(*ifp->if_slowtimo)(ifp);
+	KERNEL_UNLOCK_ONE(NULL);
 
-	if (__predict_true(ifp->if_slowtimo != NULL))
-		callout_schedule(ifp->if_slowtimo_ch, hz / IFNET_SLOWHZ);
+	mutex_enter(&isd->isd_lock);
+	isd->isd_queued = false;
+	if (!isd->isd_dying)
+		callout_schedule(&isd->isd_ch, hz / IFNET_SLOWHZ);
+	mutex_exit(&isd->isd_lock);
 }
 
 /*

Index: src/sys/net/if.h
diff -u src/sys/net/if.h:1.299 src/sys/net/if.h:1.300
--- src/sys/net/if.h:1.299	Thu Jul 28 15:15:29 2022
+++ src/sys/net/if.h	Sat Aug 20 11:09:24 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.h,v 1.299 2022/07/28 15:15:29 skrll Exp $	*/
+/*	$NetBSD: if.h,v 1.300 2022/08/20 11:09:24 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@@ -414,7 +414,7 @@ typedef struct ifnet {
 	kmutex_t	*if_ioctl_lock;	/* :: */
 	char		*if_description;	/* i: interface description */
 #ifdef _KERNEL /* XXX kvm(3) */
-	struct callout	*if_slowtimo_ch;/* :: */
+	struct if_slowtimo_data *if_slowtimo_data; /* :: */
 	struct krwlock	*if_afdata_lock;/* :: */
 	struct if_percpuq
 			*if_percpuq;	/* :: we should remove it in the future */

Reply via email to