svn commit: r286722 - head/sys/netinet

Alexander V. Chernikov melifaro at FreeBSD.org
Thu Aug 13 13:38:11 UTC 2015


Author: melifaro
Date: Thu Aug 13 13:38:09 2015
New Revision: 286722
URL: https://svnweb.freebsd.org/changeset/base/286722

Log:
  Move lle update code from from gigantic ip_arpinput() to
  separate bunch of functions. The goal is to isolate actual lle
  updates to permit more fine-grained locking.
  
  Do all lle link-level update under AFDATA wlock.
  
  Sponsored by:	Yandex LLC

Modified:
  head/sys/netinet/if_ether.c

Modified: head/sys/netinet/if_ether.c
==============================================================================
--- head/sys/netinet/if_ether.c	Thu Aug 13 13:21:00 2015	(r286721)
+++ head/sys/netinet/if_ether.c	Thu Aug 13 13:38:09 2015	(r286722)
@@ -130,6 +130,13 @@ static void	arptimer(void *);
 static void	in_arpinput(struct mbuf *);
 #endif
 
+static void arp_check_update_lle(struct arphdr *ah, struct in_addr isaddr,
+    struct ifnet *ifp, int bridged, struct llentry *la);
+static void arp_update_lle(struct arphdr *ah, struct ifnet *ifp,
+    struct llentry *la);
+static void arp_mark_lle_reachable(struct llentry *la);
+
+
 static const struct netisr_handler arp_nh = {
 	.nh_name = "arp",
 	.nh_handler = arpintr,
@@ -576,10 +583,10 @@ in_arpinput(struct mbuf *m)
 	struct sockaddr sa;
 	struct in_addr isaddr, itaddr, myaddr;
 	u_int8_t *enaddr = NULL;
-	int op, flags;
+	int op;
 	int req_len;
 	int bridged = 0, is_bridge = 0;
-	int carped, create;
+	int carped;
 	struct sockaddr_in sin;
 	sin.sin_len = sizeof(struct sockaddr_in);
 	sin.sin_family = AF_INET;
@@ -708,6 +715,16 @@ match:
 		    "%s!\n", inet_ntoa(isaddr));
 		goto drop;
 	}
