Module Name:    src
Committed By:   ozaki-r
Date:           Mon Jul 11 07:37:00 UTC 2016

Modified Files:
        src/sys/net: route.c
        src/sys/netinet: ip_flow.c
        src/sys/netinet6: ip6_flow.c nd6.c

Log Message:
Run timers in workqueue

Timers (such as nd6_timer) typically free/destroy some data in callout
(softint). If we apply psz/psref for such data, we cannot do free/destroy
process in there because synchronization of psz/psref cannot be used in
softint. So run timer callbacks in workqueue works (normal LWP context).

Doing workqueue_enqueue a work twice (i.e., call workqueue_enqueue before
a previous task is scheduled) isn't allowed. For nd6_timer and
rt_timer_timer, this doesn't happen because callout_reset is called only
from workqueue's work. OTOH, ip{,6}flow_slowtimo's callout can be called
before its work starts and completes because the callout is periodically
called regardless of completion of the work. To avoid such a situation,
add a flag for each protocol; the flag is set true when a work is
enqueued and set false after the work finished. workqueue_enqueue is
called only if the flag is false.

Proposed on tech-net and tech-kern.


To generate a diff of this commit:
cvs rdiff -u -r1.169 -r1.170 src/sys/net/route.c
cvs rdiff -u -r1.72 -r1.73 src/sys/netinet/ip_flow.c
cvs rdiff -u -r1.27 -r1.28 src/sys/netinet6/ip6_flow.c
cvs rdiff -u -r1.202 -r1.203 src/sys/netinet6/nd6.c

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/route.c
diff -u src/sys/net/route.c:1.169 src/sys/net/route.c:1.170
--- src/sys/net/route.c:1.169	Thu Jul  7 06:55:43 2016
+++ src/sys/net/route.c	Mon Jul 11 07:37:00 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: route.c,v 1.169 2016/07/07 06:55:43 msaitoh Exp $	*/
+/*	$NetBSD: route.c,v 1.170 2016/07/11 07:37:00 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2008 The NetBSD Foundation, Inc.
@@ -96,7 +96,7 @@
 #endif
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.169 2016/07/07 06:55:43 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.170 2016/07/11 07:37:00 ozaki-r Exp $");
 
 #include <sys/param.h>
 #ifdef RTFLUSH_DEBUG
@@ -114,6 +114,7 @@ __KERNEL_RCSID(0, "$NetBSD: route.c,v 1.
 #include <sys/ioctl.h>
 #include <sys/pool.h>
 #include <sys/kauth.h>
+#include <sys/workqueue.h>
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -136,6 +137,8 @@ static struct pool	rtentry_pool;
 static struct pool	rttimer_pool;
 
 static struct callout	rt_timer_ch; /* callout for rt_timer_timer() */
+struct workqueue	*rt_timer_wq;
+struct work		rt_timer_wk;
 
 #ifdef RTFLUSH_DEBUG
 static int _rtcache_debug = 0;
@@ -1147,14 +1150,22 @@ static int rt_init_done = 0;
  * that this is run when the first queue is added...
  */
 
+static void rt_timer_work(struct work *, void *);
+
 void
 rt_timer_init(void)
 {
+	int error;
+
 	assert(rt_init_done == 0);
 
 	LIST_INIT(&rttimer_queue_head);
 	callout_init(&rt_timer_ch, 0);
 	callout_reset(&rt_timer_ch, hz, rt_timer_timer, NULL);
+	error = workqueue_create(&rt_timer_wq, "rt_timer",
+	    rt_timer_work, NULL, PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE);
+	if (error)
+		panic("%s: workqueue_create failed (%d)\n", __func__, error);
 	rt_init_done = 1;
 }
 
@@ -1287,9 +1298,8 @@ rt_timer_add(struct rtentry *rt,
 	return 0;
 }
 
-/* ARGSUSED */
-void
-rt_timer_timer(void *arg)
+static void
+rt_timer_work(struct work *wk, void *arg)
 {
 	struct rttimer_queue *rtq;
 	struct rttimer *r;
@@ -1315,6 +1325,13 @@ rt_timer_timer(void *arg)
 	callout_reset(&rt_timer_ch, hz, rt_timer_timer, NULL);
 }
 
