! ! MFp4 bz_ipv6_fast: ! ! Greatly reduce the scope of scope6 lock coverage where not needed. ! Add comments to help figuring out caller context more easily. ! ! Split in6_setscope() into an unlocked wrapper and a _locked() ! version. While this was mostly for analysing performance ! difference from external callers it also turned out a nice split ! adding the shortcut `not interested in any results in these cases'. ! ! In in6_clearscope() do not do a write unless needed given we check ! for the condition already. ! ! Sponsored by: The FreeBSD Foundation ! Sponsored by: iXsystems ! ! Reviewed by: gnn (along with the big batch) ! MFC After: 6 weeks ! --- sys/netinet6/scope6.c.orig 2012-04-25 07:34:21.000000000 +0000 +++ sys/netinet6/scope6.c 2012-05-02 12:00:20.000000000 +0000 @@ -114,6 +114,9 @@ scope6_ifdetach(struct scope6_id *sid) free(sid, M_IFADDR); } +/* + * Called from SIOCSSCOPE6. + */ int scope6_set(struct ifnet *ifp, struct scope6_id *idlist) { @@ -139,7 +142,6 @@ scope6_set(struct ifnet *ifp, struct sco * interface addresses, routing table entries, PCB entries... */ - SCOPE6_LOCK(); for (i = 0; i < 16; i++) { if (idlist->s6id_list[i] && idlist->s6id_list[i] != sid->s6id_list[i]) { @@ -150,7 +152,6 @@ scope6_set(struct ifnet *ifp, struct sco if (i == IPV6_ADDR_SCOPE_INTFACELOCAL && idlist->s6id_list[i] != ifp->if_index) { IF_AFDATA_UNLOCK(ifp); - SCOPE6_UNLOCK(); return (EINVAL); } @@ -163,7 +164,6 @@ scope6_set(struct ifnet *ifp, struct sco * safety in later use. */ IF_AFDATA_UNLOCK(ifp); - SCOPE6_UNLOCK(); return (EINVAL); } @@ -175,7 +175,6 @@ scope6_set(struct ifnet *ifp, struct sco sid->s6id_list[i] = idlist->s6id_list[i]; } } - SCOPE6_UNLOCK(); IF_AFDATA_UNLOCK(ifp); return (error); @@ -193,9 +192,7 @@ scope6_get(struct ifnet *ifp, struct sco return (EINVAL); } - SCOPE6_LOCK(); *idlist = *sid; - SCOPE6_UNLOCK(); IF_AFDATA_UNLOCK(ifp); return (0); @@ -265,9 +262,9 @@ in6_addrscope(struct in6_addr *addr) } /* - * ifp - note that this might be NULL + * Only called from nd6_setdefaultiface(). + * ifp - note that this might be NULL. */ - void scope6_setdefault(struct ifnet *ifp) { @@ -291,6 +288,9 @@ scope6_setdefault(struct ifnet *ifp) SCOPE6_UNLOCK(); } +/* + * Only called for SIOCGSCOPE6DEF. + */ int scope6_get_default(struct scope6_id *idlist) { @@ -407,23 +407,15 @@ sa6_recoverscope(struct sockaddr_in6 *si * * ret_id - unnecessary? */ -int -in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) +static int +in6_setscope_locked(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) { int scope; u_int32_t zoneid = 0; struct scope6_id *sid; - IF_AFDATA_LOCK(ifp); - sid = SID(ifp); - -#ifdef DIAGNOSTIC - if (sid == NULL) { /* should not happen */ - panic("in6_setscope: scope array is NULL"); - /* NOTREACHED */ - } -#endif + KASSERT(sid != NULL, ("%s: ipf %p scope array is NULL", __func__, ifp)); /* * special case: the loopback address can only belong to a loopback @@ -431,19 +423,16 @@ in6_setscope(struct in6_addr *in6, struc */ if (IN6_IS_ADDR_LOOPBACK(in6)) { if (!(ifp->if_flags & IFF_LOOPBACK)) { - IF_AFDATA_UNLOCK(ifp); return (EINVAL); } else { if (ret_id != NULL) *ret_id = 0; /* there's no ambiguity */ - IF_AFDATA_UNLOCK(ifp); return (0); } } scope = in6_addrscope(in6); - SCOPE6_LOCK(); switch (scope) { case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */ zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL]; @@ -465,8 +454,6 @@ in6_setscope(struct in6_addr *in6, struc zoneid = 0; /* XXX: treat as global. */ break; } - SCOPE6_UNLOCK(); - IF_AFDATA_UNLOCK(ifp); if (ret_id != NULL) *ret_id = zoneid; @@ -477,6 +464,26 @@ in6_setscope(struct in6_addr *in6, struc return (0); } +int +in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) +{ + int error; + + /* + * In case we will not do anything, do an immediate return. This avoids + * taking the (rather expensive) lock. + */ + if (!IN6_IS_SCOPE_LINKLOCAL(in6) && !IN6_IS_ADDR_MC_INTFACELOCAL(in6) && + !IN6_IS_ADDR_LOOPBACK(in6) && ret_id == NULL) + return (0); + + IF_AFDATA_RLOCK(ifp); + error = in6_setscope_locked(in6, ifp, ret_id); + IF_AFDATA_RUNLOCK(ifp); + + return (error); +} + /* * Just clear the embedded scope identifier. Return 0 if the original address * is intact; return non 0 if the address is modified. @@ -487,9 +494,10 @@ in6_clearscope(struct in6_addr *in6) int modified = 0; if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { - if (in6->s6_addr16[1] != 0) + if (in6->s6_addr16[1] != 0) { modified = 1; - in6->s6_addr16[1] = 0; + in6->s6_addr16[1] = 0; + } } return (modified);