Module Name: src
Committed By: knakahara
Date: Fri Dec 11 07:59:14 UTC 2015
Modified Files:
src/sys/net: if_gif.c if_gif.h
src/sys/netinet: in_gif.c
src/sys/netinet6: in6_gif.c
Log Message:
PR kern/50522: gif(4) ioctl causes panic while someone is using the gif(4)
interface.
It is required to wait other CPU's softint completion before disestablishing
the softint handler.
To generate a diff of this commit:
cvs rdiff -u -r1.101 -r1.102 src/sys/net/if_gif.c
cvs rdiff -u -r1.19 -r1.20 src/sys/net/if_gif.h
cvs rdiff -u -r1.65 -r1.66 src/sys/netinet/in_gif.c
cvs rdiff -u -r1.62 -r1.63 src/sys/netinet6/in6_gif.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_gif.c
diff -u src/sys/net/if_gif.c:1.101 src/sys/net/if_gif.c:1.102
--- src/sys/net/if_gif.c:1.101 Fri Dec 11 04:29:24 2015
+++ src/sys/net/if_gif.c Fri Dec 11 07:59:14 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: if_gif.c,v 1.101 2015/12/11 04:29:24 knakahara Exp $ */
+/* $NetBSD: if_gif.c,v 1.102 2015/12/11 07:59:14 knakahara Exp $ */
/* $KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.101 2015/12/11 04:29:24 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.102 2015/12/11 07:59:14 knakahara Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -53,6 +53,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1
#include <sys/cpu.h>
#include <sys/intr.h>
#include <sys/kmem.h>
+#include <sys/atomic.h>
#include <net/if.h>
#include <net/if_types.h>
@@ -146,6 +147,10 @@ void
gifattach0(struct gif_softc *sc)
{
+ sc->gif_si_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
+ KASSERT(sc->gif_si_lock != NULL);
+ cv_init(&sc->gif_si_cv, "if_gif_cv");
+ sc->gif_si_refs = 0;
sc->encap_cookie4 = sc->encap_cookie6 = NULL;
sc->gif_if.if_addrlen = 0;
@@ -174,6 +179,8 @@ gif_clone_destroy(struct ifnet *ifp)
if_detach(ifp);
rtcache_free(&sc->gif_ro);
+ cv_destroy(&sc->gif_si_cv);
+ mutex_obj_free(sc->gif_si_lock);
kmem_free(sc, sizeof(struct gif_softc));
return (0);
@@ -295,7 +302,8 @@ gif_output(struct ifnet *ifp, struct mbu
m->m_flags &= ~(M_BCAST|M_MCAST);
if (!(ifp->if_flags & IFF_UP) ||
- sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+ sc->gif_psrc == NULL || sc->gif_pdst == NULL ||
+ sc->gif_si == NULL) {
m_freem(m);
error = ENETDOWN;
goto end;
@@ -321,9 +329,11 @@ gif_output(struct ifnet *ifp, struct mbu
splx(s);
goto end;
}
- splx(s);
+ /* softint_schedule() must be called with kpreempt_disabled() */
softint_schedule(sc->gif_si);
+ splx(s);
+
error = 0;
end:
@@ -346,6 +356,24 @@ gifintr(void *arg)
sc = arg;
ifp = &sc->gif_if;
+ atomic_inc_uint(&sc->gif_si_refs);
+
+ /*
+ * pattern (a) (see also gif_set_tunnel())
+ * other CPUs does {set,delete}_tunnel after curcpu have done
+ * softint_schedule().
+ */
+ if (sc->gif_pdst == NULL || sc->gif_psrc == NULL) {
+ IFQ_PURGE(&ifp->if_snd);
+
+ if (atomic_dec_uint_nv(&sc->gif_si_refs) == 0) {
+ mutex_enter(sc->gif_si_lock);
+ cv_broadcast(&sc->gif_si_cv);
+ mutex_exit(sc->gif_si_lock);
+ }
+ return;
+ }
+
/* output processing */
while (1) {
s = splnet();
@@ -397,6 +425,16 @@ gifintr(void *arg)
ifp->if_obytes += len;
}
}
+
+ /*
+ * pattern (b) (see also gif_set_tunnel())
+ * other CPUs begin {set,delete}_tunnel while curcpu si doing gifintr.
+ */
+ if (atomic_dec_uint_nv(&sc->gif_si_refs) == 0) {
+ mutex_enter(sc->gif_si_lock);
+ cv_broadcast(&sc->gif_si_cv);
+ mutex_exit(sc->gif_si_lock);
+ }
}
void
@@ -744,6 +782,7 @@ gif_set_tunnel(struct ifnet *ifp, struct
struct gif_softc *sc2;
struct sockaddr *osrc, *odst;
struct sockaddr *nsrc, *ndst;
+ void *osi;
int s;
int error;
@@ -777,8 +816,38 @@ gif_set_tunnel(struct ifnet *ifp, struct
/* Firstly, clear old configurations. */
if (sc->gif_si) {
- softint_disestablish(sc->gif_si);
+ osrc = sc->gif_psrc;
+ odst = sc->gif_pdst;
+ osi = sc->gif_si;
+ sc->gif_psrc = NULL;
+ sc->gif_pdst = NULL;
sc->gif_si = NULL;
+
+ /*
+ * At this point, gif_output() does not softint_schedule()
+ * any more. However, there are below 2 fears of other CPUs.
+ * (a) gif_output() has done softint_schedule(),and softint
+ * (gifintr()) is waiting for execution
+ * (b) gifintr() is already running
+ * see also gifintr()
+ */
+
+ /*
+ * To avoid the above fears, wait for gifintr() completion of
+ * all CPUs here.
+ */
+ mutex_enter(sc->gif_si_lock);
+ while (sc->gif_si_refs > 0) {
+ aprint_debug("%s: cv_wait on gif_softc\n", __func__);
+ cv_wait(&sc->gif_si_cv, sc->gif_si_lock);
+ }
+ mutex_exit(sc->gif_si_lock);
+
+ softint_disestablish(osi);
+ sc->gif_psrc = osrc;
+ sc->gif_pdst = odst;
+ osrc = NULL;
+ odst = NULL;
}
/* XXX we can detach from both, but be polite just in case */
if (sc->gif_psrc)
@@ -845,13 +914,31 @@ void
gif_delete_tunnel(struct ifnet *ifp)
{
struct gif_softc *sc = ifp->if_softc;
+ struct sockaddr *osrc, *odst;
+ void *osi;
int s;
s = splsoftnet();
if (sc->gif_si) {
- softint_disestablish(sc->gif_si);
+ osrc = sc->gif_psrc;
+ odst = sc->gif_pdst;
+ osi = sc->gif_si;
+
+ sc->gif_psrc = NULL;
+ sc->gif_pdst = NULL;
sc->gif_si = NULL;
+
+ mutex_enter(sc->gif_si_lock);
+ while (sc->gif_si_refs > 0) {
+ aprint_debug("%s: cv_wait on gif_softc\n", __func__);
+ cv_wait(&sc->gif_si_cv, sc->gif_si_lock);
+ }
+ mutex_exit(sc->gif_si_lock);
+
+ softint_disestablish(osi);
+ sc->gif_psrc = osrc;
+ sc->gif_pdst = odst;
}
if (sc->gif_psrc) {
sockaddr_free(sc->gif_psrc);
Index: src/sys/net/if_gif.h
diff -u src/sys/net/if_gif.h:1.19 src/sys/net/if_gif.h:1.20
--- src/sys/net/if_gif.h:1.19 Wed Nov 12 12:36:28 2008
+++ src/sys/net/if_gif.h Fri Dec 11 07:59:14 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: if_gif.h,v 1.19 2008/11/12 12:36:28 ad Exp $ */
+/* $NetBSD: if_gif.h,v 1.20 2015/12/11 07:59:14 knakahara Exp $ */
/* $KAME: if_gif.h,v 1.23 2001/07/27 09:21:42 itojun Exp $ */
/*
@@ -38,6 +38,8 @@
#define _NET_IF_GIF_H_
#include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -60,11 +62,21 @@ struct gif_softc {
const struct encaptab *encap_cookie6;
LIST_ENTRY(gif_softc) gif_list; /* list of all gifs */
void *gif_si; /* softintr handle */
+
+ struct si_sync { /* can access without gif_lock */
+ unsigned int si_refs; /* reference count for gif_si */
+ kcondvar_t si_cv; /* wait for softint completion */
+ kmutex_t *si_lock; /* lock for gif_si_sync */
+ } gif_si_sync;
};
#define GIF_ROUTE_TTL 10
#define gif_ro gifsc_gifscr.gifscr_ro
+#define gif_si_refs gif_si_sync.si_refs
+#define gif_si_cv gif_si_sync.si_cv
+#define gif_si_lock gif_si_sync.si_lock
+
#define GIF_MTU (1280) /* Default MTU */
#define GIF_MTU_MIN (1280) /* Minimum MTU */
#define GIF_MTU_MAX (8192) /* Maximum MTU */
@@ -81,4 +93,8 @@ void gif_delete_tunnel(struct ifnet *);
int gif_encapcheck(struct mbuf *, int, int, void *);
#endif
+/*
+ * Locking notes:
+ * - All members of struct si_sync are protected by si_lock (an adaptive mutex)
+ */
#endif /* !_NET_IF_GIF_H_ */
Index: src/sys/netinet/in_gif.c
diff -u src/sys/netinet/in_gif.c:1.65 src/sys/netinet/in_gif.c:1.66
--- src/sys/netinet/in_gif.c:1.65 Mon Aug 24 22:21:26 2015
+++ src/sys/netinet/in_gif.c Fri Dec 11 07:59:14 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: in_gif.c,v 1.65 2015/08/24 22:21:26 pooka Exp $ */
+/* $NetBSD: in_gif.c,v 1.66 2015/12/11 07:59:14 knakahara Exp $ */
/* $KAME: in_gif.c,v 1.66 2001/07/29 04:46:09 itojun Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.65 2015/08/24 22:21:26 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.66 2015/12/11 07:59:14 knakahara Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -223,13 +223,20 @@ in_gif_input(struct mbuf *m, ...)
return;
}
#ifndef GIF_ENCAPCHECK
- if (!gif_validate4(ip, gifp->if_softc, m->m_pkthdr.rcvif)) {
+ struct gif_softc *sc = (struct gif_softc *)gifp->if_softc;
+ /* other CPU do delete_tunnel */
+ if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
m_freem(m);
ip_statinc(IP_STAT_NOGIF);
return;
}
-#endif
+ if (!gif_validate4(ip, sc, m->m_pkthdr.rcvif)) {
+ m_freem(m);
+ ip_statinc(IP_STAT_NOGIF);
+ return;
+ }
+#endif
otos = ip->ip_tos;
m_adj(m, off);
Index: src/sys/netinet6/in6_gif.c
diff -u src/sys/netinet6/in6_gif.c:1.62 src/sys/netinet6/in6_gif.c:1.63
--- src/sys/netinet6/in6_gif.c:1.62 Mon Aug 24 22:21:27 2015
+++ src/sys/netinet6/in6_gif.c Fri Dec 11 07:59:14 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: in6_gif.c,v 1.62 2015/08/24 22:21:27 pooka Exp $ */
+/* $NetBSD: in6_gif.c,v 1.63 2015/12/11 07:59:14 knakahara Exp $ */
/* $KAME: in6_gif.c,v 1.62 2001/07/29 04:27:25 itojun Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.62 2015/08/24 22:21:27 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.63 2015/12/11 07:59:14 knakahara Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -224,7 +224,15 @@ in6_gif_input(struct mbuf **mp, int *off
return IPPROTO_DONE;
}
#ifndef GIF_ENCAPCHECK
- if (!gif_validate6(ip6, gifp->if_softc, m->m_pkthdr.rcvif)) {
+ struct gif_softc *sc = (struct gif_softc *)gifp->if_softc;
+ /* other CPU do delete_tunnel */
+ if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+ m_freem(m);
+ IP6_STATINC(IP6_STAT_NOGIF);
+ return IPPROTO_DONE;
+ }
+
+ if (!gif_validate6(ip6, sc, m->m_pkthdr.rcvif)) {
m_freem(m);
IP6_STATINC(IP6_STAT_NOGIF);
return IPPROTO_DONE;