Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F150193605
D2254.id4933.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D2254.id4933.diff
View Options
Index: sys/netinet6/in6.c
===================================================================
--- sys/netinet6/in6.c
+++ sys/netinet6/in6.c
@@ -1334,21 +1334,35 @@
in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
{
char ip6buf[INET6_ADDRSTRLEN];
+ struct in6_ifaddr *iatmp;
- IF_ADDR_WLOCK(ifp);
- TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
- IF_ADDR_WUNLOCK(ifp);
- ifa_free(&ia->ia_ifa); /* if_addrhead */
+ IN6_IFADDR_WLOCK();
+ /*
+ * Now that we have the exclusive lock, make sure we're not racing with
+ * a different thread that's attempting to unlink the same address.
+ */
+ LIST_FOREACH(iatmp, IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia6_hash) {
+ if (iatmp == ia)
+ break;
+ }
+ if (iatmp == NULL) {
+ IN6_IFADDR_WUNLOCK();
+ return;
+ }
+ TAILQ_REMOVE(&V_in6_ifaddrhead, ia, ia_link);
+ LIST_REMOVE(ia, ia6_hash);
+ IN6_IFADDR_WUNLOCK();
/*
* Defer the release of what might be the last reference to the
* in6_ifaddr so that it can't be freed before the remainder of the
* cleanup.
*/
- IN6_IFADDR_WLOCK();
- TAILQ_REMOVE(&V_in6_ifaddrhead, ia, ia_link);
- LIST_REMOVE(ia, ia6_hash);
- IN6_IFADDR_WUNLOCK();
+
+ IF_ADDR_WLOCK(ifp);
+ TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
+ IF_ADDR_WUNLOCK(ifp);
+ ifa_free(&ia->ia_ifa); /* if_addrhead */
/*
* Release the reference to the base prefix. There should be a
Index: sys/netinet6/in6_var.h
===================================================================
--- sys/netinet6/in6_var.h
+++ sys/netinet6/in6_var.h
@@ -119,7 +119,6 @@
struct sockaddr_in6 ia_net; /* network number of interface */
struct sockaddr_in6 ia_dstaddr; /* space for destination addr */
struct sockaddr_in6 ia_prefixmask; /* prefix mask */
- u_int32_t ia_plen; /* prefix length */
TAILQ_ENTRY(in6_ifaddr) ia_link; /* list of IPv6 addresses */
int ia6_flags;
@@ -136,6 +135,8 @@
LIST_HEAD(, in6_multi_mship) ia6_memberships;
/* entry in bucket of inet6 addresses */
LIST_ENTRY(in6_ifaddr) ia6_hash;
+ /* expiring address list, only to be used by nd6_timer() */
+ SLIST_ENTRY(in6_ifaddr) ia6_expiring;
};
/* List of in6_ifaddr's. */
Index: sys/netinet6/nd6.c
===================================================================
--- sys/netinet6/nd6.c
+++ sys/netinet6/nd6.c
@@ -112,11 +112,6 @@
VNET_DEFINE(int, nd6_debug) = 0;
#endif
-/* for debugging? */
-#if 0
-static int nd6_inuse, nd6_allocated;
-#endif
-
VNET_DEFINE(struct nd_drhead, nd_defrouter);
VNET_DEFINE(struct nd_prhead, nd_prefix);
@@ -606,7 +601,7 @@
/*
- * ND6 timer routine to expire default route list and prefix list
+ * ND6 timer routine to expire default router, address, and prefix list entries.
*/
void
nd6_timer(void *arg)
@@ -615,6 +610,7 @@
struct nd_defrouter *dr, *ndr;
struct nd_prefix *pr, *npr;
struct in6_ifaddr *ia6, *nia6;
+ SLIST_HEAD(, in6_ifaddr) topurge;
callout_reset(&V_nd6_timer_ch, V_nd6_prune * hz,
nd6_timer, curvnet);
@@ -631,15 +627,27 @@
* However, from a stricter speci-confrmance standpoint, we should
* rather separate address lifetimes and prefix lifetimes.
*
- * XXXRW: in6_ifaddrhead locking.
+ * Expired address are placed in the topurge list and are freed only
+ * after we have scanned the full list to avoid acquiring the address
+ * list write lock more often than is necessary.
*/
+
+ SLIST_INIT(&topurge);
+
addrloop:
- TAILQ_FOREACH_SAFE(ia6, &V_in6_ifaddrhead, ia_link, nia6) {
+ IN6_IFADDR_RLOCK();
+ TAILQ_FOREACH(ia6, &V_in6_ifaddrhead, ia_link) {
/* check address lifetime */
if (IFA6_IS_INVALID(ia6)) {
int regen = 0;
/*
+ * regen_tmpaddr() may release the address list lock, so
+ * take a reference before calling it.
+ */
+ ifa_ref(&ia6->ia_ifa);
+
+ /*
* If the expiring address is temporary, try
* regenerating a new one. This would be useful when
* we suspended a laptop PC, then turned it on after a
@@ -655,7 +663,11 @@
regen = 1;
}
- in6_purgeaddr(&ia6->ia_ifa);
+ /*
+ * The ia6_expiring field is reserved for use by
+ * nd6_timer(), and so is protected by the callout lock.
+ */
+ SLIST_INSERT_HEAD(&topurge, ia6, ia6_expiring);
if (regen)
goto addrloop; /* XXX: see below */
@@ -696,6 +708,12 @@
ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
}
}
+ IN6_IFADDR_RUNLOCK();
+
+ SLIST_FOREACH_SAFE(ia6, &topurge, ia6_expiring, nia6) {
+ in6_purgeaddr(&ia6->ia_ifa);
+ ifa_free(&ia6->ia_ifa);
+ }
/* expire prefix list */
LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) {
@@ -719,6 +737,9 @@
/*
* ia6 - deprecated/invalidated temporary address
+ *
+ * Must be called with the in6_ifaddr read lock held. The lock is released if we
+ * add a new address, in which case 0 is returned.
*/
static int
regen_tmpaddr(struct in6_ifaddr *ia6)
@@ -727,6 +748,8 @@
struct ifnet *ifp;
struct in6_ifaddr *public_ifa6 = NULL;
+ IN6_IFADDR_RLOCK_ASSERT();
+
ifp = ia6->ia_ifa.ifa_ifp;
IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
@@ -774,12 +797,10 @@
if (public_ifa6 != NULL) {
int e;
- if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0) {
- ifa_free(&public_ifa6->ia_ifa);
- log(LOG_NOTICE, "regen_tmpaddr: failed to create a new"
- " tmp addr,errno=%d\n", e);
- return (-1);
- }
+ IN6_IFADDR_RUNLOCK();
+ if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0)
+ log(LOG_NOTICE,
+ "regen_tmpaddr: failed to create a new tmp addr, errno=%d\n", e);
ifa_free(&public_ifa6->ia_ifa);
return (0);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Mar 31, 4:17 AM (17 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30585026
Default Alt Text
D2254.id4933.diff (5 KB)
Attached To
Mode
D2254: Fix a number of issues with in6 ifaddr list locking.
Attached
Detach File
Event Timeline
Log In to Comment