svn commit: r186070 - projects/arpv2_merge_1/sys/netinet6
Kip Macy
kmacy at FreeBSD.org
Sat Dec 13 17:12:55 PST 2008
Author: kmacy
Date: Sun Dec 14 01:12:55 2008
New Revision: 186070
URL: http://svn.freebsd.org/changeset/base/186070
Log:
Avoid a lock recursion in the call to if_output from nd6_output_lle by
creating a chain of packets to be sent after the nd6_output_lle caller
drops the lle lock.
Modified:
projects/arpv2_merge_1/sys/netinet6/nd6.c
projects/arpv2_merge_1/sys/netinet6/nd6.h
projects/arpv2_merge_1/sys/netinet6/nd6_nbr.c
Modified: projects/arpv2_merge_1/sys/netinet6/nd6.c
==============================================================================
--- projects/arpv2_merge_1/sys/netinet6/nd6.c Sun Dec 14 00:40:14 2008 (r186069)
+++ projects/arpv2_merge_1/sys/netinet6/nd6.c Sun Dec 14 01:12:55 2008 (r186070)
@@ -1404,6 +1404,8 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
int llchange;
int flags = 0;
int newstate = 0;
+ struct sockaddr_in6 sin6;
+ struct mbuf *chain = NULL;
IF_AFDATA_UNLOCK_ASSERT(ifp);
@@ -1519,8 +1521,10 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
* just set the 2nd argument as the
* 1st one.
*/
- nd6_output_lle(ifp, ifp, m_hold, L3_ADDR_SIN6(ln), NULL, ln);
+ nd6_output_lle(ifp, ifp, m_hold, L3_ADDR_SIN6(ln), NULL, ln, &chain);
}
+ if (chain)
+ memcpy(&sin6, L3_ADDR_SIN6(ln), sizeof(sin6));
}
} else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/* probe right away */
@@ -1593,6 +1597,17 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
break;
}
+ if (ln) {
+ if (flags & ND6_EXCLUSIVE)
+ LLE_WUNLOCK(ln);
+ else
+ LLE_RUNLOCK(ln);
+ if (ln->la_flags & LLE_STATIC)
+ ln = NULL;
+ }
+ if (chain)
+ nd6_output_flush(ifp, ifp, chain, &sin6, NULL);
+
/*
* When the link-layer address of a router changes, select the
* best router again. In particular, when the neighbor entry is newly
@@ -1609,18 +1624,13 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
* cases for safety.
*/
if (do_update && ln->ln_router && !V_ip6_forwarding && V_ip6_accept_rtadv) {
-#ifdef notyet
- /*
- * XXX implement the boiler plate
- */
- taskqueue_enqueue(ipv6_taskq, defrouter_select_task);
-#endif
/*
* guaranteed recursion
*/
defrouter_select();
}
+ return (ln);
done:
if (ln) {
if (flags & ND6_EXCLUSIVE)
@@ -1669,7 +1679,7 @@ nd6_output(struct ifnet *ifp, struct ifn
struct sockaddr_in6 *dst, struct rtentry *rt0)
{
- return (nd6_output_lle(ifp, origifp, m0, dst, rt0, NULL));
+ return (nd6_output_lle(ifp, origifp, m0, dst, rt0, NULL, NULL));
}
@@ -1685,7 +1695,8 @@ nd6_output(struct ifnet *ifp, struct ifn
int
nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0,
- struct sockaddr_in6 *dst, struct rtentry *rt0, struct llentry *lle)
+ struct sockaddr_in6 *dst, struct rtentry *rt0, struct llentry *lle,
+ struct mbuf **tail)
{
INIT_VNET_INET6(curvnet);
struct mbuf *m = m0;
@@ -1695,8 +1706,12 @@ nd6_output_lle(struct ifnet *ifp, struct
int flags = 0;
#ifdef INVARIANTS
- if (lle)
+ if (lle) {
+
LLE_WLOCK_ASSERT(lle);
+
+ KASSERT(tail != NULL, (" lle locked but no tail pointer passed"));
+ }
#endif
if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
goto sendpkt;
@@ -1715,7 +1730,7 @@ nd6_output_lle(struct ifnet *ifp, struct
* or an anycast address(i.e. not a multicast).
*/
- flags = (m || lle) ? LLE_EXCLUSIVE : 0;
+ flags = ((m != NULL) || (lle != NULL)) ? LLE_EXCLUSIVE : 0;
if (ln == NULL) {
retry:
IF_AFDATA_LOCK(rt->rt_ifp);
@@ -1863,6 +1878,13 @@ nd6_output_lle(struct ifnet *ifp, struct
#ifdef MAC
mac_netinet6_nd6_send(ifp, m);
#endif
+ if (lle != NULL) {
+ if (*tail == NULL)
+ *tail = m;
+ else
+ (*tail)->m_nextpkt = m;
+ return (error);
+ }
if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
return ((*ifp->if_output)(origifp, m, (struct sockaddr *)dst,
rt));
@@ -1887,6 +1909,37 @@ nd6_output_lle(struct ifnet *ifp, struct
}
#undef senderr
+
+int
+nd6_output_flush(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *chain,
+ struct sockaddr_in6 *dst, struct rtentry *rt)
+{
+ struct mbuf *m, *m_head;
+ struct ifnet *outifp;
+ int error = 0;
+
+ m_head = chain;
+ if ((ifp->if_flags & IFF_LOOPBACK) != 0)
+ outifp = origifp;
+ else
+ outifp = ifp;
+
+ while (m_head) {
+ m = m_head;
+ m_head = m_head->m_nextpkt;
+ error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt);
+ }
+
+ /*
+ * XXX
+ * note that intermediate errors are blindly ignored - but this is
+ * the same convention as used with nd6_output when called by
+ * nd6_cache_lladdr
+ */
+ return (error);
+}
+
+
int
nd6_need_cache(struct ifnet *ifp)
{
Modified: projects/arpv2_merge_1/sys/netinet6/nd6.h
==============================================================================
--- projects/arpv2_merge_1/sys/netinet6/nd6.h Sun Dec 14 00:40:14 2008 (r186069)
+++ projects/arpv2_merge_1/sys/netinet6/nd6.h Sun Dec 14 01:12:55 2008 (r186070)
@@ -392,7 +392,10 @@ struct llentry *nd6_cache_lladdr __P((st
int nd6_output __P((struct ifnet *, struct ifnet *, struct mbuf *,
struct sockaddr_in6 *, struct rtentry *));
int nd6_output_lle __P((struct ifnet *, struct ifnet *, struct mbuf *,
- struct sockaddr_in6 *, struct rtentry *, struct llentry *));
+ struct sockaddr_in6 *, struct rtentry *, struct llentry *,
+ struct mbuf **));
+int nd6_output_flush __P((struct ifnet *, struct ifnet *, struct mbuf *,
+ struct sockaddr_in6 *, struct rtentry *));
int nd6_need_cache __P((struct ifnet *));
int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *,
struct sockaddr *, u_char *, struct llentry **));
Modified: projects/arpv2_merge_1/sys/netinet6/nd6_nbr.c
==============================================================================
--- projects/arpv2_merge_1/sys/netinet6/nd6_nbr.c Sun Dec 14 00:40:14 2008 (r186069)
+++ projects/arpv2_merge_1/sys/netinet6/nd6_nbr.c Sun Dec 14 01:12:55 2008 (r186070)
@@ -606,6 +606,8 @@ nd6_na_input(struct mbuf *m, int off, in
struct ifaddr *ifa;
struct llentry *ln = NULL;
union nd_opts ndopts;
+ struct mbuf *chain = NULL;
+ struct sockaddr_in6 sin6;
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
if (ip6->ip6_hlim != 255) {
@@ -872,13 +874,18 @@ nd6_na_input(struct mbuf *m, int off, in
* we assume ifp is not a loopback here, so just set
* the 2nd argument as the 1st one.
*/
- nd6_output_lle(ifp, ifp, m_hold, L3_ADDR_SIN6(ln), NULL, ln);
+ nd6_output_lle(ifp, ifp, m_hold, L3_ADDR_SIN6(ln), NULL, ln, &chain);
}
}
freeit:
- if (ln)
+ if (ln) {
+ if (chain)
+ memcpy(&sin6, L3_ADDR_SIN6(ln), sizeof(sin6));
LLE_WUNLOCK(ln);
+ if (chain)
+ nd6_output_flush(ifp, ifp, chain, &sin6, NULL);
+ }
m_freem(m);
return;
More information about the svn-src-projects
mailing list