Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108168542
D3780.id9142.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D3780.id9142.diff
View Options
Index: sys/net/if_llatbl.h
===================================================================
--- sys/net/if_llatbl.h
+++ sys/net/if_llatbl.h
@@ -63,7 +63,8 @@
uint16_t mac16[3];
uint8_t mac8[20]; /* IB needs 20 bytes. */
} ll_addr;
- uint32_t spare0;
+ uint16_t r_flags; /* LLE runtime flags */
+ uint16_t r_skip_req; /* feedback from fast path */
uint64_t spare1;
struct lltable *lle_tbl;
@@ -78,11 +79,14 @@
int16_t ln_state; /* IPv6 has ND6_LLINFO_NOSTATE == -2 */
uint16_t ln_router;
time_t ln_ntick;
+ time_t lle_remtime; /* Real time remaining */
+ time_t lle_hittime; /* Time when r_skip_req was unset */
int lle_refcnt;
LIST_ENTRY(llentry) lle_chain; /* chain of deleted items */
struct callout lle_timer;
struct rwlock lle_lock;
+ struct mtx req_mtx;
};
#define LLE_WLOCK(lle) rw_wlock(&(lle)->lle_lock)
@@ -95,6 +99,12 @@
#define LLE_LOCK_DESTROY(lle) rw_destroy(&(lle)->lle_lock)
#define LLE_WLOCK_ASSERT(lle) rw_assert(&(lle)->lle_lock, RA_WLOCKED)
+#define LLE_REQ_INIT(lle) mtx_init(&(lle)->req_mtx, "lle req", \
+ NULL, MTX_DEF)
+#define LLE_REQ_DESTROY(lle) mtx_destroy(&(lle)->req_mtx)
+#define LLE_REQ_LOCK(lle) mtx_lock(&(lle)->req_mtx)
+#define LLE_REQ_UNLOCK(lle) mtx_unlock(&(lle)->req_mtx)
+
#define LLE_IS_VALID(lle) (((lle) != NULL) && ((lle) != (void *)-1))
#define LLE_ADDREF(lle) do { \
@@ -187,6 +197,11 @@
#define LLE_LINKED 0x0040 /* linked to lookup structure */
/* LLE request flags */
#define LLE_EXCLUSIVE 0x2000 /* return lle xlocked */
+#define LLE_UNLOCKED 0x4000 /* return lle unlocked */
+
+/* LLE flags used by fastpath code */
+#define RLLE_VALID 0x0001 /* entry is valid */
+#define RLLE_IFADDR LLE_IFADDR /* entry is ifaddr */
#define LLATBL_HASH(key, mask) \
(((((((key >> 8) ^ key) >> 8) ^ key) >> 8) ^ key) & mask)
@@ -207,6 +222,10 @@
/* helper functions */
size_t lltable_drop_entry_queue(struct llentry *);
+void lltable_set_entry_addr(struct llentry *lle, char *lladdr,
+ struct ifnet *ifp);
+int lltable_try_set_entry_addr(struct llentry *lle, char *lladdr,
+ struct ifnet *ifp);
struct llentry *lltable_alloc_entry(struct lltable *llt, u_int flags,
const struct sockaddr *l4addr);
Index: sys/net/if_llatbl.c
===================================================================
--- sys/net/if_llatbl.c
+++ sys/net/if_llatbl.c
@@ -277,6 +277,49 @@
return (pkts_dropped);
}
+void
+lltable_set_entry_addr(struct llentry *lle, char *lladdr, struct ifnet *ifp)
+{
+
+ bcopy(lladdr, &lle->ll_addr, ifp->if_addrlen);
+ lle->la_flags |= LLE_VALID;
+ lle->r_flags |= RLLE_VALID;
+}
+
+int
+lltable_try_set_entry_addr(struct llentry *lle, char *lladdr,
+ struct ifnet *ifp)
+{
+
+ /* 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);
+
+ /*
+ * Since we droppped LLE lock, other thread might have deleted
+ * this lle. Check and return
+ */
+ if ((lle->la_flags & LLE_DELETED) != 0) {
+ IF_AFDATA_WUNLOCK(ifp);
+ LLE_FREE_LOCKED(lle);
+ return (0);
+ }
+
+ /* Update data */
+ lltable_set_entry_addr(lle, lladdr, ifp);
+
+ IF_AFDATA_WUNLOCK(ifp);
+
+ LLE_REMREF(lle);
+
+ return (1);
+}
+
+
/*
*
* Performes generic cleanup routines and frees lle.
@@ -631,6 +674,7 @@
if ((rtm->rtm_flags & RTF_ANNOUNCE))
lle->la_flags |= LLE_PUB;
lle->la_flags |= LLE_VALID;
+ lle->r_flags |= RLLE_VALID;
lle->la_expire = rtm->rtm_rmx.rmx_expire;
laflags = lle->la_flags;
Index: sys/netinet6/in6.c
===================================================================
--- sys/netinet6/in6.c
+++ sys/netinet6/in6.c
@@ -2063,6 +2063,7 @@
LLE_WUNLOCK(lle);
LLE_LOCK_DESTROY(lle);
+ LLE_REQ_DESTROY(lle);
free(lle, M_LLTABLE);
}
@@ -2079,6 +2080,7 @@
lle->base.lle_refcnt = 1;
lle->base.lle_free = in6_lltable_destroy_lle;
LLE_LOCK_INIT(&lle->base);
+ LLE_REQ_INIT(&lle->base);
callout_init(&lle->base.lle_timer, 1);
return (&lle->base);
@@ -2287,6 +2289,13 @@
if (lle == NULL)
return (NULL);
+ KASSERT((flags & (LLE_UNLOCKED|LLE_EXCLUSIVE)) !=
+ (LLE_UNLOCKED|LLE_EXCLUSIVE),("wrong lle request flags: 0x%X",
+ flags));
+
+ if (flags & LLE_UNLOCKED)
+ return (lle);
+
if (flags & LLE_EXCLUSIVE)
LLE_WLOCK(lle);
else
@@ -2349,8 +2358,8 @@
sdl->sdl_index = ifp->if_index;
sdl->sdl_type = ifp->if_type;
bcopy(&lle->ll_addr, LLADDR(sdl), ifp->if_addrlen);
- ndpc.rtm.rtm_rmx.rmx_expire =
- lle->la_flags & LLE_STATIC ? 0 : lle->la_expire;
+ ndpc.rtm.rtm_rmx.rmx_expire = lle->la_expire +
+ lle->lle_remtime / hz;
ndpc.rtm.rtm_flags |= (RTF_HOST | RTF_LLDATA);
if (lle->la_flags & LLE_STATIC)
ndpc.rtm.rtm_flags |= RTF_STATIC;
Index: sys/netinet6/nd6.c
===================================================================
--- sys/netinet6/nd6.c
+++ sys/netinet6/nd6.c
@@ -542,6 +542,110 @@
}
/*
+ * Checks if we need to switch from STALE state.
+ *
+ * RFC 4861 requires switching from STALE to DELAY state
+ * on first packet matching entry, waiting V_nd6_delay and
+ * transition to PROBE state (if upper layer confirmation was
+ * not received).
+ *
+ * This code performs a bit differently:
+ * On packet hit we don't change state (but desired state
+ * can be guessed by control plane). However, after V_nd6_delay
+ * seconds code will transition to PROBE state (so DELAY state
+ * is kinda skipped in most situations).
+ *
+ * Typically, V_nd6_gctimer is bigger than V_nd6_delay, so
+ * we perform the following upon entering STALE state:
+ *
+ * 1) Arm timer to run each V_nd6_delay seconds to make sure that
+ * if packet was transmitted at the start of given interval, we
+ * would be able to switch to PROBE state in V_nd6_delay seconds
+ * as user expects.
+ *
+ * 2) Reshedule timer until original V_nd6_gctimer expires keeping
+ * lle in STALE state (remaining timer value stored in lle_remtime).
+ *
+ * 3) Reshedule timer if packet was transmitted less that V_nd6_delay
+ * seconds ago.
+ *
+ * Returns non-zero value if the entry is still STALE (storing
+ * the next timer interval in @pdelay).
+ *
+ * Returns zero value if original timer expired or we need to switch to
+ * PROBE (store that in @do_switch variable).
+ */
+static int
+nd6_is_stale(struct llentry *lle, long *pdelay, int *do_switch)
+{
+ int nd_delay, nd_gctimer, r_skip_req, time_diff;
+ time_t lle_hittime;
+ long delay;
+
+ *do_switch = 0;
+ nd_gctimer = V_nd6_gctimer;
+ nd_delay = V_nd6_delay;
+
+ LLE_REQ_LOCK(lle);
+ r_skip_req = lle->r_skip_req;
+ lle_hittime = lle->lle_hittime;
+ LLE_REQ_UNLOCK(lle);
+
+ if (r_skip_req > 0) {
+
+ /*
+ * Nonzero r_skip_req value was set upon entering
+ * STALE state. Since value was not changed, no
+ * packets were passed using this lle. Ask for
+ * timer reschedule and keep STALE state.
+ */
+ delay = (long)(MIN(nd_gctimer, nd_delay));
+ delay *= hz;
+ if (lle->lle_remtime > delay)
+ lle->lle_remtime -= delay;
+ else {
+ delay = lle->lle_remtime;
+ lle->lle_remtime = 0;
+ }
+
+ if (delay == 0) {
+
+ /*
+ * The original ng6_gctime timeout ended,
+ * no more rescheduling.
+ */
+ return (0);
+ }
+
+ *pdelay = delay;
+ return (1);
+ }
+
+ /*
+ * Packet received. Verify timestamp
+ */
+ time_diff = time_uptime - lle_hittime;
+ if (time_diff < 0)
+ time_diff = 0;
+ if (time_diff < nd6_delay) {
+
+ /*
+ * V_nd6_delay still not passed since the first
+ * hit in STALE state.
+ * Reshedule timer and return.
+ */
+ delay = (long)(nd_delay - time_diff) * hz;
+ *pdelay = delay;
+ return (1);
+ }
+
+ /* Request switching to probe */
+ *do_switch = 1;
+ return (0);
+}
+
+
+/*
* Switch @lle state to new state optionally arming timers.
*
* Set noinline to be dtrace-friendly
@@ -550,9 +654,11 @@
nd6_llinfo_setstate(struct llentry *lle, int newstate)
{
struct ifnet *ifp;
- long delay;
+ int nd_gctimer, nd_delay;
+ long delay, remtime;
delay = 0;
+ remtime = 0;
switch (newstate) {
case ND6_LLINFO_INCOMPLETE:
@@ -566,7 +672,19 @@
}
break;
case ND6_LLINFO_STALE:
- delay = (long)V_nd6_gctimer * hz;
+
+ /*
+ * Notify fast path that we want to know if any packet
+ * is transmitted by setting r_skip_req.
+ */
+ LLE_REQ_LOCK(lle);
+ lle->r_skip_req = 1;
+ LLE_REQ_UNLOCK(lle);
+ nd_delay = V_nd6_delay;
+ nd_gctimer = V_nd6_gctimer;
+
+ delay = (long)(MIN(nd_gctimer, nd_delay)) * hz;
+ remtime = (long)nd_gctimer * hz - delay;
break;
case ND6_LLINFO_DELAY:
lle->la_asked = 0;
@@ -577,6 +695,7 @@
if (delay > 0)
nd6_llinfo_settimer_locked(lle, delay);
+ lle->lle_remtime = remtime;
lle->ln_state = newstate;
}
@@ -592,7 +711,8 @@
struct in6_addr *dst, *pdst, *psrc, src;
struct ifnet *ifp;
struct nd_ifinfo *ndi = NULL;
- int send_ns;
+ int do_switch, send_ns;
+ long delay;
KASSERT(arg != NULL, ("%s: arg NULL", __func__));
ln = (struct llentry *)arg;
@@ -680,13 +800,35 @@
break;
case ND6_LLINFO_STALE:
- /* Garbage Collection(RFC 2461 5.3) */
- if (!ND6_LLINFO_PERMANENT(ln)) {
- EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED);
- nd6_free(ln, 1);
- ln = NULL;
+ if (nd6_is_stale(ln, &delay, &do_switch) != 0) {
+
+ /*
+ * No packet has used this entry and GC timeout
+ * has not been passed. Reshedule timer and
+ * return.
+ */
+ nd6_llinfo_settimer_locked(ln, delay);
+ break;
}
- break;
+
+ if (do_switch == 0) {
+
+ /*
+ * GC timer has ended and entry hasn't been used.
+ * Run Garbage collector (RFC 4861, 5.3)
+ */
+ if (!ND6_LLINFO_PERMANENT(ln)) {
+ EVENTHANDLER_INVOKE(lle_event, ln,
+ LLENTRY_EXPIRED);
+ nd6_free(ln, 1);
+ ln = NULL;
+ }
+ break;
+ }
+
+ /* Entry has been used AND delay timer has ended. */
+
+ /* FALLTHROUGH */
case ND6_LLINFO_DELAY:
if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
@@ -1738,10 +1880,8 @@
* Since we already know all the data for the new entry,
* fill it before insertion.
*/
- if (lladdr != NULL) {
- bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen);
- ln->la_flags |= LLE_VALID;
- }
+ if (lladdr != NULL)
+ lltable_set_entry_addr(ln, lladdr, ifp);
IF_AFDATA_WLOCK(ifp);
LLE_WLOCK(ln);
/* Prefer any existing lle over newly-created one */
@@ -1795,12 +1935,11 @@
if (is_newentry == 0 && llchange != 0) {
do_update = 1; /* (3,5) */
- /*
- * Record source link-layer address
- * XXX is it dependent to ifp->if_type?
- */
- bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen);
- ln->la_flags |= LLE_VALID;
+ if (lltable_try_set_entry_addr(ln, lladdr, ifp) == 0) {
+ /* Entry was deleted */
+ return;
+ }
+
nd6_llinfo_setstate(ln, ND6_LLINFO_STALE);
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
@@ -2000,31 +2139,25 @@
}
IF_AFDATA_RLOCK(ifp);
- ln = nd6_lookup(&dst6->sin6_addr, 0, ifp);
- IF_AFDATA_RUNLOCK(ifp);
-
- /*
- * Perform fast path for the following cases:
- * 1) lle state is REACHABLE
- * 2) lle state is DELAY (NS message sent)
- *
- * Every other case involves lle modification, so we handle
- * them separately.
- */
- if (ln == NULL || (ln->ln_state != ND6_LLINFO_REACHABLE &&
- ln->ln_state != ND6_LLINFO_DELAY)) {
- /* Fall back to slow processing path */
- if (ln != NULL)
- LLE_RUNLOCK(ln);
- return (nd6_resolve_slow(ifp, m, dst6, desten, pflags));
+ ln = nd6_lookup(&dst6->sin6_addr, LLE_UNLOCKED, ifp);
+ if (ln != NULL && (ln->r_flags & RLLE_VALID) != 0) {
+ /* Entry found, let's copy lle info */
+ bcopy(&ln->ll_addr, desten, ifp->if_addrlen);
+ if (pflags != NULL)
+ *pflags = LLE_VALID | (ln->r_flags & RLLE_IFADDR);
+ /* Check if we have feedback request from nd6 timer */
+ if (ln->r_skip_req != 0) {
+ LLE_REQ_LOCK(ln);
+ ln->r_skip_req = 0; /* Notify that entry was used */
+ ln->lle_hittime = time_uptime;
+ LLE_REQ_UNLOCK(ln);
+ }
+ IF_AFDATA_RUNLOCK(ifp);
+ return (0);
}
+ IF_AFDATA_RUNLOCK(ifp);
-
- bcopy(&ln->ll_addr, desten, ifp->if_addrlen);
- if (pflags != NULL)
- *pflags = ln->la_flags;
- LLE_RUNLOCK(ln);
- return (0);
+ return (nd6_resolve_slow(ifp, m, dst6, desten, pflags));
}
Index: sys/netinet6/nd6_nbr.c
===================================================================
--- sys/netinet6/nd6_nbr.c
+++ sys/netinet6/nd6_nbr.c
@@ -765,8 +765,10 @@
/*
* Record link-layer address, and update the state.
*/
- bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen);
- ln->la_flags |= LLE_VALID;
+ if (lltable_try_set_entry_addr(ln, lladdr, ifp) == 0) {
+ ln = NULL;
+ goto freeit;
+ }
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
if (is_solicited)
nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE);
@@ -832,8 +834,12 @@
* Update link-local address, if any.
*/
if (lladdr != NULL) {
- bcopy(lladdr, &ln->ll_addr, ifp->if_addrlen);
- ln->la_flags |= LLE_VALID;
+ int ret;
+ ret = lltable_try_set_entry_addr(ln,lladdr,ifp);
+ if (ret == 0) {
+ ln = NULL;
+ goto freeit;
+ }
EVENTHANDLER_INVOKE(lle_event, ln,
LLENTRY_RESOLVED);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jan 23, 3:58 AM (1 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16046175
Default Alt Text
D3780.id9142.diff (12 KB)
Attached To
Mode
D3780: Remove LLE read lock from IPv6 fast path.
Attached
Detach File
Event Timeline
Log In to Comment