Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 29 Aug 2015 13:44:28 +0000 (UTC)
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r287291 - in projects/routing/sys: net netinet
Message-ID:  <201508291344.t7TDiS1s032411@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201508291344.t7TDiS1s032411>