+
+	if (ifp->if_addrlen != ah->ar_hln) {
+		LLE_WUNLOCK(la);
+		ARP_LOG(LOG_WARNING, "from %*D: addr len: new %d, "
+		    "i/f %d (ignored)\n", ifp->if_addrlen,
+		    (u_char *) ar_sha(ah), ":", ah->ar_hln,
+		    ifp->if_addrlen);
+		goto drop;
+	}
+
 	/*
 	 * Warn if another host is using the same IP address, but only if the
 	 * IP address isn't 0.0.0.0, which is used for DHCP only, in which
@@ -730,100 +747,22 @@ match:
 	sin.sin_len = sizeof(struct sockaddr_in);
 	sin.sin_family = AF_INET;
 	sin.sin_addr = isaddr;
-	create = (itaddr.s_addr == myaddr.s_addr) ? 1 : 0;
-	flags = LLE_EXCLUSIVE;
-	IF_AFDATA_LOCK(ifp);
-	if (create != 0)
-		la = lla_create(LLTABLE(ifp), 0, (struct sockaddr *)&sin);
-	else
-		la = lla_lookup(LLTABLE(ifp), flags, (struct sockaddr *)&sin);
-	IF_AFDATA_UNLOCK(ifp);
-	if (la != NULL) {
-		/* the following is not an error when doing bridging */
-		if (!bridged && la->lle_tbl->llt_ifp != ifp) {
-			if (log_arp_wrong_iface)
-				ARP_LOG(LOG_WARNING, "%s is on %s "
-				    "but got reply from %*D on %s\n",
-				    inet_ntoa(isaddr),
-				    la->lle_tbl->llt_ifp->if_xname,
-				    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
-				    ifp->if_xname);
-			LLE_WUNLOCK(la);
-			goto reply;
-		}
-		if ((la->la_flags & LLE_VALID) &&
-		    bcmp(ar_sha(ah), &la->ll_addr, ifp->if_addrlen)) {
-			if (la->la_flags & LLE_STATIC) {
-				LLE_WUNLOCK(la);
-				if (log_arp_permanent_modify)
-					ARP_LOG(LOG_ERR,
-					    "%*D attempts to modify "
-					    "permanent entry for %s on %s\n",
-					    ifp->if_addrlen,
-					    (u_char *)ar_sha(ah), ":",
-					    inet_ntoa(isaddr), ifp->if_xname);
-				goto reply;
-			}
-			if (log_arp_movements) {
-				ARP_LOG(LOG_INFO, "%s moved from %*D "
-				    "to %*D on %s\n",
-				    inet_ntoa(isaddr),
-				    ifp->if_addrlen,
-				    (u_char *)&la->ll_addr, ":",
-				    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
-				    ifp->if_xname);
-			}
-		}
-
-		if (ifp->if_addrlen != ah->ar_hln) {
-			LLE_WUNLOCK(la);
-			ARP_LOG(LOG_WARNING, "from %*D: addr len: new %d, "
-			    "i/f %d (ignored)\n", ifp->if_addrlen,
-			    (u_char *) ar_sha(ah), ":", ah->ar_hln,
-			    ifp->if_addrlen);
-			goto drop;
-		}
-		(void)memcpy(&la->ll_addr, ar_sha(ah), ifp->if_addrlen);
-		la->la_flags |= LLE_VALID;
-
-		EVENTHANDLER_INVOKE(lle_event, la, LLENTRY_RESOLVED);
-
-		if (!(la->la_flags & LLE_STATIC)) {
-			int canceled;
-
-			LLE_ADDREF(la);
-			la->la_expire = time_uptime + V_arpt_keep;
-			canceled = callout_reset(&la->lle_timer,
-			    hz * V_arpt_keep, arptimer, la);
-			if (canceled)
-				LLE_REMREF(la);
-		}
-		la->la_asked = 0;
-		la->la_preempt = V_arp_maxtries;
+	IF_AFDATA_RLOCK(ifp);
+	la = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, (struct sockaddr *)&sin);
+	IF_AFDATA_RUNLOCK(ifp);
+	if (la != NULL)
+		arp_check_update_lle(ah, isaddr, ifp, bridged, la);
+	else if (itaddr.s_addr == myaddr.s_addr) {
 		/*
-		 * The packets are all freed within the call to the output
-		 * routine.
-		 *
-		 * NB: The lock MUST be released before the call to the
-		 * output routine.
+		 * Reply to our address, but no lle exists yet.
+		 * do we really have to create an entry?
 		 */
-		if (la->la_hold != NULL) {
-			struct mbuf *m_hold, *m_hold_next;
-
-			m_hold = la->la_hold;
-			la->la_hold = NULL;
-			la->la_numheld = 0;
-			lltable_fill_sa_entry(la, (struct sockaddr *)&sa);
-			LLE_WUNLOCK(la);
-			for (; m_hold != NULL; m_hold = m_hold_next) {
-				m_hold_next = m_hold->m_nextpkt;
-				m_hold->m_nextpkt = NULL;
-				/* Avoid confusing lower layers. */
-				m_clrprotoflags(m_hold);
-				(*ifp->if_output)(ifp, m_hold, &sa, NULL);
-			}
-		} else
-			LLE_WUNLOCK(la);
+		IF_AFDATA_WLOCK(ifp);
+		la = lla_create(LLTABLE(ifp), 0, (struct sockaddr *)&sin);
+		arp_update_lle(ah, ifp, la);
+		IF_AFDATA_WUNLOCK(ifp);
+		arp_mark_lle_reachable(la);
+		LLE_WUNLOCK(la);
 	}
 reply:
 	if (op != ARPOP_REQUEST)
@@ -934,6 +873,140 @@ drop:
 }
 #endif
 