+void
+rt_timer_timer(void *arg)
+{
+
+	workqueue_enqueue(rt_timer_wq, &rt_timer_wk, NULL);
+}
+
 static struct rtentry *
 _rtcache_init(struct route *ro, int flag)
 {

Index: src/sys/netinet/ip_flow.c
diff -u src/sys/netinet/ip_flow.c:1.72 src/sys/netinet/ip_flow.c:1.73
--- src/sys/netinet/ip_flow.c:1.72	Mon Jun 20 06:46:38 2016
+++ src/sys/netinet/ip_flow.c	Mon Jul 11 07:37:00 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_flow.c,v 1.72 2016/06/20 06:46:38 knakahara Exp $	*/
+/*	$NetBSD: ip_flow.c,v 1.73 2016/07/11 07:37:00 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip_flow.c,v 1.72 2016/06/20 06:46:38 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_flow.c,v 1.73 2016/07/11 07:37:00 ozaki-r Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -45,6 +45,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip_flow.c,v 
 #include <sys/kernel.h>
 #include <sys/pool.h>
 #include <sys/sysctl.h>
+#include <sys/workqueue.h>
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -105,6 +106,10 @@ static int ip_hashsize = IPFLOW_DEFAULT_
 static struct ipflow *ipflow_reap(bool);
 static void ipflow_sysctl_init(struct sysctllog **);
 
+static void ipflow_slowtimo_work(struct work *, void *);
+static struct workqueue	*ipflow_slowtimo_wq;
+static struct work	ipflow_slowtimo_wk;
+
 static size_t 
 ipflow_hash(const struct ip *ip)
 {
@@ -176,6 +181,12 @@ ipflow_reinit(int table_size)
 void
 ipflow_init(void)
 {
+	int error;
+
+	error = workqueue_create(&ipflow_slowtimo_wq, "ipflow_slowtimo",
+	    ipflow_slowtimo_work, NULL, PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE);
+	if (error != 0)
+		panic("%s: workqueue_create failed (%d)\n", __func__, error);
 
 	mutex_init(&ipflow_lock, MUTEX_DEFAULT, IPL_NONE);
 
@@ -419,8 +430,10 @@ ipflow_reap(bool just_one)
 	return NULL;
 }
 
-void
-ipflow_slowtimo(void)
+static bool ipflow_work_enqueued = false;
+
+static void
+ipflow_slowtimo_work(struct work *wk, void *arg)
 {
 	struct rtentry *rt;
 	struct ipflow *ipf, *next_ipf;
@@ -445,12 +458,29 @@ ipflow_slowtimo(void)
 			ipf->ipf_uses = 0;
 		}
 	}
+	ipflow_work_enqueued = false;
 	KERNEL_UNLOCK_ONE(NULL);
 	mutex_exit(&ipflow_lock);
 	mutex_exit(softnet_lock);
 }
 
 void
+ipflow_slowtimo(void)
+{
+
+	/* Avoid enqueuing another work when one is already enqueued */
+	mutex_enter(&ipflow_lock);
+	if (ipflow_work_enqueued) {
+		mutex_exit(&ipflow_lock);
+		return;
+	}
+	ipflow_work_enqueued = true;
+	mutex_exit(&ipflow_lock);
+
+	workqueue_enqueue(ipflow_slowtimo_wq, &ipflow_slowtimo_wk, NULL);
+}
+
+void
 ipflow_create(const struct route *ro, struct mbuf *m)
 {
 	const struct ip *const ip = mtod(m, const struct ip *);

Index: src/sys/netinet6/ip6_flow.c
diff -u src/sys/netinet6/ip6_flow.c:1.27 src/sys/netinet6/ip6_flow.c:1.28
--- src/sys/netinet6/ip6_flow.c:1.27	Mon Jun 20 06:46:38 2016
+++ src/sys/netinet6/ip6_flow.c	Mon Jul 11 07:37:00 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip6_flow.c,v 1.27 2016/06/20 06:46:38 knakahara Exp $	*/
+/*	$NetBSD: ip6_flow.c,v 1.28 2016/07/11 07:37:00 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 2007 The NetBSD Foundation, Inc.
@@ -38,7 +38,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip6_flow.c,v 1.27 2016/06/20 06:46:38 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_flow.c,v 1.28 2016/07/11 07:37:00 ozaki-r Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -52,6 +52,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip6_flow.c,v
 #include <sys/kernel.h>
 #include <sys/pool.h>
 #include <sys/sysctl.h>
+#include <sys/workqueue.h>
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -100,6 +101,10 @@ static struct ip6flowhead *ip6flowtable 
 static struct ip6flowhead ip6flowlist;
 static int ip6flow_inuse;
 
+static void ip6flow_slowtimo_work(struct work *, void *);
+static struct workqueue	*ip6flow_slowtimo_wq;
+static struct work	ip6flow_slowtimo_wk;
+
 /*
  * Insert an ip6flow into the list.
  */
@@ -217,7 +222,12 @@ ip6flow_init_locked(int table_size)
 int
 ip6flow_init(int table_size)
 {
-	int ret;
+	int ret, error;
+
+	error = workqueue_create(&ip6flow_slowtimo_wq, "ip6flow_slowtimo",
+	    ip6flow_slowtimo_work, NULL, PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE);
+	if (error != 0)
+		panic("%s: workqueue_create failed (%d)\n", __func__, error);
 
 	mutex_init(&ip6flow_lock, MUTEX_DEFAULT, IPL_NONE);
 
@@ -456,8 +466,10 @@ ip6flow_reap(int just_one)
 	return ip6f;
 }
 
+static bool ip6flow_work_enqueued = false;
+
 void
-ip6flow_slowtimo(void)
+ip6flow_slowtimo_work(struct work *wk, void *arg)
 {
 	struct ip6flow *ip6f, *next_ip6f;
 
@@ -478,12 +490,29 @@ ip6flow_slowtimo(void)
 			ip6f->ip6f_forwarded = 0;
 		}
 	}
