Page MenuHomeFreeBSD

D31432.id93367.diff
No OneTemporary

D31432.id93367.diff

diff --git a/sys/net/if_llatbl.h b/sys/net/if_llatbl.h
--- a/sys/net/if_llatbl.h
+++ b/sys/net/if_llatbl.h
@@ -238,6 +238,8 @@
struct ifnet *lltable_get_ifp(const struct lltable *llt);
int lltable_get_af(const struct lltable *llt);
+bool lltable_acquire_wlock(struct ifnet *ifp, struct llentry *lle);
+
int lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f,
void *farg);
/*
diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c
--- a/sys/net/if_llatbl.c
+++ b/sys/net/if_llatbl.c
@@ -318,22 +318,18 @@
}
/*
- * Tries to update @lle link-level address.
- * Since update requires AFDATA WLOCK, function
- * drops @lle lock, acquires AFDATA lock and then acquires
- * @lle lock to maintain lock order.
+ * Acquires lltable write lock.
*
- * Returns 1 on success.
+ * Returns true on success, with both lltable and lle lock held.
+ * On failure, false is returned and lle wlock is still held.
*/
-int
-lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle,
- const char *linkhdr, size_t linkhdrsize, int lladdr_off)
+bool
+lltable_acquire_wlock(struct ifnet *ifp, struct llentry *lle)
{
+ NET_EPOCH_ASSERT();
/* Perform real LLE update */
/* use afdata WLOCK to update fields */
- LLE_WLOCK_ASSERT(lle);
- LLE_ADDREF(lle);
LLE_WUNLOCK(lle);
IF_AFDATA_WLOCK(ifp);
LLE_WLOCK(lle);
@@ -344,17 +340,33 @@
*/
if ((lle->la_flags & LLE_DELETED) != 0) {
IF_AFDATA_WUNLOCK(ifp);
- LLE_FREE_LOCKED(lle);
- return (0);
+ return (false);
}
+ return (true);
+}
+
+/*
+ * Tries to update @lle link-level address.
+ * Since update requires AFDATA WLOCK, function
+ * drops @lle lock, acquires AFDATA lock and then acquires
+ * @lle lock to maintain lock order.
+ *
+ * Returns 1 on success.
+ */
+int
+lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle,
+ const char *linkhdr, size_t linkhdrsize, int lladdr_off)
+{
+
+ if (!lltable_acquire_wlock(ifp, lle))
+ return (0);
+
/* Update data */
lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off);
IF_AFDATA_WUNLOCK(ifp);
- LLE_REMREF(lle);
-
return (1);
}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -376,6 +376,7 @@
int nd6_ioctl(u_long, caddr_t, struct ifnet *);
void nd6_cache_lladdr(struct ifnet *, struct in6_addr *,
char *, int, int, int);
+bool nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, char *lladdr);
struct mbuf *nd6_grab_holdchain(struct llentry *);
int nd6_flush_holdchain(struct ifnet *, struct llentry *, struct mbuf *);
int nd6_add_ifa_lle(struct in6_ifaddr *);
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -1382,6 +1382,35 @@
return (rc);
}
+/*
+ * Tries to update @lle address/prepend data with new @lladdr.
+ *
+ * Returns true on success.
+ * In any case, @lle is returned wlocked.
+ */
+bool
+nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, char *lladdr)
+{
+ u_char linkhdr[LLE_MAX_LINKHDR];
+ size_t linkhdrsize;
+ int lladdr_off;
+
+ LLE_WLOCK_ASSERT(lle);
+
+ linkhdrsize = sizeof(linkhdr);
+ if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
+ linkhdr, &linkhdrsize, &lladdr_off) != 0) {
+ return (false);
+ }
+
+ if (!lltable_acquire_wlock(ifp, lle))
+ return (false);
+ lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off);
+ IF_AFDATA_WUNLOCK(ifp);
+
+ return (true);
+}
+
/*
* Free an nd6 llinfo entry.
* Since the function would cause significant changes in the kernel, DO NOT
@@ -2027,14 +2056,9 @@
* Record source link-layer address
* XXX is it dependent to ifp->if_type?
*/
- linkhdrsize = sizeof(linkhdr);
- if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
- linkhdr, &linkhdrsize, &lladdr_off) != 0)
- return;
-
- if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize,
- lladdr_off) == 0) {
+ if (!nd6_try_set_entry_addr(ifp, ln, lladdr)) {
/* Entry was deleted */
+ LLE_WUNLOCK(ln);
return;
}
@@ -2257,6 +2281,39 @@
return (nd6_resolve_slow(ifp, 0, m, dst6, desten, pflags, plle));
}
+/*
+ * Finds or creates a new llentry for @addr.
+ * Returns wlocked llentry or NULL.
+ */
+static __noinline struct llentry *
+nd6_get_llentry(struct ifnet *ifp, const struct in6_addr *addr)
+{
+ struct llentry *lle, *lle_tmp;
+
+ lle = nd6_alloc(addr, 0, ifp);
+ if (lle == NULL) {
+ char ip6buf[INET6_ADDRSTRLEN];
+ log(LOG_DEBUG,
+ "nd6_get_llentry: can't allocate llinfo for %s "
+ "(ln=%p)\n",
+ ip6_sprintf(ip6buf, addr), lle);
+ return (NULL);
+ }
+
+ IF_AFDATA_WLOCK(ifp);
+ LLE_WLOCK(lle);
+ /* Prefer any existing entry over newly-created one */
+ lle_tmp = nd6_lookup(addr, LLE_EXCLUSIVE, ifp);
+ if (lle_tmp == NULL)
+ lltable_link_entry(LLTABLE6(ifp), lle);
+ IF_AFDATA_WUNLOCK(ifp);
+ if (lle_tmp != NULL) {
+ lltable_free_entry(LLTABLE6(ifp), lle);
+ return (lle_tmp);
+ } else
+ return (lle);
+}
+
/*
* Do L2 address resolution for @sa_dst address. Stores found
* address in @desten buffer. Copy of lle ln_flags can be also
@@ -2273,7 +2330,7 @@
const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags,
struct llentry **plle)
{
- struct llentry *lle = NULL, *lle_tmp;
+ struct llentry *lle = NULL;
struct in6_addr *psrc, src;
int send_ns, ll_len;
char *lladdr;
@@ -2286,39 +2343,16 @@
* At this point, the destination of the packet must be a unicast
* or an anycast address(i.e. not a multicast).
*/
- if (lle == NULL) {
- lle = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp);
- if ((lle == NULL) && nd6_is_addr_neighbor(dst, ifp)) {
- /*
- * Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
- * the condition below is not very efficient. But we believe
- * it is tolerable, because this should be a rare case.
- */
- lle = nd6_alloc(&dst->sin6_addr, 0, ifp);
- if (lle == NULL) {
- char ip6buf[INET6_ADDRSTRLEN];
- log(LOG_DEBUG,
- "nd6_output: can't allocate llinfo for %s "
- "(ln=%p)\n",
- ip6_sprintf(ip6buf, &dst->sin6_addr), lle);
- m_freem(m);
- return (ENOBUFS);
- }
+ lle = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp);
+ if ((lle == NULL) && nd6_is_addr_neighbor(dst, ifp)) {
+ /*
+ * Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
+ * the condition below is not very efficient. But we believe
+ * it is tolerable, because this should be a rare case.
+ */
+ lle = nd6_get_llentry(ifp, &dst->sin6_addr);
+ }
- IF_AFDATA_WLOCK(ifp);
- LLE_WLOCK(lle);
- /* Prefer any existing entry over newly-created one */
- lle_tmp = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp);
- if (lle_tmp == NULL)
- lltable_link_entry(LLTABLE6(ifp), lle);
- IF_AFDATA_WUNLOCK(ifp);
- if (lle_tmp != NULL) {
- lltable_free_entry(LLTABLE6(ifp), lle);
- lle = lle_tmp;
- lle_tmp = NULL;
- }
- }
- }
if (lle == NULL) {
m_freem(m);
return (ENOBUFS);
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -770,16 +770,9 @@
/*
* Record link-layer address, and update the state.
*/
- linkhdrsize = sizeof(linkhdr);
- if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
- linkhdr, &linkhdrsize, &lladdr_off) != 0)
- return;
-
- if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize,
- lladdr_off) == 0) {
- ln = NULL;
+ if (!nd6_try_set_entry_addr(ifp, ln, lladdr))
goto freeit;
- }
+
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
if (is_solicited)
nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE);

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 29, 9:29 AM (4 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26325115
Default Alt Text
D31432.id93367.diff (7 KB)

Event Timeline