commit cd2e06c91ebb84625b7deb4e393507ff6469df81 Author: Ryota Ozaki Date: Wed Jan 11 14:25:37 2017 +0900 Run icmp6_input in workqueue diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index ce7837a..e0f9a95 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -81,6 +81,8 @@ __KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.203 2016/12/12 03:55:57 ozaki-r Exp $"); #include #include #include +#include +#include #include #include @@ -169,10 +171,25 @@ static void icmp6_mtudisc_timeout(struct rtentry *, struct rttimer *); static void icmp6_redirect_timeout(struct rtentry *, struct rttimer *); static void sysctl_net_inet6_icmp6_setup(struct sysctllog **); +struct icmp6_work { + struct mbuf *iw_mbuf; + int iw_off; + int iw_proto; + TAILQ_ENTRY(icmp6_work) iw_next; +}; + +static void icmp6_input_work(struct work *, void *); +static struct workqueue *icmp6_input_wq; +static struct work icmp6_input_wk; +static struct pool icmp6_work_pool; +static unsigned int icmp6_work_enqueued = 0; +static TAILQ_HEAD(, icmp6_work) icmp6_work_list; +static kmutex_t icmp6_work_lock; void icmp6_init(void) { + int error; sysctl_net_inet6_icmp6_setup(NULL); mld_init(); @@ -180,6 +197,16 @@ icmp6_init(void) icmp6_redirect_timeout_q = rt_timer_queue_create(icmp6_redirtimeout); icmp6stat_percpu = percpu_alloc(sizeof(uint64_t) * ICMP6_NSTATS); + + TAILQ_INIT(&icmp6_work_list); + mutex_init(&icmp6_work_lock, MUTEX_DEFAULT, IPL_NONE); + pool_init(&icmp6_work_pool, sizeof(struct icmp6_work), 0, 0, 0, + "icmp6_work", NULL, IPL_SOFTNET); + /* TODO: support WQ_PERCPU */ + error = workqueue_create(&icmp6_input_wq, "icmp6_input", + icmp6_input_work, NULL, PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE); + if (error != 0) + panic("%s: workqueue_create failed (%d)\n", __func__, error); } static void @@ -444,8 +471,8 @@ icmp6_error(struct mbuf *m, int type, int code, int param) /* * Process a received ICMP6 message. */ -int -icmp6_input(struct mbuf **mp, int *offp, int proto) +static int +icmp6_input1(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp, *n; struct ip6_hdr *ip6, *nip6; @@ -901,6 +928,80 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) return IPPROTO_DONE; } +static struct icmp6_work * +icmp6_input_work_get(void) +{ + struct icmp6_work *work; + + mutex_enter(&icmp6_work_lock); + work = TAILQ_FIRST(&icmp6_work_list); + if (work != NULL) + TAILQ_REMOVE(&icmp6_work_list, work, iw_next); + mutex_exit(&icmp6_work_lock); + + return work; +} + +static void +icmp6_input_work_put(struct icmp6_work *work) +{ + + mutex_enter(&icmp6_work_lock); + TAILQ_INSERT_TAIL(&icmp6_work_list, work, iw_next); + mutex_exit(&icmp6_work_lock); +} + +static void +icmp6_input_work(struct work *wk, void *arg) +{ + struct icmp6_work *work; + int bound = curlwp_bind(); + + /* We can allow enqueuing another work at this point */ + atomic_swap_uint(&icmp6_work_enqueued, 0); + + while ((work = icmp6_input_work_get()) != NULL) { + int s; + + s = splsoftnet(); + mutex_enter(softnet_lock); + icmp6_input1(&work->iw_mbuf, &work->iw_off, work->iw_proto); + mutex_exit(softnet_lock); + splx(s); + + pool_put(&icmp6_work_pool, work); + } + + curlwp_bindx(bound); +} + +static void +icmp6_input_work_schedule(void) +{ + + /* Avoid enqueuing another work when one is already enqueued */ + if (atomic_swap_uint(&icmp6_work_enqueued, 1) == 1) + return; + + workqueue_enqueue(icmp6_input_wq, &icmp6_input_wk, NULL); +} + +int +icmp6_input(struct mbuf **mp, int *offp, int proto) +{ + struct icmp6_work *work; + + work = pool_get(&icmp6_work_pool, PR_NOWAIT); + work->iw_mbuf = *mp; + work->iw_off = *offp; + work->iw_proto = proto; + + icmp6_input_work_put(work); + icmp6_input_work_schedule(); + + return IPPROTO_DONE; +} + static int icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code) {