+	ip6flow_work_enqueued = false;
 
 	KERNEL_UNLOCK_ONE(NULL);
 	mutex_exit(&ip6flow_lock);
 	mutex_exit(softnet_lock);
 }
 
+void
+ip6flow_slowtimo(void)
+{
+
+	/* Avoid enqueuing another work when one is already enqueued */
+	mutex_enter(&ip6flow_lock);
+	if (ip6flow_work_enqueued) {
+		mutex_exit(&ip6flow_lock);
+		return;
+	}
+	ip6flow_work_enqueued = true;
+	mutex_exit(&ip6flow_lock);
+
+	workqueue_enqueue(ip6flow_slowtimo_wq, &ip6flow_slowtimo_wk, NULL);
+}
+
 /*
  * We have successfully forwarded a packet using the normal
  * IPv6 stack. Now create/update a flow.

Index: src/sys/netinet6/nd6.c
diff -u src/sys/netinet6/nd6.c:1.202 src/sys/netinet6/nd6.c:1.203
--- src/sys/netinet6/nd6.c:1.202	Thu Jul  7 09:32:03 2016
+++ src/sys/netinet6/nd6.c	Mon Jul 11 07:37:00 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6.c,v 1.202 2016/07/07 09:32:03 ozaki-r Exp $	*/
+/*	$NetBSD: nd6.c,v 1.203 2016/07/11 07:37:00 ozaki-r Exp $	*/
 /*	$KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.202 2016/07/07 09:32:03 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.203 2016/07/11 07:37:00 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -56,6 +56,7 @@ __KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.20
 #include <sys/syslog.h>
 #include <sys/queue.h>
 #include <sys/cprng.h>
+#include <sys/workqueue.h>
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -113,10 +114,13 @@ static int regen_tmpaddr(const struct in
 static void nd6_free(struct llentry *, int);
 static void nd6_llinfo_timer(void *);
 static void nd6_timer(void *);
+static void nd6_timer_work(struct work *, void *);
 static void clear_llinfo_pqueue(struct llentry *);
 
 static callout_t nd6_slowtimo_ch;
 static callout_t nd6_timer_ch;
+static struct workqueue	*nd6_timer_wq;
+static struct work	nd6_timer_wk;
 
 static int fill_drlist(void *, size_t *, size_t);
 static int fill_prlist(void *, size_t *, size_t);
@@ -126,6 +130,7 @@ MALLOC_DEFINE(M_IP6NDP, "NDP", "IPv6 Nei
 void
 nd6_init(void)
 {
+	int error;
 
 	/* initialization of the default router list */
 	TAILQ_INIT(&nd_defrouter);
@@ -133,6 +138,11 @@ nd6_init(void)
 	callout_init(&nd6_slowtimo_ch, CALLOUT_MPSAFE);
 	callout_init(&nd6_timer_ch, CALLOUT_MPSAFE);
 
+	error = workqueue_create(&nd6_timer_wq, "nd6_timer",
+	    nd6_timer_work, NULL, PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE);
+	if (error)
+		panic("%s: workqueue_create failed (%d)\n", __func__, error);
+
 	/* start timer */
 	callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz,
 	    nd6_slowtimo, NULL);
@@ -556,7 +566,7 @@ out:
  * ND6 timer routine to expire default route list and prefix list
  */
 static void
-nd6_timer(void *ignored_arg)
+nd6_timer_work(struct work *wk, void *arg)
 {
 	struct nd_defrouter *next_dr, *dr;
 	struct nd_prefix *next_pr, *pr;
@@ -678,6 +688,13 @@ nd6_timer(void *ignored_arg)
 	mutex_exit(softnet_lock);
 }
 
+static void
+nd6_timer(void *ignored_arg)
+{
+
+	workqueue_enqueue(nd6_timer_wq, &nd6_timer_wk, NULL);
+}
+
 /* ia6: deprecated/invalidated temporary address */
 static int
 regen_tmpaddr(const struct in6_ifaddr *ia6)

Reply via email to