svn commit: r287291 - in projects/routing/sys: net netinet
Alexander V. Chernikov
melifaro at FreeBSD.org
Sat Aug 29 13:44:29 UTC 2015
Author: melifaro
Date: Sat Aug 29 13:44:27 2015
New Revision: 287291
URL: https://svnweb.freebsd.org/changeset/base/287291
Log:
Remove lle read lock from IPv4 fast path.
Since r286722, IPv4 LLE address field is updated while holding AFDATA WLOCK.
However, we need a bit more to be able to actually remove read lock.
First, some sort of flags needs to be added to check if we really can
use given address data. Second, we need to provide some sort of feedback
if the entry is really used when expiration time is approaching.
First is easily solved by adding another flag field updated under AFDATA
WLOCK along with mac address.
Second one is more tricky.
Currently this is done the following way:
When packet is sent using given lle and its expiration time is within
lle->la_preempt = V_arp_maxtries = 5 seconds, we decrease la_preempt under
lle RLOCK and send arprequest towards dst. If traffic flow continues (and
int case of no reply) we will send up to V_arp_maxtries (one each second)
before expiring entry.
New behavior:
Introduce simple state machine (incomplete->reachable<->verify->deleted)
Schedule arptimer V_arp_maxtries seconds ealier. On first timer call in
"reachable" state set special r_kick field to 1, set state to "verify" and
reschedule itself V_arpt_rexmit = 1 second forward. While looking up lle
fast path checks if given r_kick field is nonzero and sets it to zero
under lle WLOCK (which should be very rare). On subsequent calls in arptimer
we check if r_kick was changed (and issue arprequest if yes) and
reschedule until expire (if valid reply was received, set state back to
"reachable" and reschedule arptimer according to V_arpt_keep value.
Given that, introduce 2 new fields:
- r_flags, containing flags used by fast path code. New flags are:
RLLE_VALID (pre-compiled yes/no flag if we can use the entry)
RLLE_IFADDR (to be able to pass given flag as hint to ether_output)
- r_kick, used for fast path feedback.
Modified:
projects/routing/sys/net/if_llatbl.c
projects/routing/sys/net/if_llatbl.h
projects/routing/sys/netinet/if_ether.c
projects/routing/sys/netinet/in.c
Modified: projects/routing/sys/net/if_llatbl.c
==============================================================================
--- projects/routing/sys/net/if_llatbl.c Sat Aug 29 13:35:33 2015 (r287290)
+++ projects/routing/sys/net/if_llatbl.c Sat Aug 29 13:44:27 2015 (r287291)
@@ -600,6 +600,7 @@ lla_rt_output(struct rt_msghdr *rtm, str
if ((rtm->rtm_flags & RTF_ANNOUNCE))
lle->la_flags |= LLE_PUB;
lle->la_flags |= LLE_VALID;
+ lle->r_flags |= RLLE_VALID;
#ifdef INET6
/*
* ND6
Modified: projects/routing/sys/net/if_llatbl.h
==============================================================================
--- projects/routing/sys/net/if_llatbl.h Sat Aug 29 13:35:33 2015 (r287290)
+++ projects/routing/sys/net/if_llatbl.h Sat Aug 29 13:44:27 2015 (r287291)
@@ -63,7 +63,8 @@ struct llentry {
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_kick; /* feedback from fast path */
uint64_t spare1;
struct lltable *lle_tbl;
@@ -188,6 +189,11 @@ MALLOC_DECLARE(M_LLTABLE);
#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)
Modified: projects/routing/sys/netinet/if_ether.c
==============================================================================
--- projects/routing/sys/netinet/if_ether.c Sat Aug 29 13:35:33 2015 (r287290)
+++ projects/routing/sys/netinet/if_ether.c Sat Aug 29 13:44:27 2015 (r287291)
@@ -82,6 +82,14 @@ __FBSDID("$FreeBSD$");
#define SIN(s) ((const struct sockaddr_in *)(s))
#define SDL(s) ((struct sockaddr_dl *)s)
+/* Simple ARP state machine */
+enum arp_llinfo_state {
+ ARP_LLINFO_INCOMPLETE = 0, /* No LLE data */
+ ARP_LLINFO_REACHABLE, /* LLE is valid */
+ ARP_LLINFO_VERIFY, /* LLE is valid, need refresh */
+ ARP_LLINFO_DELETED, /* LLE is deleted */
+};
+
SYSCTL_DECL(_net_link_ether);
static SYSCTL_NODE(_net_link_ether, PF_INET, inet, CTLFLAG_RW, 0, "");
static SYSCTL_NODE(_net_link_ether, PF_ARP, arp, CTLFLAG_RW, 0, "");
@@ -93,6 +101,7 @@ static VNET_DEFINE(int, arp_maxtries) =
static VNET_DEFINE(int, arp_proxyall) = 0;
static VNET_DEFINE(int, arpt_down) = 20; /* keep incomplete entries for
* 20 seconds */
+static VNET_DEFINE(int, arpt_rexmit) = 1; /* retransmit arp entries, sec*/
VNET_PCPUSTAT_DEFINE(struct arpstat, arpstat); /* ARP statistics, see if_arp.h */
VNET_PCPUSTAT_SYSINIT(arpstat);
@@ -104,6 +113,7 @@ static VNET_DEFINE(int, arp_maxhold) = 1
#define V_arpt_keep VNET(arpt_keep)
#define V_arpt_down VNET(arpt_down)
+#define V_arpt_rexmit VNET(arpt_rexmit)
#define V_arp_maxtries VNET(arp_maxtries)
#define V_arp_proxyall VNET(arp_proxyall)
#define V_arp_maxhold VNET(arp_maxhold)
@@ -202,6 +212,49 @@ arptimer(void *arg)
ifp = lle->lle_tbl->llt_ifp;
CURVNET_SET(ifp->if_vnet);
+ switch (lle->ln_state) {
+ case ARP_LLINFO_REACHABLE:
+
+ /*
+ * Expiration time is approaching.
+ * Let's try to refresh entry if it is still
+ * in use.
+ *
+ * Set r_kick to get feedback from
+ * fast path. Change state and re-schedule
+ * ourselves.
+ */
+ lle->r_kick = 1;
+ lle->ln_state = ARP_LLINFO_VERIFY;
+ callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit);
+ LLE_WUNLOCK(lle);
+ CURVNET_RESTORE();
+ return;
+ case ARP_LLINFO_VERIFY:
+ if (lle->r_kick == 0 && lle->la_preempt > 0) {
+ /* Entry was used, issue refresh request */
+ struct in_addr dst;
+ dst = lle->r_l3addr.addr4;
+ lle->la_preempt--;
+ callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit);
+ LLE_WUNLOCK(lle);
+ arprequest(ifp, NULL, &dst, NULL);
+ CURVNET_RESTORE();
+ return;
+ }
+ /* Nothing happened. Reschedule if not too late */
+ if (lle->la_expire > time_uptime) {
+ callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit);
+ LLE_WUNLOCK(lle);
+ CURVNET_RESTORE();
+ return;
+ }
+ break;
+ case ARP_LLINFO_INCOMPLETE:
+ case ARP_LLINFO_DELETED:
+ break;
+ }
+
if ((lle->la_flags & LLE_DELETED) == 0) {
int evt;
@@ -320,10 +373,9 @@ int
arpresolve_fast(struct ifnet *ifp, struct in_addr dst, u_int mflags,
u_char *dst_addr)
{
- int do_arp, error;
+ int error;
struct llentry *la;
struct sockaddr_in sin;
- int renew;
if (mflags & M_BCAST) {
memcpy(dst_addr, ifp->if_broadcastaddr, ifp->if_addrlen);
@@ -334,7 +386,6 @@ arpresolve_fast(struct ifnet *ifp, struc
return (0);
}
- do_arp = 0;
error = EAGAIN;
memset(&sin, 0, sizeof(sin));
@@ -343,35 +394,21 @@ arpresolve_fast(struct ifnet *ifp, struc
sin.sin_len = sizeof(sin);
IF_AFDATA_RLOCK(ifp);
- la = lla_lookup(LLTABLE(ifp), 0, (const struct sockaddr *)&sin);
- IF_AFDATA_RUNLOCK(ifp);
-
- if (la == NULL)
- return (error);
-
- if ((la->la_flags & LLE_VALID) &&
- ((la->la_flags & LLE_STATIC) || la->la_expire > time_uptime)) {
+ la = lla_lookup(LLTABLE(ifp), LLE_UNLOCKED, (struct sockaddr *)&sin);
+ if (la != NULL && (la->r_flags & RLLE_VALID) != 0) {
+ /* Entry found, let's copy lle info */
bcopy(&la->ll_addr, dst_addr, ifp->if_addrlen);
- renew = 0;
- /*
- * If entry has an expiry time and it is approaching,
- * see if we need to send an ARP request within this
- * arpt_down interval.
- */
- if (!(la->la_flags & LLE_STATIC) &&
- time_uptime + la->la_preempt > la->la_expire) {
- renew = 1;
- la->la_preempt--;
+ /* Check if we have feedback request from arptimer() */
+ if (la->r_kick != 0) {
+ LLE_WLOCK(la);
+ la->r_kick = 0; /* Notify that entry was used */
+ LLE_WUNLOCK(la);
}
-
- LLE_RUNLOCK(la);
-
- if (renew == 1)
- arprequest(ifp, NULL, &dst, NULL);
-
+ IF_AFDATA_RUNLOCK(ifp);
return (0);
}
- LLE_RUNLOCK(la);
+ IF_AFDATA_RUNLOCK(ifp);
+
return (error);
}
@@ -434,26 +471,13 @@ arpresolve_full(struct ifnet *ifp, int i
if ((la->la_flags & LLE_VALID) &&
((la->la_flags & LLE_STATIC) || la->la_expire > time_uptime)) {
bcopy(&la->ll_addr, desten, ifp->if_addrlen);
- renew = 0;
- /*
- * If entry has an expiry time and it is approaching,
- * see if we need to send an ARP request within this
- * arpt_down interval.
- */
- if (!(la->la_flags & LLE_STATIC) &&
- time_uptime + la->la_preempt > la->la_expire) {
- renew = 1;
- la->la_preempt--;
- }
+ /* Check if we have feedback request from arptimer() */
+ if (la->r_kick != 0)
+ la->r_kick = 0; /* Notify that entry was used */
if (pflags != NULL)
- *pflags = la->la_flags;
-
+ *pflags = la->la_flags & (LLE_VALID|LLE_IFADDR);
LLE_WUNLOCK(la);
-
- if (renew == 1)
- arprequest(ifp, NULL, &SIN(dst)->sin_addr, NULL);
-
return (0);
}
@@ -521,7 +545,7 @@ arpresolve_full(struct ifnet *ifp, int i
* m is the mbuf. May be NULL if we don't have a packet.
* dst is the next hop,
* desten is the storage to put LL address.
- * flags returns lle entry flags.
+ * flags returns subset of lle flags: LLE_VALID | LLE_IFADDR
*
* On success, desten and flags are filled in and the function returns 0;
* If the packet must be held pending resolution, we return EWOULDBLOCK
@@ -533,7 +557,6 @@ arpresolve(struct ifnet *ifp, int is_gw,
const struct sockaddr *dst, u_char *desten, uint32_t *pflags)
{
struct llentry *la = 0;
- int renew;
if (pflags != NULL)
*pflags = 0;
@@ -553,40 +576,24 @@ arpresolve(struct ifnet *ifp, int is_gw,
}
IF_AFDATA_RLOCK(ifp);
- la = lla_lookup(LLTABLE(ifp), 0, dst);
- IF_AFDATA_RUNLOCK(ifp);
-
- if (la == NULL)
- return (arpresolve_full(ifp, is_gw, 1, m, dst, desten, pflags));
-
- if ((la->la_flags & LLE_VALID) &&
- ((la->la_flags & LLE_STATIC) || la->la_expire > time_uptime)) {
+ la = lla_lookup(LLTABLE(ifp), LLE_UNLOCKED, dst);
+ if (la != NULL && (la->r_flags & RLLE_VALID) != 0) {
+ /* Entry found, let's copy lle info */
bcopy(&la->ll_addr, desten, ifp->if_addrlen);
- renew = 0;
- /*
- * If entry has an expiry time and it is approaching,
- * see if we need to send an ARP request within this
- * arpt_down interval.
- */
- if (!(la->la_flags & LLE_STATIC) &&
- time_uptime + la->la_preempt > la->la_expire) {
- renew = 1;
- la->la_preempt--;
- }
-
if (pflags != NULL)
- *pflags = la->la_flags;
-
- LLE_RUNLOCK(la);
-
- if (renew == 1)
- arprequest(ifp, NULL, &SIN(dst)->sin_addr, NULL);
-
+ *pflags = LLE_VALID | (la->r_flags & RLLE_IFADDR);
+ /* Check if we have feedback request from arptimer() */
+ if (la->r_kick != 0) {
+ LLE_WLOCK(la);
+ la->r_kick = 0; /* Notify that entry was used */
+ LLE_WUNLOCK(la);
+ }
+ IF_AFDATA_RUNLOCK(ifp);
return (0);
}
- LLE_RUNLOCK(la);
+ IF_AFDATA_RUNLOCK(ifp);
- return (arpresolve_full(ifp, is_gw, 0, m, dst, desten, pflags));
+ return (arpresolve_full(ifp, is_gw, 1, m, dst, desten, pflags));
}
/*
@@ -1124,22 +1131,28 @@ arp_update_lle(struct arphdr *ah, struct
memcpy(&la->ll_addr, ar_sha(ah), ifp->if_addrlen);
la->la_flags |= LLE_VALID;
+ la->r_flags |= RLLE_VALID;
+ la->r_kick = 0;
}
static void
arp_mark_lle_reachable(struct llentry *la)
{
- int canceled;
+ int canceled, wtime;
LLE_WLOCK_ASSERT(la);
+ la->ln_state = ARP_LLINFO_REACHABLE;
EVENTHANDLER_INVOKE(lle_event, la, LLENTRY_RESOLVED);
if (!(la->la_flags & LLE_STATIC)) {
LLE_ADDREF(la);
la->la_expire = time_uptime + V_arpt_keep;
+ wtime = V_arpt_keep - V_arp_maxtries * V_arpt_rexmit;
+ if (wtime < 0)
+ V_arpt_keep;
canceled = callout_reset(&la->lle_timer,
- hz * V_arpt_keep, arptimer, la);
+ hz * wtime, arptimer, la);
if (canceled)
LLE_REMREF(la);
}
Modified: projects/routing/sys/netinet/in.c
==============================================================================
--- projects/routing/sys/netinet/in.c Sat Aug 29 13:35:33 2015 (r287290)
+++ projects/routing/sys/netinet/in.c Sat Aug 29 13:44:27 2015 (r287291)
@@ -1195,9 +1195,12 @@ in_lltable_alloc(struct lltable *llt, u_
return (NULL);
}
lle->la_flags = flags;
+ if (flags & LLE_STATIC)
+ lle->r_flags |= RLLE_VALID;
if ((flags & LLE_IFADDR) == LLE_IFADDR) {
bcopy(IF_LLADDR(ifp), &lle->ll_addr, ifp->if_addrlen);
lle->la_flags |= (LLE_VALID | LLE_STATIC);
+ lle->r_flags |= RLLE_IFADDR;
}
return (lle);
@@ -1221,6 +1224,9 @@ in_lltable_lookup(struct lltable *llt, u
if (lle == NULL)
return (NULL);
+ if (flags & LLE_UNLOCKED)
+ return (lle);
+
if (flags & LLE_EXCLUSIVE)
LLE_WLOCK(lle);
else
More information about the svn-src-projects
mailing list