From owner-svn-src-projects@FreeBSD.ORG Mon Dec 1 21:43:50 2014 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 5F7D4A60; Mon, 1 Dec 2014 21:43:50 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 4AACECB8; Mon, 1 Dec 2014 21:43:50 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id sB1Lho5Q076560; Mon, 1 Dec 2014 21:43:50 GMT (envelope-from melifaro@FreeBSD.org) Received: (from melifaro@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id sB1LhnAb076557; Mon, 1 Dec 2014 21:43:49 GMT (envelope-from melifaro@FreeBSD.org) Message-Id: <201412012143.sB1LhnAb076557@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: melifaro set sender to melifaro@FreeBSD.org using -f From: "Alexander V. Chernikov" Date: Mon, 1 Dec 2014 21:43:49 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r275382 - in projects/routing/sys: netinet netinet6 X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 01 Dec 2014 21:43:50 -0000 Author: melifaro Date: Mon Dec 1 21:43:48 2014 New Revision: 275382 URL: https://svnweb.freebsd.org/changeset/base/275382 Log: Do more fine-grained locking in lltable code: lltable_create_lle() does actual new lle creation without extensive locking and existing lle search. Move lle updating code from gigantic in_arpinput() to arp_update_llle() and some other functions. IPv6 changes to follow. Modified: projects/routing/sys/netinet/if_ether.c projects/routing/sys/netinet/in.c projects/routing/sys/netinet6/in6.c Modified: projects/routing/sys/netinet/if_ether.c ============================================================================== --- projects/routing/sys/netinet/if_ether.c Mon Dec 1 21:33:28 2014 (r275381) +++ projects/routing/sys/netinet/if_ether.c Mon Dec 1 21:43:48 2014 (r275382) @@ -138,6 +138,11 @@ static void arpintr(struct mbuf *); static void arptimer(void *); #ifdef INET static void in_arpinput(struct mbuf *); +static void arp_set_lle_reachable(struct llentry *); +static void arp_update_lle_addr(struct arphdr *, struct ifnet *, + struct llentry *); +static void arp_update_lle(struct arphdr *, struct in_addr, struct ifnet *, + int , struct llentry *); #endif static int arpresolve_slow(struct ifnet *, int is_gw, struct mbuf *, const struct sockaddr *, u_char *, struct llentry **); @@ -254,6 +259,7 @@ arptimer(void *arg) * Note other thread could have removed given entry * stopping callout and removing LLE reference. */ + //llt->llt_stop_timers(lle); if ((lle->la_flags & LLE_CALLOUTREF) != 0) { LLE_REMREF(lle); lle->la_flags &= ~LLE_CALLOUTREF; @@ -484,7 +490,7 @@ static int arpresolve_slow(struct ifnet *ifp, int is_gw, struct mbuf *m, const struct sockaddr *dst, u_char *desten, struct llentry **lle) { - struct llentry *la = 0; + struct llentry *la, *la_tmp; struct mbuf *curr = NULL; struct mbuf *next = NULL; int create, error; @@ -497,14 +503,28 @@ arpresolve_slow(struct ifnet *ifp, int i IF_AFDATA_RUNLOCK(ifp); if (la == NULL && (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) { create = 1; - IF_AFDATA_CFG_WLOCK(ifp); la = lltable_create_lle(LLTABLE(ifp), 0, dst); if (la != NULL) { - IF_AFDATA_RUN_WLOCK(ifp); - llentry_link(LLTABLE(ifp), la); - IF_AFDATA_RUN_WUNLOCK(ifp); + IF_AFDATA_CFG_WLOCK(ifp); + LLE_WLOCK(la); + la_tmp = lltable_lookup_lle(LLTABLE(ifp), LLE_EXCLUSIVE, + dst); + if (la_tmp == NULL) { + /* + * No entry has been found. Link new one. + */ + IF_AFDATA_RUN_WLOCK(ifp); + llentry_link(LLTABLE(ifp), la); + IF_AFDATA_RUN_WUNLOCK(ifp); + } + IF_AFDATA_CFG_WUNLOCK(ifp); + + if (la_tmp != NULL) { + LLE_FREE_LOCKED(la); + la = la_tmp; + la_tmp = NULL; + } } - IF_AFDATA_CFG_WUNLOCK(ifp); } if (la == NULL) { if (create != 0) @@ -704,13 +724,14 @@ 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 canceled, carped, create; - int wtime; + int carped; struct nhop4_extended nh_ext; struct sockaddr_in sin; + struct llentry *la_tmp; + sin.sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; @@ -838,6 +859,15 @@ match: "%s!\n", inet_ntoa(isaddr)); goto drop; } + + if (ifp->if_addrlen != ah->ar_hln) { + 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 @@ -860,128 +890,46 @@ 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_CFG_WLOCK(ifp); - if (create != 0) { - la = lltable_create_lle(LLTABLE(ifp), 0, - (struct sockaddr *)&sin); - } else - la = lltable_lookup_lle(LLTABLE(ifp), flags, - (struct sockaddr *)&sin); - IF_AFDATA_CFG_WUNLOCK(ifp); - if (la != NULL) { - /* the following is not an error when doing bridging */ - if (!bridged && la->lle_tbl && 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; - } - - /* Check if something has changed */ - if (memcmp(&la->ll_addr, ar_sha(ah), ifp->if_addrlen) != 0 || - (la->la_flags & LLE_VALID) == 0 || - la->la_expire != time_uptime + V_arpt_keep) { - /* use afdata WLOCK to update fields */ - LLE_ADDREF(la); - LLE_WUNLOCK(la); - IF_AFDATA_CFG_WLOCK(ifp); - LLE_WLOCK(la); - /* Update data */ - IF_AFDATA_RUN_WLOCK(ifp); - memcpy(&la->ll_addr, ar_sha(ah), ifp->if_addrlen); - la->la_flags |= LLE_VALID; - la->r_flags |= RLLE_VALID; - if ((la->la_flags & LLE_STATIC) == 0) - la->la_expire = time_uptime + V_arpt_keep; - llentry_link(LLTABLE(ifp), la); - IF_AFDATA_RUN_WUNLOCK(ifp); - - IF_AFDATA_CFG_WUNLOCK(ifp); - LLE_REMREF(la); - } - - la->ln_state = ARP_LLINFO_REACHABLE; - EVENTHANDLER_INVOKE(lle_event, la, LLENTRY_RESOLVED); + IF_AFDATA_CFG_RLOCK(ifp); + la = lltable_lookup_lle(LLTABLE(ifp), LLE_EXCLUSIVE, + (struct sockaddr *)&sin); + IF_AFDATA_CFG_RUNLOCK(ifp); + if (la != NULL) + arp_update_lle(ah, isaddr, ifp, bridged, la); + else if (itaddr.s_addr == myaddr.s_addr) { - if (!(la->la_flags & LLE_STATIC)) { - wtime = V_arpt_keep - V_arp_maxtries; - if (wtime < 0) - wtime = V_arpt_keep; - - LLE_ADDREF(la); - canceled = callout_reset(&la->la_timer, - hz * wtime, arptimer, la); - if (canceled) - LLE_REMREF(la); - else - la->la_flags |= LLE_CALLOUTREF; - } - la->la_asked = 0; - la->la_preempt = V_arp_maxtries; /* - * 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; - memcpy(&sa, L3_ADDR(la), sizeof(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); + la = lltable_create_lle(LLTABLE(ifp), 0, + (struct sockaddr *)&sin); + if (la != NULL) { + IF_AFDATA_CFG_WLOCK(ifp); + LLE_WLOCK(la); + /* Let's try to search another time */ + la_tmp = lltable_lookup_lle(LLTABLE(ifp), LLE_EXCLUSIVE, + (struct sockaddr *)&sin); + if (la_tmp != NULL) { + /* + * Someone has already inserted another entry. + * Let's use it. + */ + IF_AFDATA_CFG_WUNLOCK(ifp); + arp_update_lle(ah, isaddr, ifp, bridged,la_tmp); + LLE_FREE_LOCKED(la); + } else { + /* + * Use new entry. Skip all checks, update + * immediately + */ + arp_update_lle_addr(ah, ifp, la); + IF_AFDATA_CFG_WUNLOCK(ifp); + arp_set_lle_reachable(la); + LLE_WUNLOCK(la); } - } else - LLE_WUNLOCK(la); + } } reply: if (op != ARPOP_REQUEST) @@ -1085,41 +1033,202 @@ drop: } #endif -void -arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa) +static void +arp_update_lle_addr(struct arphdr *ah, struct ifnet *ifp, struct llentry *la) { - struct llentry *lle; - if (ifa->ifa_carp != NULL) + LLE_WLOCK_ASSERT(la); + + /* Update data */ + IF_AFDATA_RUN_WLOCK(ifp); + memcpy(&la->ll_addr, ar_sha(ah), ifp->if_addrlen); + la->la_flags |= LLE_VALID; + la->r_flags |= RLLE_VALID; + if ((la->la_flags & LLE_STATIC) == 0) + la->la_expire = time_uptime + V_arpt_keep; + llentry_link(LLTABLE(ifp), la); + IF_AFDATA_RUN_WUNLOCK(ifp); +} + +static void +arp_set_lle_reachable(struct llentry *la) +{ + int canceled, wtime; + + la->ln_state = ARP_LLINFO_REACHABLE; + EVENTHANDLER_INVOKE(lle_event, la, LLENTRY_RESOLVED); + + if (!(la->la_flags & LLE_STATIC)) { + wtime = V_arpt_keep - V_arp_maxtries; + if (wtime < 0) + wtime = V_arpt_keep; + + LLE_ADDREF(la); + canceled = callout_reset(&la->la_timer, + hz * wtime, arptimer, la); + if (canceled) + LLE_REMREF(la); + else + la->la_flags |= LLE_CALLOUTREF; + } + la->la_asked = 0; + la->la_preempt = V_arp_maxtries; +} + +static void +arp_update_lle(struct arphdr *ah, struct in_addr isaddr, struct ifnet *ifp, + int bridged, struct llentry *la) +{ + struct sockaddr_in sin; + 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 && 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 || + la->la_expire != time_uptime + V_arpt_keep) { + /* Perform real LLE update */ + /* use afdata WLOCK to update fields */ + LLE_ADDREF(la); + LLE_WUNLOCK(la); + IF_AFDATA_CFG_WLOCK(ifp); + LLE_WLOCK(la); - if (ntohl(IA_SIN(ifa)->sin_addr.s_addr) != INADDR_ANY) { - arprequest(ifp, &IA_SIN(ifa)->sin_addr, - &IA_SIN(ifa)->sin_addr, IF_LLADDR(ifp)); /* - * interface address is considered static entry - * because the output of the arp utility shows - * that L2 entry as permanent + * Since we droppped LLE lock, other thread might have deleted + * this lle. Check and return */ - IF_AFDATA_CFG_WLOCK(ifp); - lle = lltable_create_lle(LLTABLE(ifp), LLE_IFADDR | LLE_STATIC, - (struct sockaddr *)IA_SIN(ifa)); - if (lle != NULL) { - IF_AFDATA_RUN_WLOCK(ifp); - bcopy(IF_LLADDR(ifp), &lle->ll_addr, ifp->if_addrlen); - lle->la_flags |= (LLE_VALID | LLE_STATIC); - lle->r_flags |= RLLE_VALID; - llentry_link(LLTABLE(ifp), lle); - IF_AFDATA_RUN_WUNLOCK(ifp); + if ((la->la_flags & LLE_DELETED) != 0) { + IF_AFDATA_CFG_WUNLOCK(ifp); + LLE_FREE_LOCKED(la); + return; } + + /* Update data */ + arp_update_lle_addr(ah, ifp, la); + IF_AFDATA_CFG_WUNLOCK(ifp); - if (lle == NULL) - log(LOG_INFO, "arp_ifinit: cannot create arp " - "entry for interface address\n"); - else - LLE_WUNLOCK(lle); + LLE_REMREF(la); } + + arp_set_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; + memcpy(&sin, L3_ADDR(la), sizeof(sin)); + 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, (struct sockaddr *)&sin, NULL); + } + } else + LLE_WUNLOCK(la); +} + +void +arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa) +{ + struct llentry *lle; + struct in_addr addr; + + if (ifa->ifa_carp != NULL) + return; + ifa->ifa_rtrequest = NULL; + addr = IA_SIN(ifa)->sin_addr; + + if (ntohl(addr.s_addr) == INADDR_ANY) { + /* XXX-ME why? */ + return; + } + + arprequest(ifp, &addr, &addr, IF_LLADDR(ifp)); + + /* + * interface address is considered static entry + * because the output of the arp utility shows + * that L2 entry as permanent + */ + lle = lltable_create_lle(LLTABLE(ifp), LLE_IFADDR | LLE_STATIC, + (struct sockaddr *)IA_SIN(ifa)); + if (lle == NULL) { + log(LOG_INFO, "arp_ifinit: cannot create arp " + "entry for interface address\n"); + return; + } + + IF_AFDATA_CFG_WLOCK(ifp); + + /* Lock or new shiny lle */ + LLE_WLOCK(lle); + + /* + * Check if we already have some corresponding entry. + * Instead of dealing with callouts/flags/etc we simply + * delete it and add new one. + */ + lltable_delete_lle(LLTABLE(ifp), LLE_IFADDR, + (struct sockaddr *)IA_SIN(ifa)); + + IF_AFDATA_RUN_WLOCK(ifp); + bcopy(IF_LLADDR(ifp), &lle->ll_addr, ifp->if_addrlen); + lle->la_flags |= (LLE_VALID | LLE_STATIC); + lle->r_flags |= RLLE_VALID; + llentry_link(LLTABLE(ifp), lle); + IF_AFDATA_RUN_WUNLOCK(ifp); + + IF_AFDATA_CFG_WUNLOCK(ifp); + LLE_WUNLOCK(lle); } void Modified: projects/routing/sys/netinet/in.c ============================================================================== --- projects/routing/sys/netinet/in.c Mon Dec 1 21:33:28 2014 (r275381) +++ projects/routing/sys/netinet/in.c Mon Dec 1 21:43:48 2014 (r275382) @@ -1036,7 +1036,7 @@ in_lltable_new(const struct sockaddr *l3 } static void -in_lltable_stop_timers(struct llentry *lle) +in_lltable_stop_timers(struct llentry *lle) { LLE_WLOCK_ASSERT(lle); @@ -1181,23 +1181,12 @@ in_lltable_delete(struct lltable *llt, u static struct llentry * in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { - const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr; struct ifnet *ifp = llt->llt_ifp; struct llentry *lle; - IF_AFDATA_CFG_WLOCK_ASSERT(ifp); KASSERT(l3addr->sa_family == AF_INET, ("sin_family %d", l3addr->sa_family)); - lle = in_lltable_find_dst(llt, sin->sin_addr); - - if (lle != NULL) { - LLE_WLOCK(lle); - return (lle); - } - - /* no existing record, we need to create new one */ - /* * A route that covers the given address must have * been installed 1st because we are doing a resolution, @@ -1213,7 +1202,6 @@ in_lltable_create(struct lltable *llt, u return (NULL); } lle->la_flags = flags; - LLE_WLOCK(lle); return (lle); } Modified: projects/routing/sys/netinet6/in6.c ============================================================================== --- projects/routing/sys/netinet6/in6.c Mon Dec 1 21:33:28 2014 (r275381) +++ projects/routing/sys/netinet6/in6.c Mon Dec 1 21:43:48 2014 (r275382) @@ -2223,7 +2223,6 @@ in6_lltable_create(struct lltable *llt, struct ifnet *ifp = llt->llt_ifp; struct llentry *lle; - IF_AFDATA_CFG_WLOCK_ASSERT(ifp); KASSERT(l3addr->sa_family == AF_INET6, ("sin_family %d", l3addr->sa_family));