From owner-svn-src-projects@freebsd.org Sat Aug 29 13:44:29 2015 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 949EA9C5021 for ; Sat, 29 Aug 2015 13:44:29 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from repo.freebsd.org (repo.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 8498B15A; Sat, 29 Aug 2015 13:44:29 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.70]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id t7TDiTOG032415; Sat, 29 Aug 2015 13:44:29 GMT (envelope-from melifaro@FreeBSD.org) Received: (from melifaro@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id t7TDiS1s032411; Sat, 29 Aug 2015 13:44:28 GMT (envelope-from melifaro@FreeBSD.org) Message-Id: <201508291344.t7TDiS1s032411@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: melifaro set sender to melifaro@FreeBSD.org using -f From: "Alexander V. Chernikov" Date: Sat, 29 Aug 2015 13:44:28 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r287291 - in projects/routing/sys: net netinet 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.20 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: Sat, 29 Aug 2015 13:44:29 -0000 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