Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F115762725
D31826.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D31826.diff
View Options
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -84,9 +84,9 @@
static void nd6_dad_add(struct dadq *dp);
static void nd6_dad_del(struct dadq *dp);
static void nd6_dad_rele(struct dadq *);
-static void nd6_dad_starttimer(struct dadq *, int, int);
+static void nd6_dad_starttimer(struct dadq *, int);
static void nd6_dad_stoptimer(struct dadq *);
-static void nd6_dad_timer(struct dadq *);
+static void nd6_dad_timer(void *);
static void nd6_dad_duplicated(struct ifaddr *, struct dadq *);
static void nd6_dad_ns_output(struct dadq *);
static void nd6_dad_ns_input(struct ifaddr *, struct nd_opt_nonce *);
@@ -1135,26 +1135,31 @@
#define V_dadq VNET(dadq)
#define V_dad_rwlock VNET(dad_rwlock)
-#define DADQ_RLOCK() rw_rlock(&V_dad_rwlock)
-#define DADQ_RUNLOCK() rw_runlock(&V_dad_rwlock)
-#define DADQ_WLOCK() rw_wlock(&V_dad_rwlock)
-#define DADQ_WUNLOCK() rw_wunlock(&V_dad_rwlock)
+#define DADQ_LOCKPTR() (&V_dad_rwlock)
+#define DADQ_LOCK_INIT() rw_init(DADQ_LOCKPTR(), "nd6 DAD queue")
+#define DADQ_RLOCK() rw_rlock(DADQ_LOCKPTR())
+#define DADQ_RUNLOCK() rw_runlock(DADQ_LOCKPTR())
+#define DADQ_WLOCK() rw_wlock(DADQ_LOCKPTR())
+#define DADQ_WUNLOCK() rw_wunlock(DADQ_LOCKPTR())
+
+#define DADQ_LOCK_ASSERT() rw_assert(DADQ_LOCKPTR(), RA_LOCKED);
+#define DADQ_RLOCK_ASSERT() rw_assert(DADQ_LOCKPTR(), RA_RLOCKED);
+#define DADQ_WLOCK_ASSERT() rw_assert(DADQ_LOCKPTR(), RA_WLOCKED);
static void
nd6_dad_add(struct dadq *dp)
{
+ DADQ_WLOCK_ASSERT();
- DADQ_WLOCK();
TAILQ_INSERT_TAIL(&V_dadq, dp, dad_list);
dp->dad_ondadq = true;
- DADQ_WUNLOCK();
}
static void
nd6_dad_del(struct dadq *dp)
{
+ DADQ_WLOCK_ASSERT();
- DADQ_WLOCK();
if (dp->dad_ondadq) {
/*
* Remove dp from the dadq and release the dadq's
@@ -1162,10 +1167,8 @@
*/
TAILQ_REMOVE(&V_dadq, dp, dad_list);
dp->dad_ondadq = false;
- DADQ_WUNLOCK();
nd6_dad_rele(dp);
- } else
- DADQ_WUNLOCK();
+ }
}
static struct dadq *
@@ -1173,10 +1176,12 @@
{
struct dadq *dp;
- DADQ_RLOCK();
+ DADQ_LOCK_ASSERT();
+
TAILQ_FOREACH(dp, &V_dadq, dad_list) {
if (dp->dad_ifa != ifa)
continue;
+
/*
* Skip if the nonce matches the received one.
* +2 in the length is required because of type and
@@ -1185,42 +1190,35 @@
if (n != NULL &&
n->nd_opt_nonce_len == (ND_OPT_NONCE_LEN + 2) / 8 &&
memcmp(&n->nd_opt_nonce[0], &dp->dad_nonce[0],
- ND_OPT_NONCE_LEN) == 0) {
+ ND_OPT_NONCE_LEN) == 0) {
dp->dad_ns_lcount++;
continue;
}
- refcount_acquire(&dp->dad_refcnt);
break;
}
- DADQ_RUNLOCK();
return (dp);
}
static void
-nd6_dad_starttimer(struct dadq *dp, int ticks, int send_ns)
+nd6_dad_starttimer(struct dadq *dp, int ticks)
{
+ DADQ_WLOCK_ASSERT();
- NET_EPOCH_ASSERT();
-
- if (send_ns != 0)
- nd6_dad_ns_output(dp);
- callout_reset(&dp->dad_timer_ch, ticks,
- (void (*)(void *))nd6_dad_timer, (void *)dp);
+ callout_reset(&dp->dad_timer_ch, ticks, nd6_dad_timer, dp);
}
static void
nd6_dad_stoptimer(struct dadq *dp)
{
-
callout_drain(&dp->dad_timer_ch);
}
static void
nd6_dad_rele(struct dadq *dp)
{
-
if (refcount_release(&dp->dad_refcnt)) {
+ KASSERT(!dp->dad_ondadq, ("dp %p still on DAD queue", dp));
ifa_free(dp->dad_ifa);
free(dp, M_IP6NDP);
}
@@ -1229,8 +1227,7 @@
void
nd6_dad_init(void)
{
-
- rw_init(&V_dad_rwlock, "nd6 DAD queue");
+ DADQ_LOCK_INIT();
TAILQ_INIT(&V_dadq);
}
@@ -1243,7 +1240,6 @@
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct dadq *dp;
char ip6buf[INET6_ADDRSTRLEN];
- struct epoch_tracker et;
KASSERT((ia->ia6_flags & IN6_IFF_TENTATIVE) != 0,
("starting DAD on non-tentative address %p", ifa));
@@ -1265,12 +1261,13 @@
(ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_IFDISABLED) != 0)
return;
+ DADQ_WLOCK();
if ((dp = nd6_dad_find(ifa, NULL)) != NULL) {
/*
* DAD is already in progress. Let the existing entry
* finish it.
*/
- nd6_dad_rele(dp);
+ DADQ_WUNLOCK();
return;
}
@@ -1282,7 +1279,8 @@
ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
return;
}
- callout_init(&dp->dad_timer_ch, 0);
+ callout_init_rw(&dp->dad_timer_ch, DADQ_LOCKPTR(),
+ CALLOUT_RETURNUNLOCKED);
#ifdef VIMAGE
dp->dad_vnet = curvnet;
#endif
@@ -1305,9 +1303,8 @@
/* Add this to the dadq and add a reference for the dadq. */
refcount_init(&dp->dad_refcnt, 1);
nd6_dad_add(dp);
- NET_EPOCH_ENTER(et);
- nd6_dad_starttimer(dp, delay, 0);
- NET_EPOCH_EXIT(et);
+ nd6_dad_starttimer(dp, delay);
+ DADQ_WUNLOCK();
}
/*
@@ -1318,29 +1315,36 @@
{
struct dadq *dp;
+ DADQ_WLOCK();
dp = nd6_dad_find(ifa, NULL);
- if (!dp) {
+ if (dp == NULL) {
+ DADQ_WUNLOCK();
/* DAD wasn't started yet */
return;
}
- nd6_dad_stoptimer(dp);
+ /*
+ * Acquire a temporary reference so that we can safely stop the callout.
+ */
+ (void)refcount_acquire(&dp->dad_refcnt);
nd6_dad_del(dp);
+ DADQ_WUNLOCK();
- /* Release this function's reference, acquired by nd6_dad_find(). */
+ nd6_dad_stoptimer(dp);
nd6_dad_rele(dp);
}
static void
-nd6_dad_timer(struct dadq *dp)
+nd6_dad_timer(void *arg)
{
- CURVNET_SET(dp->dad_vnet);
+ struct dadq *dp = arg;
struct ifaddr *ifa = dp->dad_ifa;
struct ifnet *ifp = dp->dad_ifa->ifa_ifp;
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
char ip6buf[INET6_ADDRSTRLEN];
struct epoch_tracker et;
+ CURVNET_SET(dp->dad_vnet);
KASSERT(ia != NULL, ("DAD entry %p with no address", dp));
NET_EPOCH_ENTER(et);
@@ -1381,17 +1385,18 @@
* We have more NS to go. Send NS packet for DAD.
*/
nd6_dad_starttimer(dp,
- (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, 1);
+ (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
+ nd6_dad_ns_output(dp);
goto done;
} else {
/*
* We have transmitted sufficient number of DAD packets.
* See what we've got.
*/
- if (dp->dad_ns_icount > 0 || dp->dad_na_icount > 0)
+ if (dp->dad_ns_icount > 0 || dp->dad_na_icount > 0) {
/* We've seen NS or NA, means DAD has failed. */
nd6_dad_duplicated(ifa, dp);
- else if (V_dad_enhanced != 0 &&
+ } else if (V_dad_enhanced != 0 &&
dp->dad_ns_lcount > 0 &&
dp->dad_ns_lcount > dp->dad_loopbackprobe) {
/*
@@ -1412,8 +1417,8 @@
dp->dad_count =
dp->dad_ns_ocount + V_nd6_mmaxtries - 1;
nd6_dad_starttimer(dp,
- (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000,
- 1);
+ (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
+ nd6_dad_ns_output(dp);
goto done;
} else {
/*
@@ -1439,6 +1444,7 @@
}
err:
nd6_dad_del(dp);
+ DADQ_WUNLOCK();
done:
NET_EPOCH_EXIT(et);
CURVNET_RESTORE();
@@ -1498,6 +1504,10 @@
}
}
+/*
+ * Transmit a neighbour solicitation for the purpose of DAD. Returns with the
+ * DAD queue unlocked.
+ */
static void
nd6_dad_ns_output(struct dadq *dp)
{
@@ -1505,11 +1515,15 @@
struct ifnet *ifp = dp->dad_ifa->ifa_ifp;
int i;
+ DADQ_WLOCK_ASSERT();
+
dp->dad_ns_tcount++;
if ((ifp->if_flags & IFF_UP) == 0) {
+ DADQ_WUNLOCK();
return;
}
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ DADQ_WUNLOCK();
return;
}
@@ -1526,6 +1540,7 @@
* should work well in almost all cases.
*/
}
+ DADQ_WUNLOCK();
nd6_ns_output(ifp, NULL, NULL, &ia->ia_addr.sin6_addr,
(uint8_t *)&dp->dad_nonce[0]);
}
@@ -1541,12 +1556,11 @@
/* Ignore Nonce option when Enhanced DAD is disabled. */
if (V_dad_enhanced == 0)
ndopt_nonce = NULL;
+ DADQ_RLOCK();
dp = nd6_dad_find(ifa, ndopt_nonce);
- if (dp == NULL)
- return;
-
- dp->dad_ns_icount++;
- nd6_dad_rele(dp);
+ if (dp != NULL)
+ dp->dad_ns_icount++;
+ DADQ_RUNLOCK();
}
static void
@@ -1557,9 +1571,9 @@
if (ifa == NULL)
panic("ifa == NULL in nd6_dad_na_input");
+ DADQ_RLOCK();
dp = nd6_dad_find(ifa, NULL);
- if (dp != NULL) {
+ if (dp != NULL)
dp->dad_na_icount++;
- nd6_dad_rele(dp);
- }
+ DADQ_RUNLOCK();
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 29, 7:40 AM (18 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17838555
Default Alt Text
D31826.diff (7 KB)
Attached To
Mode
D31826: nd6: Make the DAD callout MPSAFE
Attached
Detach File
Event Timeline
Log In to Comment