+/*
+ * Checks received arp data against existing @la.
+ * Updates lle state/performs notification if necessary.
+ */
+static void
+arp_check_update_lle(struct arphdr *ah, struct in_addr isaddr, struct ifnet *ifp,
+    int bridged, struct llentry *la)
+{
+	struct sockaddr sa;
+	struct mbuf *m_hold, *m_hold_next;
+
+	LLE_WLOCK_ASSERT(la);
+
+	/* the following is not an error when doing bridging */
+	if (!bridged && la->lle_tbl->llt_ifp != ifp) {
+		if (log_arp_wrong_iface)
+			ARP_LOG(LOG_WARNING, "%s is on %s "
+			    "but got reply from %*D on %s\n",
+			    inet_ntoa(isaddr),
+			    la->lle_tbl->llt_ifp->if_xname,
+			    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+			    ifp->if_xname);
+		LLE_WUNLOCK(la);
+		return;
+	}
+	if ((la->la_flags & LLE_VALID) &&
+	    bcmp(ar_sha(ah), &la->ll_addr, ifp->if_addrlen)) {
+		if (la->la_flags & LLE_STATIC) {
+			LLE_WUNLOCK(la);
+			if (log_arp_permanent_modify)
+				ARP_LOG(LOG_ERR,
+				    "%*D attempts to modify "
+				    "permanent entry for %s on %s\n",
+				    ifp->if_addrlen,
+				    (u_char *)ar_sha(ah), ":",
+				    inet_ntoa(isaddr), ifp->if_xname);
+			return;
+		}
+		if (log_arp_movements) {
+			ARP_LOG(LOG_INFO, "%s moved from %*D "
+			    "to %*D on %s\n",
+			    inet_ntoa(isaddr),
+			    ifp->if_addrlen,
+			    (u_char *)&la->ll_addr, ":",
+			    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+			    ifp->if_xname);
+		}
+	}
+
+	/* Check if something has changed */
+	if (memcmp(&la->ll_addr, ar_sha(ah), ifp->if_addrlen) != 0 ||
+	    (la->la_flags & LLE_VALID) == 0) {
+		/* Perform real LLE update */
+		/* use afdata WLOCK to update fields */
+		LLE_ADDREF(la);
+		LLE_WUNLOCK(la);
+		IF_AFDATA_WLOCK(ifp);
+		LLE_WLOCK(la);
+
+		/*
+		 * Since we droppped LLE lock, other thread might have deleted
+		 * this lle. Check and return
+		 */
+		if ((la->la_flags & LLE_DELETED) != 0) {
+			IF_AFDATA_WUNLOCK(ifp);
+			LLE_FREE_LOCKED(la);
+			return;
+		}
+
+		/* Update data */
+		arp_update_lle(ah, ifp, la);
+
+		IF_AFDATA_WUNLOCK(ifp);
+		LLE_REMREF(la);
+	}
+
+	arp_mark_lle_reachable(la);
+
+	/*
+	 * The packets are all freed within the call to the output
+	 * routine.
+	 *
+	 * NB: The lock MUST be released before the call to the
+	 * output routine.
+	 */
+	if (la->la_hold != NULL) {
+		m_hold = la->la_hold;
+		la->la_hold = NULL;
+		la->la_numheld = 0;
+		lltable_fill_sa_entry(la, &sa);
+		LLE_WUNLOCK(la);
+		for (; m_hold != NULL; m_hold = m_hold_next) {
+			m_hold_next = m_hold->m_nextpkt;
+			m_hold->m_nextpkt = NULL;
+			/* Avoid confusing lower layers. */
+			m_clrprotoflags(m_hold);
+			(*ifp->if_output)(ifp, m_hold, &sa, NULL);
+		}
+	} else
+		LLE_WUNLOCK(la);
+}
+
+/*
+ * Updates @la fields used by fast path code.
+ */
+static void
+arp_update_lle(struct arphdr *ah, struct ifnet *ifp, struct llentry *la)
+{
+
+	memcpy(&la->ll_addr, ar_sha(ah), ifp->if_addrlen);
+	la->la_flags |= LLE_VALID;
+}
+
+static void
+arp_mark_lle_reachable(struct llentry *la)
+{
+	int canceled;
+
+	LLE_WLOCK_ASSERT(la);
+
+	EVENTHANDLER_INVOKE(lle_event, la, LLENTRY_RESOLVED);
+
+	if (!(la->la_flags & LLE_STATIC)) {
+		LLE_ADDREF(la);
+		la->la_expire = time_uptime + V_arpt_keep;
+		canceled = callout_reset(&la->lle_timer,
+		    hz * V_arpt_keep, arptimer, la);
+		if (canceled)
+			LLE_REMREF(la);
+	}
+	la->la_asked = 0;
+	la->la_preempt = V_arp_maxtries;
+}
+
 void
 arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa)
 {


More information about the svn-src-all mailing list