Index: sys/netinet6/in6.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6.c,v retrieving revision 1.194 diff -p -u -r1.194 in6.c --- sys/netinet6/in6.c 12 Dec 2015 23:34:25 -0000 1.194 +++ sys/netinet6/in6.c 4 Feb 2016 04:58:37 -0000 @@ -84,6 +84,7 @@ __KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.19 #include #include #include +#include #include #include @@ -209,7 +210,7 @@ in6_ifremlocal(struct ifaddr *ifa) * XXX agree, especially now that I have fixed the dangling * XXX ifp-pointers bug. */ - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + IN6_IFADDR_FOREACH(ia) { if (!IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) continue; if (ia->ia_ifp != ifa->ifa_ifp) @@ -922,12 +923,12 @@ in6_update_ifa1(struct ifnet *ifp, struc (struct sockaddr *)&ia->ia_prefixmask; ia->ia_ifp = ifp; - if ((oia = in6_ifaddr) != NULL) { - for ( ; oia->ia_next; oia = oia->ia_next) + if ((oia = _in6_ifaddr) != NULL) { + for ( ; oia->_ia_next; oia = oia->_ia_next) continue; - oia->ia_next = ia; + oia->_ia_next = ia; } else - in6_ifaddr = ia; + _in6_ifaddr = ia; /* gain a refcnt for the link from in6_ifaddr */ ifaref(&ia->ia_ifa); @@ -1296,11 +1297,11 @@ in6_update_ifa(struct ifnet *ifp, struct return rc; } -void -in6_purgeaddr(struct ifaddr *ifa) +static void +in6_purgeaddr1(struct in6_ifaddr *ia) { + struct ifaddr *ifa = &ia->ia_ifa; struct ifnet *ifp = ifa->ifa_ifp; - struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; struct in6_multi_mship *imm; /* stop DAD processing */ @@ -1339,6 +1340,89 @@ in6_purgeaddr(struct ifaddr *ifa) in6_unlink_ifa(ia, ifp); } +static struct in6_purge { + kmutex_t lock; + kcondvar_t cv; + struct workqueue *wq; +} in6_purge __cacheline_aligned; + +static bool +in6_purge_acquire(struct in6_ifaddr *ia) +{ + bool acquired = false; + + mutex_enter(&in6_purge.lock); + if (ia->ia6_purging) { + do { + cv_wait(&in6_purge.cv, &in6_purge.lock); + } while (ia->ia6_purging); + goto out; + } + ia->ia6_purging = true; + acquired = true; +out: mutex_exit(&in6_purge.lock); + return acquired; +} + +static void +in6_purge_release(struct in6_ifaddr *ia) +{ + + mutex_enter(&in6_purge.lock); + KASSERT(ia->ia6_purging); + ia->ia6_purging = false; + cv_broadcast(&in6_purge.cv); + mutex_exit(&in6_purge.lock); +} + +static void +in6_purgeaddr_work(struct work *wk, void *cookie __unused) +{ + struct in6_ifaddr *ia = container_of(wk, struct in6_ifaddr, + ia6_purge_work); + + KASSERT(ia->ia6_purging); + in6_purgeaddr1(ia); + in6_purge_release(ia); +} + +int +in6_purge_init(void) +{ + int error; + + error = workqueue_create(&in6_purge.wq, "in6purgeq", + &in6_purgeaddr_work, NULL, PRI_NONE, IPL_SOFTNET, 0); + if (error) + return error; + + mutex_init(&in6_purge.lock, MUTEX_DEFAULT, IPL_SOFTNET); + cv_init(&in6_purge.cv, "in6purge"); + + return 0; +} + +void +in6_purgeaddr(struct ifaddr *ifa) +{ + struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; + + if (!in6_purge_acquire(ia)) + return; + in6_purgeaddr1(ia); + in6_purge_release(ia); +} + +void +in6_purgeaddr_async(struct ifaddr *ifa) +{ + struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; + + if (!in6_purge_acquire(ia)) + return; + workqueue_enqueue(in6_purge.wq, &ia->ia6_purge_work, NULL); +} + static void in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { @@ -1348,13 +1432,13 @@ in6_unlink_ifa(struct in6_ifaddr *ia, st ifa_remove(ifp, &ia->ia_ifa); oia = ia; - if (oia == (ia = in6_ifaddr)) - in6_ifaddr = ia->ia_next; + if (oia == (ia = _in6_ifaddr)) + _in6_ifaddr = ia->_ia_next; else { - while (ia->ia_next && (ia->ia_next != oia)) - ia = ia->ia_next; - if (ia->ia_next) - ia->ia_next = oia->ia_next; + while (ia->_ia_next && (ia->_ia_next != oia)) + ia = ia->_ia_next; + if (ia->_ia_next) + ia->_ia_next = oia->_ia_next; else { /* search failed */ printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n"); @@ -1784,7 +1868,7 @@ in6ifa_ifwithaddr(const struct in6_addr IN6_IFADDR_RLOCK(); LIST_FOREACH(ia, IN6ADDR_HASH(addr), ia6_hash) { #else - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + IN6_IFADDR_FOREACH(ia) { #endif if (IN6_ARE_ADDR_EQUAL(IA6_IN6(ia), addr)) { if (zoneid != 0 && @@ -1855,7 +1939,7 @@ in6_localaddr(const struct in6_addr *in6 if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) return 1; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) + IN6_IFADDR_FOREACH(ia) if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, &ia->ia_prefixmask.sin6_addr)) return 1; @@ -1868,7 +1952,7 @@ in6_is_addr_deprecated(struct sockaddr_i { struct in6_ifaddr *ia; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + IN6_IFADDR_FOREACH(ia) { if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &sa6->sin6_addr) && #ifdef SCOPEDROUTING Index: sys/netinet6/in6_ifattach.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_ifattach.c,v retrieving revision 1.95 diff -p -u -r1.95 in6_ifattach.c --- sys/netinet6/in6_ifattach.c 23 Feb 2015 19:15:59 -0000 1.95 +++ sys/netinet6/in6_ifattach.c 4 Feb 2016 04:58:37 -0000 @@ -246,7 +246,7 @@ generate_tmp_ifid(u_int8_t *seed0, const else { struct in6_ifaddr *ia; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + IN6_IFADDR_FOREACH(ia) { if (!memcmp(&ia->ia_addr.sin6_addr.s6_addr[8], ret, 8)) { badid = 1; @@ -891,13 +891,13 @@ in6_ifdetach(struct ifnet *ifp) /* also remove from the IPv6 address chain(itojun&jinmei) */ oia = ia; - if (oia == (ia = in6_ifaddr)) - in6_ifaddr = ia->ia_next; + if (oia == (ia = _in6_ifaddr)) + _in6_ifaddr = ia->_ia_next; else { - while (ia->ia_next && (ia->ia_next != oia)) - ia = ia->ia_next; - if (ia->ia_next) - ia->ia_next = oia->ia_next; + while (ia->_ia_next && (ia->_ia_next != oia)) + ia = ia->_ia_next; + if (ia->_ia_next) + ia->_ia_next = oia->_ia_next; else { nd6log((LOG_ERR, "%s: didn't unlink in6ifaddr from list\n", Index: sys/netinet6/in6_proto.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_proto.c,v retrieving revision 1.110 diff -p -u -r1.110 in6_proto.c --- sys/netinet6/in6_proto.c 21 Jan 2016 15:41:30 -0000 1.110 +++ sys/netinet6/in6_proto.c 4 Feb 2016 04:58:37 -0000 @@ -425,6 +425,11 @@ bool in6_present = false; static void in6_init(void) { + int error; + + error = in6_purge_init(); + if (error) + panic("failed to initialize in6 purge async: error=%d", error); in6_present = true; } Index: sys/netinet6/in6_src.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_src.c,v retrieving revision 1.59 diff -p -u -r1.59 in6_src.c --- sys/netinet6/in6_src.c 12 Dec 2015 23:34:25 -0000 1.59 +++ sys/netinet6/in6_src.c 4 Feb 2016 04:58:37 -0000 @@ -287,7 +287,7 @@ in6_selectsrc(struct sockaddr_in6 *dstso if (*errorp != 0) return (NULL); - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + IN6_IFADDR_FOREACH(ia) { int new_scope = -1, new_matchlen = -1; struct in6_addrpolicy *new_policy = NULL; u_int32_t srczone, osrczone, dstzone; Index: sys/netinet6/in6_var.h =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_var.h,v retrieving revision 1.76 diff -p -u -r1.76 in6_var.h --- sys/netinet6/in6_var.h 4 Feb 2016 02:48:37 -0000 1.76 +++ sys/netinet6/in6_var.h 4 Feb 2016 04:58:37 -0000 @@ -67,6 +67,10 @@ #include #include +#ifdef _KERNEL +#include +#endif + /* * Interface address, Internet version. One of these structures * is allocated for each interface with an Internet address. @@ -111,7 +115,7 @@ struct in6_ifaddr { struct sockaddr_in6 ia_dstaddr; /* space for destination addr */ struct sockaddr_in6 ia_prefixmask; /* prefix mask */ u_int32_t ia_plen; /* prefix length */ - struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */ + struct in6_ifaddr *_ia_next; /* next in6 list of IP6 addresses */ struct in6_multihead ia6_multiaddrs; /* list of multicast addresses */ int ia6_flags; @@ -127,6 +131,12 @@ struct in6_ifaddr { /* multicast addresses joined from the kernel */ LIST_HEAD(, in6_multi_mship) ia6_memberships; + +#ifdef _KERNEL + /* asynchronous purge work, locked by in6_purge.lock */ + struct work ia6_purge_work; + bool ia6_purging; +#endif }; /* control structure to manage address selection policy */ @@ -490,7 +500,41 @@ extern pktqueue_t *ip6_pktq; MALLOC_DECLARE(M_IP6OPT); -extern struct in6_ifaddr *in6_ifaddr; +extern struct in6_ifaddr *_in6_ifaddr; + +static inline struct in6_ifaddr * +in6_ifaddr_skip(struct in6_ifaddr *ia6) +{ + + for (; ia6 != NULL; ia6 = ia6->_ia_next) + if (!ia6->ia6_purging) + return ia6; + return NULL; +} + +static inline struct in6_ifaddr * +in6_ifaddr_first(void) +{ + + return in6_ifaddr_skip(_in6_ifaddr); +} + +static inline struct in6_ifaddr * +in6_ifaddr_next(struct in6_ifaddr *ia6) +{ + + KASSERT(ia6 != NULL); + return in6_ifaddr_skip(ia6->_ia_next); +} + +#define IN6_IFADDR_FOREACH(ia6) \ + for ((ia6) = _in6_ifaddr; (ia6) != NULL; (ia6) = (ia6)->_ia_next) \ + if (!(ia6)->ia6_purging) +#define IN6_IFADDR_FOREACH_SAFE(ia6, nia6) \ + for ((ia6) = _in6_ifaddr; \ + ((ia6) != NULL ? ((nia6) = (ia6)->_ia_next, 1) : 0); \ + (ia6) = (nia6)) \ + if (!(ia6)->ia6_purging) #define in6_ifstat_inc(ifp, tag) \ do { \ @@ -614,7 +658,7 @@ in6_next_multi(struct in6_multistep *ste } while (step->i_ia != NULL) { in6m = LIST_FIRST(&step->i_ia->ia6_multiaddrs); - step->i_ia = step->i_ia->ia_next; + step->i_ia = in6_ifaddr_next(step->i_ia); if (in6m != NULL) { step->i_in6m = LIST_NEXT(in6m, in6m_entry); break; @@ -625,10 +669,11 @@ in6_next_multi(struct in6_multistep *ste static inline struct in6_multi * in6_first_multi(struct in6_multistep *step) -{ - step->i_ia = in6_ifaddr; - step->i_in6m = NULL; - return in6_next_multi(step); +{ + + step->i_ia = in6_ifaddr_first(); + step->i_in6m = NULL; + return in6_next_multi(step); } #define IN6_NEXT_MULTI(__step, __in6m) \ @@ -688,7 +733,9 @@ int in6_mask2len(struct in6_addr *, u_ch int in6_control(struct socket *, u_long, void *, struct ifnet *); int in6_update_ifa(struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *, int); +int in6_purge_init(void); void in6_purgeaddr(struct ifaddr *); +void in6_purgeaddr_async(struct ifaddr *); void in6_purgeif(struct ifnet *); void in6_savemkludge(struct in6_ifaddr *); void in6_setmaxmtu (void); Index: sys/netinet6/ip6_input.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/ip6_input.c,v retrieving revision 1.155 diff -p -u -r1.155 ip6_input.c --- sys/netinet6/ip6_input.c 4 Feb 2016 02:48:37 -0000 1.155 +++ sys/netinet6/ip6_input.c 4 Feb 2016 04:58:37 -0000 @@ -133,7 +133,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip6_input.c, extern struct domain inet6domain; u_char ip6_protox[IPPROTO_MAX]; -struct in6_ifaddr *in6_ifaddr; +struct in6_ifaddr *_in6_ifaddr; pktqueue_t *ip6_pktq __read_mostly; int ip6_forward_srcrt; /* XXX */ Index: sys/netinet6/mld6.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/mld6.c,v retrieving revision 1.64 diff -p -u -r1.64 mld6.c --- sys/netinet6/mld6.c 12 Nov 2015 15:01:06 -0000 1.64 +++ sys/netinet6/mld6.c 4 Feb 2016 04:58:37 -0000 @@ -739,7 +739,7 @@ in6_delmulti(struct in6_multi *in6m) * Delete all references of this multicasting group from * the membership arrays */ - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + IN6_IFADDR_FOREACH(ia) { struct in6_multi_mship *imm; LIST_FOREACH(imm, &ia->ia6_memberships, i6mm_chain) { if (imm->i6mm_maddr == in6m) Index: sys/netinet6/nd6.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/nd6.c,v retrieving revision 1.185 diff -p -u -r1.185 nd6.c --- sys/netinet6/nd6.c 4 Feb 2016 02:48:37 -0000 1.185 +++ sys/netinet6/nd6.c 4 Feb 2016 04:58:37 -0000 @@ -620,8 +620,7 @@ nd6_timer(void *ignored_arg) * rather separate address lifetimes and prefix lifetimes. */ addrloop: - for (ia6 = in6_ifaddr; ia6; ia6 = nia6) { - nia6 = ia6->ia_next; + IN6_IFADDR_FOREACH_SAFE(ia6, nia6) { /* check address lifetime */ if (IFA6_IS_INVALID(ia6)) { int regen = 0; @@ -642,7 +641,7 @@ nd6_timer(void *ignored_arg) regen = 1; } - in6_purgeaddr(&ia6->ia_ifa); + in6_purgeaddr_async(&ia6->ia_ifa); if (regen) goto addrloop; /* XXX: see below */ @@ -1908,10 +1907,7 @@ nd6_ioctl(u_long cmd, void *data, struct continue; /* XXX */ /* do we really have to remove addresses as well? */ - for (ia = in6_ifaddr; ia; ia = ia_next) { - /* ia might be removed. keep the next ptr. */ - ia_next = ia->ia_next; - + IN6_IFADDR_FOREACH_SAFE(ia, ia_next) { if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; Index: sys/netinet6/nd6_rtr.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/nd6_rtr.c,v retrieving revision 1.105 diff -p -u -r1.105 nd6_rtr.c --- sys/netinet6/nd6_rtr.c 25 Nov 2015 06:21:26 -0000 1.105 +++ sys/netinet6/nd6_rtr.c 4 Feb 2016 04:58:38 -0000 @@ -1536,7 +1536,7 @@ pfxlist_onlink_check(void) * always be attached. * The precise detection logic is same as the one for prefixes. */ - for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + IN6_IFADDR_FOREACH(ifa) { if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF)) continue; @@ -1553,7 +1553,7 @@ pfxlist_onlink_check(void) break; } if (ifa) { - for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + IN6_IFADDR_FOREACH(ifa) { if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; @@ -1580,7 +1580,7 @@ pfxlist_onlink_check(void) } } else { - for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + IN6_IFADDR_FOREACH(ifa) { if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; @@ -1942,7 +1942,7 @@ in6_tmpifadd( * there may be a time lag between generation of the ID and generation * of the address. So, we'll do one more sanity check. */ - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + IN6_IFADDR_FOREACH(ia) { if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &ifra.ifra_addr.sin6_addr)) { if (trylimit-- == 0) { Index: sys/netipsec/key.c =================================================================== RCS file: /cvsroot/src/sys/netipsec/key.c,v retrieving revision 1.91 diff -p -u -r1.91 key.c --- sys/netipsec/key.c 16 Jun 2014 03:34:45 -0000 1.91 +++ sys/netipsec/key.c 4 Feb 2016 04:58:38 -0000 @@ -4138,7 +4138,7 @@ key_ismyaddr6(const struct sockaddr_in6 const struct in6_ifaddr *ia; const struct in6_multi *in6m; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + IN6_IFADDR_FOREACH(ia) { if (key_sockaddrcmp((const struct sockaddr *)&sin6, (const struct sockaddr *)&ia->ia_addr, 0) == 0) return 1;