Module Name: src Committed By: knakahara Date: Thu Sep 19 06:09:52 UTC 2019
Modified Files: src/sys/net: if_l2tp.c Log Message: l2tp(4): avoid having struct ifqueue directly in a percpu storage. percpu(9) has a certain memory storage for each CPU and provides it by the piece to users. If the storages went short, percpu(9) enlarges them by allocating new larger memory areas, replacing old ones with them and destroying the old ones. A percpu storage referenced by a pointer gotten via percpu_getref can be destroyed by the mechanism after a running thread sleeps even if percpu_putref has not been called. Tx processing of l2tp(4) uses normally involves sleepable operations so we must avoid dereferencing a percpu data (struct ifqueue) after executing Tx processing. Address this situation by having just a pointer to the data in a percpu storage instead. Reviewed by ozaki-r@ and yamaguchi@ To generate a diff of this commit: cvs rdiff -u -r1.38 -r1.39 src/sys/net/if_l2tp.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/if_l2tp.c diff -u src/sys/net/if_l2tp.c:1.38 src/sys/net/if_l2tp.c:1.39 --- src/sys/net/if_l2tp.c:1.38 Thu Sep 19 06:07:24 2019 +++ src/sys/net/if_l2tp.c Thu Sep 19 06:09:52 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: if_l2tp.c,v 1.38 2019/09/19 06:07:24 knakahara Exp $ */ +/* $NetBSD: if_l2tp.c,v 1.39 2019/09/19 06:09:52 knakahara Exp $ */ /* * Copyright (c) 2017 Internet Initiative Japan Inc. @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_l2tp.c,v 1.38 2019/09/19 06:07:24 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_l2tp.c,v 1.39 2019/09/19 06:09:52 knakahara Exp $"); #ifdef _KERNEL_OPT #include "opt_inet.h" @@ -116,6 +116,7 @@ pserialize_t l2tp_psz __read_mostly; struct psref_class *lv_psref_class __read_mostly; static void l2tp_ifq_init_pc(void *, void *, struct cpu_info *); +static void l2tp_ifq_fini_pc(void *, void *, struct cpu_info *); static int l2tp_clone_create(struct if_clone *, int); static int l2tp_clone_destroy(struct ifnet *); @@ -151,6 +152,20 @@ static void l2tp_set_state(struct l2tp_s static int l2tp_encap_attach(struct l2tp_variant *); static int l2tp_encap_detach(struct l2tp_variant *); +static inline struct ifqueue * +l2tp_ifq_percpu_getref(percpu_t *pc) +{ + + return *(struct ifqueue **)percpu_getref(pc); +} + +static inline void +l2tp_ifq_percpu_putref(percpu_t *pc) +{ + + percpu_putref(pc); +} + #ifndef MAX_L2TP_NEST /* * This macro controls the upper limitation on nesting of l2tp tunnels. @@ -252,7 +267,7 @@ l2tp_clone_create(struct if_clone *ifc, sc->l2tp_ro_percpu = if_tunnel_alloc_ro_percpu(); - sc->l2tp_ifq_percpu = percpu_alloc(sizeof(struct ifqueue)); + sc->l2tp_ifq_percpu = percpu_alloc(sizeof(struct ifqueue *)); percpu_foreach(sc->l2tp_ifq_percpu, l2tp_ifq_init_pc, NULL); sc->l2tp_si = softint_establish(si_flags, l2tpintr_softint, sc); @@ -319,10 +334,18 @@ l2tpattach0(struct l2tp_softc *sc) void l2tp_ifq_init_pc(void *p, void *arg __unused, struct cpu_info *ci __unused) { - struct ifqueue *ifq = p; + struct ifqueue **ifqp = p; - memset(ifq, 0, sizeof(*ifq)); - ifq->ifq_maxlen = IFQ_MAXLEN; + *ifqp = kmem_zalloc(sizeof(**ifqp), KM_SLEEP); + (*ifqp)->ifq_maxlen = IFQ_MAXLEN; +} + +void +l2tp_ifq_fini_pc(void *p, void *arg __unused, struct cpu_info *ci __unused) +{ + struct ifqueue **ifqp = p; + + kmem_free(*ifqp, sizeof(**ifqp)); } static int @@ -344,7 +367,8 @@ l2tp_clone_destroy(struct ifnet *ifp) mutex_exit(&sc->l2tp_lock); softint_disestablish(sc->l2tp_si); - percpu_free(sc->l2tp_ifq_percpu, sizeof(struct ifqueue)); + percpu_foreach(sc->l2tp_ifq_percpu, l2tp_ifq_fini_pc, NULL); + percpu_free(sc->l2tp_ifq_percpu, sizeof(struct ifqueue *)); mutex_enter(&l2tp_softcs.lock); LIST_REMOVE(sc, l2tp_list); @@ -378,10 +402,10 @@ l2tp_tx_enqueue(struct l2tp_variant *var ifp = &sc->l2tp_ec.ec_if; s = splsoftnet(); - ifq = percpu_getref(sc->l2tp_ifq_percpu); + ifq = l2tp_ifq_percpu_getref(sc->l2tp_ifq_percpu); if (IF_QFULL(ifq)) { ifp->if_oerrors++; - percpu_putref(sc->l2tp_ifq_percpu); + l2tp_ifq_percpu_putref(sc->l2tp_ifq_percpu); splx(s); m_freem(m); return ENOBUFS; @@ -503,16 +527,16 @@ l2tpintr(struct l2tp_variant *var) /* output processing */ if (var->lv_my_sess_id == 0 || var->lv_peer_sess_id == 0) { - ifq = percpu_getref(sc->l2tp_ifq_percpu); + ifq = l2tp_ifq_percpu_getref(sc->l2tp_ifq_percpu); IF_PURGE(ifq); - percpu_putref(sc->l2tp_ifq_percpu); + l2tp_ifq_percpu_putref(sc->l2tp_ifq_percpu); if (cpuid == 0) IFQ_PURGE(&ifp->if_snd); return; } /* Currently, l2tpintr() is always called in softint context. */ - ifq = percpu_getref(sc->l2tp_ifq_percpu); + ifq = l2tp_ifq_percpu_getref(sc->l2tp_ifq_percpu); for (;;) { IF_DEQUEUE(ifq, m); if (m != NULL) @@ -520,7 +544,7 @@ l2tpintr(struct l2tp_variant *var) else break; } - percpu_putref(sc->l2tp_ifq_percpu); + l2tp_ifq_percpu_putref(sc->l2tp_ifq_percpu); if (cpuid == 0) { for (;;) {