Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145451062
D14969.1777943834.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
25 KB
Referenced Files
None
Subscribers
None
D14969.1777943834.diff
View Options
Index: sys/net/if.c
===================================================================
--- sys/net/if.c
+++ sys/net/if.c
@@ -3317,6 +3317,7 @@
struct sockaddr_dl sdl;
int error;
+ IN_MULTI_LIST_UNLOCK_ASSERT();
/*
* If the address is already present, return a new reference to it;
* otherwise, allocate storage and set up a new address.
@@ -3498,6 +3499,8 @@
struct ifnet *ifp;
int lastref;
+ IN_MULTI_LIST_UNLOCK_ASSERT();
+ IN_MULTI_LOCK_ASSERT();
ifp = ifma->ifma_ifp;
#ifdef DIAGNOSTIC
if (ifp == NULL) {
Index: sys/netinet/igmp.c
===================================================================
--- sys/netinet/igmp.c
+++ sys/netinet/igmp.c
@@ -136,7 +136,7 @@
struct in_multi *, const int, const int, const int);
static int igmp_v3_enqueue_filter_change(struct mbufq *,
struct in_multi *);
-static void igmp_v3_process_group_timers(struct igmp_ifsoftc *,
+static void igmp_v3_process_group_timers(struct in_multi_head *,
struct mbufq *, struct mbufq *, struct in_multi *,
const int);
static int igmp_v3_merge_state_changes(struct in_multi *,
@@ -162,12 +162,12 @@
* themselves are not virtualized.
*
* Locking:
- * * The permitted lock order is: IN_MULTI_LOCK, IGMP_LOCK, IF_ADDR_LOCK.
+ * * The permitted lock order is: IN_MULTI_LIST_LOCK, IGMP_LOCK, IF_ADDR_LOCK.
* Any may be taken independently; if any are held at the same
* time, the above lock order must be followed.
* * All output is delegated to the netisr.
* Now that Giant has been eliminated, the netisr may be inlined.
- * * IN_MULTI_LOCK covers in_multi.
+ * * IN_MULTI_LIST_LOCK covers in_multi.
* * IGMP_LOCK covers igmp_ifsoftc and any global variables in this file,
* including the output queue.
* * IF_ADDR_LOCK covers if_multiaddrs, which is used for a variety of
@@ -441,7 +441,7 @@
if (error)
return (error);
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
IGMP_LOCK();
if (name[0] <= 0 || name[0] > V_if_index) {
@@ -475,7 +475,7 @@
out_locked:
IGMP_UNLOCK();
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
return (error);
}
@@ -586,7 +586,6 @@
igi->igi_qi = IGMP_QI_INIT;
igi->igi_qri = IGMP_QRI_INIT;
igi->igi_uri = IGMP_URI_INIT;
- SLIST_INIT(&igi->igi_relinmhead);
mbufq_init(&igi->igi_gq, IGMP_MAX_RESPONSE_PACKETS);
LIST_INSERT_HEAD(&V_igi_head, igi, igi_link);
@@ -612,11 +611,12 @@
{
struct igmp_ifsoftc *igi;
struct ifmultiaddr *ifma;
- struct in_multi *inm, *tinm;
-
+ struct in_multi *inm;
+ struct in_multi_head inm_free_tmp;
CTR3(KTR_IGMPV3, "%s: called for ifp %p(%s)", __func__, ifp,
ifp->if_xname);
+ SLIST_INIT(&inm_free_tmp);
IGMP_LOCK();
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
@@ -632,23 +632,18 @@
#endif
inm = (struct in_multi *)ifma->ifma_protospec;
if (inm->inm_state == IGMP_LEAVING_MEMBER) {
- SLIST_INSERT_HEAD(&igi->igi_relinmhead,
- inm, inm_nrele);
+ if (--inm->inm_refcount == 0) {
+ SLIST_INSERT_HEAD(&inm_free_tmp,
+ inm, inm_nrele);
+ }
}
inm_clear_recorded(inm);
}
IF_ADDR_RUNLOCK(ifp);
- /*
- * Free the in_multi reference(s) for this IGMP lifecycle.
- */
- SLIST_FOREACH_SAFE(inm, &igi->igi_relinmhead, inm_nrele,
- tinm) {
- SLIST_REMOVE_HEAD(&igi->igi_relinmhead, inm_nrele);
- inm_release_locked(inm);
- }
+ inm_release_deferred(&inm_free_tmp);
}
-
IGMP_UNLOCK();
+
}
/*
@@ -684,11 +679,6 @@
mbufq_drain(&igi->igi_gq);
LIST_REMOVE(igi, igi_link);
-
- KASSERT(SLIST_EMPTY(&igi->igi_relinmhead),
- ("%s: there are dangling in_multi references",
- __func__));
-
free(igi, M_IGMP);
return;
}
@@ -722,7 +712,7 @@
}
IGMPSTAT_INC(igps_rcv_gen_queries);
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
IGMP_LOCK();
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
@@ -778,7 +768,7 @@
out_locked:
IGMP_UNLOCK();
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
return (0);
}
@@ -816,7 +806,7 @@
IGMPSTAT_INC(igps_rcv_group_queries);
}
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
IGMP_LOCK();
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
@@ -872,7 +862,7 @@
out_locked:
IGMP_UNLOCK();
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
return (0);
}
@@ -899,7 +889,7 @@
CTR4(KTR_IGMPV3, "0x%08x: %s/%s timer=%d", __func__,
ntohl(inm->inm_addr.s_addr), inm->inm_ifp->if_xname, timer);
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
switch (inm->inm_state) {
case IGMP_NOT_MEMBER:
@@ -1011,7 +1001,7 @@
IGMPSTAT_INC(igps_rcv_gsr_queries);
}
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
IGMP_LOCK();
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
@@ -1092,7 +1082,7 @@
out_locked:
IGMP_UNLOCK();
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
return (0);
}
@@ -1109,7 +1099,7 @@
int retval;
uint16_t nsrc;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
retval = 0;
@@ -1246,7 +1236,7 @@
* If we are a member of this group, and our membership should be
* reported, stop our group timer and transition to the 'lazy' state.
*/
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
inm = inm_lookup(ifp, igmp->igmp_group);
if (inm != NULL) {
struct igmp_ifsoftc *igi;
@@ -1305,7 +1295,7 @@
}
out_locked:
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
return (0);
}
@@ -1373,7 +1363,7 @@
* reported, and our group timer is pending or about to be reset,
* stop our group timer by transitioning to the 'lazy' state.
*/
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
inm = inm_lookup(ifp, igmp->igmp_group);
if (inm != NULL) {
struct igmp_ifsoftc *igi;
@@ -1418,7 +1408,7 @@
}
out_locked:
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
return (0);
}
@@ -1647,6 +1637,7 @@
struct igmp_ifsoftc *igi;
struct ifmultiaddr *ifma;
struct in_multi *inm;
+ struct in_multi_head inm_free_tmp;
int loop, uri_fasthz;
loop = 0;
@@ -1662,7 +1653,8 @@
!V_state_change_timers_running)
return;
- IN_MULTI_LOCK();
+ SLIST_INIT(&inm_free_tmp);
+ IN_MULTI_LIST_LOCK();
IGMP_LOCK();
/*
@@ -1720,7 +1712,7 @@
igi->igi_version);
break;
case IGMP_VERSION_3:
- igmp_v3_process_group_timers(igi, &qrq,
+ igmp_v3_process_group_timers(&inm_free_tmp, &qrq,
&scq, inm, uri_fasthz);
break;
}
@@ -1728,8 +1720,6 @@
IF_ADDR_RUNLOCK(ifp);
if (igi->igi_version == IGMP_VERSION_3) {
- struct in_multi *tinm;
-
igmp_dispatch_queue(&qrq, 0, loop);
igmp_dispatch_queue(&scq, 0, loop);
@@ -1737,18 +1727,13 @@
* Free the in_multi reference(s) for this
* IGMP lifecycle.
*/
- SLIST_FOREACH_SAFE(inm, &igi->igi_relinmhead,
- inm_nrele, tinm) {
- SLIST_REMOVE_HEAD(&igi->igi_relinmhead,
- inm_nrele);
- inm_release_locked(inm);
- }
+ inm_release_deferred(&inm_free_tmp);
}
}
out_locked:
IGMP_UNLOCK();
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
}
/*
@@ -1760,7 +1745,7 @@
{
int report_timer_expired;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
if (inm->inm_timer == 0) {
@@ -1802,14 +1787,14 @@
* Note: Unlocked read from igi.
*/
static void
-igmp_v3_process_group_timers(struct igmp_ifsoftc *igi,
+igmp_v3_process_group_timers(struct in_multi_head *inmh,
struct mbufq *qrq, struct mbufq *scq,
struct in_multi *inm, const int uri_fasthz)
{
int query_response_timer_expired;
int state_change_retransmit_timer_expired;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
query_response_timer_expired = 0;
@@ -1907,8 +1892,7 @@
if (inm->inm_state == IGMP_LEAVING_MEMBER &&
inm->inm_scrv == 0) {
inm->inm_state = IGMP_NOT_MEMBER;
- SLIST_INSERT_HEAD(&igi->igi_relinmhead,
- inm, inm_nrele);
+ SLIST_INSERT_HEAD(inmh, inm, inm_nrele);
}
}
break;
@@ -1929,7 +1913,7 @@
igmp_v3_suppress_group_record(struct in_multi *inm)
{
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
KASSERT(inm->inm_igi->igi_version == IGMP_VERSION_3,
("%s: not IGMPv3 mode on link", __func__));
@@ -2003,13 +1987,15 @@
{
struct ifmultiaddr *ifma;
struct ifnet *ifp;
- struct in_multi *inm, *tinm;
+ struct in_multi *inm;
+ struct in_multi_head inm_free_tmp;
CTR3(KTR_IGMPV3, "%s: cancel v3 timers on ifp %p(%s)", __func__,
igi->igi_ifp, igi->igi_ifp->if_xname);
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
+ SLIST_INIT(&inm_free_tmp);
/*
* Stop the v3 General Query Response on this link stone dead.
@@ -2050,7 +2036,9 @@
* message is sent upstream to the old querier --
* transition to NOT would lose the leave and race.
*/
- SLIST_INSERT_HEAD(&igi->igi_relinmhead, inm, inm_nrele);
+ if (--inm->inm_refcount == 0) {
+ SLIST_INSERT_HEAD(&inm_free_tmp, inm, inm_nrele);
+ }
/* FALLTHROUGH */
case IGMP_G_QUERY_PENDING_MEMBER:
case IGMP_SG_QUERY_PENDING_MEMBER:
@@ -2069,10 +2057,8 @@
mbufq_drain(&inm->inm_scq);
}
IF_ADDR_RUNLOCK(ifp);
- SLIST_FOREACH_SAFE(inm, &igi->igi_relinmhead, inm_nrele, tinm) {
- SLIST_REMOVE_HEAD(&igi->igi_relinmhead, inm_nrele);
- inm_release_locked(inm);
- }
+
+ inm_release_deferred(&inm_free_tmp);
}
/*
@@ -2199,7 +2185,7 @@
struct ip *ip;
struct mbuf *m;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
ifp = inm->inm_ifp;
@@ -2276,10 +2262,8 @@
struct ifnet *ifp;
int error;
- IN_MULTI_LOCK_ASSERT();
-
error = 0;
-
+ IN_MULTI_LOCK_ASSERT();
/*
* Try to detect if the upper layer just asked us to change state
* for an interface which has now gone away.
@@ -2379,9 +2363,10 @@
* group around for the final INCLUDE {} enqueue.
*/
if (igi->igi_version == IGMP_VERSION_3 &&
- inm->inm_state == IGMP_LEAVING_MEMBER)
- inm_release_locked(inm);
-
+ inm->inm_state == IGMP_LEAVING_MEMBER) {
+ MPASS(inm->inm_refcount > 1);
+ --inm->inm_refcount;
+ }
inm->inm_state = IGMP_REPORTING_MEMBER;
switch (igi->igi_version) {
@@ -2473,7 +2458,7 @@
ifp = inm->inm_ifp;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
KASSERT(igi && igi->igi_ifp == ifp, ("%s: inconsistent ifp", __func__));
@@ -2531,7 +2516,7 @@
__func__, ntohl(inm->inm_addr.s_addr), inm->inm_ifp,
inm->inm_ifp->if_xname);
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
switch (inm->inm_state) {
@@ -2658,7 +2643,7 @@
in_addr_t naddr;
uint8_t mode;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
error = 0;
ifp = inm->inm_ifp;
@@ -3018,7 +3003,7 @@
uint8_t mode, now, then;
rectype_t crt, drt, nrt;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
if (inm->inm_nsrc == 0 ||
(inm->inm_st[0].iss_asm > 0 && inm->inm_st[1].iss_asm > 0))
@@ -3221,7 +3206,7 @@
domerge = 0;
recslen = 0;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
/*
@@ -3320,7 +3305,7 @@
struct in_multi *inm;
int retval, loop;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IGMP_LOCK_ASSERT();
KASSERT(igi->igi_version == IGMP_VERSION_3,
@@ -3632,7 +3617,6 @@
db_printf(" qi %u\n", igi->igi_qi);
db_printf(" qri %u\n", igi->igi_qri);
db_printf(" uri %u\n", igi->igi_uri);
- /* SLIST_HEAD(,in_multi) igi_relinmhead */
/* struct mbufq igi_gq; */
db_printf("\n");
}
Index: sys/netinet/igmp_var.h
===================================================================
--- sys/netinet/igmp_var.h
+++ sys/netinet/igmp_var.h
@@ -214,7 +214,6 @@
uint32_t igi_qi; /* IGMPv3 Query Interval (s) */
uint32_t igi_qri; /* IGMPv3 Query Response Interval (s) */
uint32_t igi_uri; /* IGMPv3 Unsolicited Report Interval (s) */
- SLIST_HEAD(,in_multi) igi_relinmhead; /* released groups */
struct mbufq igi_gq; /* general query responses queue */
};
Index: sys/netinet/in.c
===================================================================
--- sys/netinet/in.c
+++ sys/netinet/in.c
@@ -632,12 +632,10 @@
struct in_ifinfo *ii;
ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
- IN_MULTI_LOCK();
if (ii->ii_allhosts) {
- (void)in_leavegroup_locked(ii->ii_allhosts, NULL);
+ (void)in_leavegroup(ii->ii_allhosts, NULL);
ii->ii_allhosts = NULL;
}
- IN_MULTI_UNLOCK();
}
IF_ADDR_WLOCK(ifp);
@@ -1011,12 +1009,12 @@
static void
in_purgemaddrs(struct ifnet *ifp)
{
- LIST_HEAD(,in_multi) purgeinms;
- struct in_multi *inm, *tinm;
+ struct in_multi_head purgeinms;
+ struct in_multi *inm;
struct ifmultiaddr *ifma;
- LIST_INIT(&purgeinms);
- IN_MULTI_LOCK();
+ SLIST_INIT(&purgeinms);
+ IN_MULTI_LIST_LOCK();
/*
* Extract list of in_multi associated with the detaching ifp
@@ -1034,17 +1032,13 @@
("%s: ifma_protospec is NULL", __func__));
#endif
inm = (struct in_multi *)ifma->ifma_protospec;
- LIST_INSERT_HEAD(&purgeinms, inm, inm_link);
+ SLIST_INSERT_HEAD(&purgeinms, inm, inm_nrele);
}
IF_ADDR_RUNLOCK(ifp);
- LIST_FOREACH_SAFE(inm, &purgeinms, inm_link, tinm) {
- LIST_REMOVE(inm, inm_link);
- inm_release_locked(inm);
- }
+ inm_release_deferred(&purgeinms);
igmp_ifdetach(ifp);
-
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
}
struct in_llentry {
Index: sys/netinet/in_mcast.c
===================================================================
--- sys/netinet/in_mcast.c
+++ sys/netinet/in_mcast.c
@@ -91,17 +91,23 @@
/*
* Locking:
- * - Lock order is: Giant, INP_WLOCK, IN_MULTI_LOCK, IGMP_LOCK, IF_ADDR_LOCK.
+ * - Lock order is: Giant, INP_WLOCK, IN_MULTI_LIST_LOCK, IGMP_LOCK, IF_ADDR_LOCK.
* - The IF_ADDR_LOCK is implicitly taken by inm_lookup() earlier, however
* it can be taken by code in net/if.c also.
* - ip_moptions and in_mfilter are covered by the INP_WLOCK.
*
- * struct in_multi is covered by IN_MULTI_LOCK. There isn't strictly
+ * struct in_multi is covered by IN_MULTI_LIST_LOCK. There isn't strictly
* any need for in_multi itself to be virtualized -- it is bound to an ifp
* anyway no matter what happens.
*/
-struct mtx in_multi_mtx;
-MTX_SYSINIT(in_multi_mtx, &in_multi_mtx, "in_multi_mtx", MTX_DEF);
+struct mtx in_multi_list_mtx;
+MTX_SYSINIT(in_multi_mtx, &in_multi_list_mtx, "in_multi_list_mtx", MTX_DEF);
+
+struct mtx in_multi_free_mtx;
+MTX_SYSINIT(in_multi_free_mtx, &in_multi_free_mtx, "in_multi_free_mtx", MTX_DEF);
+
+struct sx in_multi_sx;
+SX_SYSINIT(in_multi_sx, &in_multi_sx, "in_multi_sx");
/*
* Functions with non-static linkage defined in this file should be
@@ -151,6 +157,7 @@
static int inm_merge(struct in_multi *, /*const*/ struct in_mfilter *);
static void inm_purge(struct in_multi *);
static void inm_reap(struct in_multi *);
+static void inm_release(struct in_multi *);
static struct ip_moptions *
inp_findmoptions(struct inpcb *);
static void inp_freemoptions_internal(struct ip_moptions *);
@@ -216,6 +223,41 @@
}
#endif
+static struct in_multi_head inm_free_list;
+static void inm_init()
+{
+ SLIST_INIT(&inm_free_list);
+}
+
+SYSINIT(inm_init, SI_SUB_SMP + 1, SI_ORDER_FIRST,
+ inm_init, NULL);
+
+void
+inm_release_deferred(struct in_multi_head *inmh)
+{
+ mtx_lock(&in_multi_free_mtx);
+ SLIST_CONCAT(&inm_free_list, inmh, in_multi, inm_nrele);
+ mtx_unlock(&in_multi_free_mtx);
+}
+
+static void
+inm_release_task(void *arg __unused, int pending __unused)
+{
+ struct in_multi_head inm_free_tmp;
+ struct in_multi *inm, *tinm;
+
+ SLIST_INIT(&inm_free_tmp);
+ mtx_lock(&in_multi_free_mtx);
+ SLIST_CONCAT(&inm_free_tmp, &inm_free_list, in_multi, inm_nrele);
+ mtx_unlock(&in_multi_free_mtx);
+ IN_MULTI_LOCK();
+ SLIST_FOREACH_SAFE(inm, &inm_free_tmp, inm_nrele, tinm) {
+ SLIST_REMOVE_HEAD(&inm_free_tmp, inm_nrele);
+ inm_release(inm);
+ }
+ IN_MULTI_UNLOCK();
+}
+
/*
* Initialize an in_mfilter structure to a known state at t0, t1
* with an empty source filter list.
@@ -232,7 +274,7 @@
/*
* Function for looking up an in_multi record for an IPv4 multicast address
* on a given interface. ifp must be valid. If no record found, return NULL.
- * The IN_MULTI_LOCK and IF_ADDR_LOCK on ifp must be held.
+ * The IN_MULTI_LIST_LOCK and IF_ADDR_LOCK on ifp must be held.
*/
struct in_multi *
inm_lookup_locked(struct ifnet *ifp, const struct in_addr ina)
@@ -240,7 +282,7 @@
struct ifmultiaddr *ifma;
struct in_multi *inm;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IF_ADDR_LOCK_ASSERT(ifp);
inm = NULL;
@@ -264,7 +306,7 @@
{
struct in_multi *inm;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
IF_ADDR_RLOCK(ifp);
inm = inm_lookup_locked(ifp, ina);
IF_ADDR_RUNLOCK(ifp);
@@ -451,7 +493,7 @@
IN_MULTI_LOCK_ASSERT();
ii = (struct in_ifinfo *)ifp->if_afdata[AF_INET];
-
+ IN_MULTI_LIST_LOCK();
inm = inm_lookup(ifp, *group);
if (inm != NULL) {
/*
@@ -462,9 +504,11 @@
("%s: bad refcount %d", __func__, inm->inm_refcount));
++inm->inm_refcount;
*pinm = inm;
- return (0);
}
-
+ IN_MULTI_LIST_UNLOCK();
+ if (inm != NULL)
+ return (0);
+
memset(&gsin, 0, sizeof(gsin));
gsin.sin_family = AF_INET;
gsin.sin_len = sizeof(struct sockaddr_in);
@@ -550,21 +594,14 @@
* If the refcount drops to 0, free the in_multi record and
* delete the underlying link-layer membership.
*/
-void
-inm_release_locked(struct in_multi *inm)
+static void
+inm_release(struct in_multi *inm)
{
struct ifmultiaddr *ifma;
- IN_MULTI_LOCK_ASSERT();
-
+ IN_MULTI_LIST_LOCK();
CTR2(KTR_IGMPV3, "%s: refcount is %d", __func__, inm->inm_refcount);
-
- if (--inm->inm_refcount > 0) {
- CTR2(KTR_IGMPV3, "%s: refcount is now %d", __func__,
- inm->inm_refcount);
- return;
- }
-
+ MPASS(inm->inm_refcount == 0);
CTR2(KTR_IGMPV3, "%s: freeing inm %p", __func__, inm);
ifma = inm->inm_ifma;
@@ -576,7 +613,7 @@
ifma->ifma_protospec = NULL;
inm_purge(inm);
-
+ IN_MULTI_LIST_UNLOCK();
free(inm, M_IPMADDR);
if_delmulti_ifma(ifma);
@@ -592,7 +629,7 @@
{
struct ip_msource *ims;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
if (ims->ims_stp) {
@@ -632,7 +669,7 @@
struct ip_msource find;
struct ip_msource *ims, *nims;
- IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_LOCK_ASSERT();
find.ims_haddr = ntohl(naddr);
ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
@@ -959,6 +996,7 @@
schanged = 0;
error = 0;
nsrc1 = nsrc0 = 0;
+ IN_MULTI_LIST_LOCK_ASSERT();
/*
* Update the source filters first, as this may fail.
@@ -1165,6 +1203,7 @@
int error;
IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_UNLOCK_ASSERT();
CTR4(KTR_IGMPV3, "%s: join 0x%08x on %p(%s))", __func__,
ntohl(gina->s_addr), ifp, ifp->if_xname);
@@ -1186,7 +1225,7 @@
CTR1(KTR_IGMPV3, "%s: in_getmulti() failure", __func__);
return (error);
}
-
+ IN_MULTI_LIST_LOCK();
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
error = inm_merge(inm, imf);
if (error) {
@@ -1201,10 +1240,15 @@
goto out_inm_release;
}
-out_inm_release:
+ out_inm_release:
+ IN_MULTI_LIST_UNLOCK();
if (error) {
+ struct in_multi_head tmp;
+
CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm);
- inm_release_locked(inm);
+ SLIST_INIT(&tmp);
+ SLIST_INSERT_HEAD(&tmp, inm, inm_nrele);
+ inm_release_deferred(&tmp);
} else {
*pinm = inm;
}
@@ -1243,12 +1287,14 @@
int
in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
{
+ struct in_multi_head tmp;
struct in_mfilter timf;
int error;
error = 0;
IN_MULTI_LOCK_ASSERT();
+ IN_MULTI_LIST_UNLOCK_ASSERT();
CTR5(KTR_IGMPV3, "%s: leave inm %p, 0x%08x/%s, imf %p", __func__,
inm, ntohl(inm->inm_addr.s_addr),
@@ -1272,18 +1318,22 @@
* the transaction, it MUST NOT fail.
*/
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
+ IN_MULTI_LIST_LOCK();
error = inm_merge(inm, imf);
KASSERT(error == 0, ("%s: failed to merge inm state", __func__));
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
CURVNET_SET(inm->inm_ifp->if_vnet);
error = igmp_change_state(inm);
+ IN_MULTI_LIST_UNLOCK();
CURVNET_RESTORE();
if (error)
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm);
- inm_release_locked(inm);
+ SLIST_INIT(&tmp);
+ SLIST_INSERT_HEAD(&tmp, inm, inm_nrele);
+ inm_release_deferred(&tmp);
return (error);
}
@@ -1487,7 +1537,7 @@
* Begin state merge transaction at IGMP layer.
*/
IN_MULTI_LOCK();
-
+ IN_MULTI_LIST_LOCK();
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
error = inm_merge(inm, imf);
if (error) {
@@ -1503,7 +1553,7 @@
out_in_multi_locked:
IN_MULTI_UNLOCK();
-
+ IN_MULTI_UNLOCK();
out_imf_rollback:
if (error)
imf_rollback(imf);
@@ -1582,9 +1632,9 @@
{
KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__));
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
STAILQ_INSERT_TAIL(&imo_gc_list, imo, imo_link);
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
taskqueue_enqueue(taskqueue_thread, &imo_gc_task);
}
@@ -1615,15 +1665,15 @@
{
struct ip_moptions *imo;
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
while (!STAILQ_EMPTY(&imo_gc_list)) {
imo = STAILQ_FIRST(&imo_gc_list);
STAILQ_REMOVE_HEAD(&imo_gc_list, imo_link);
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
inp_freemoptions_internal(imo);
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
}
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
}
/*
@@ -2163,6 +2213,8 @@
/*
* Begin state merge transaction at IGMP layer.
*/
+ in_pcbref(inp);
+ INP_WUNLOCK(inp);
IN_MULTI_LOCK();
if (is_new) {
@@ -2171,20 +2223,23 @@
if (error) {
CTR1(KTR_IGMPV3, "%s: in_joingroup_locked failed",
__func__);
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
goto out_imo_free;
}
imo->imo_membership[idx] = inm;
} else {
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
+ IN_MULTI_LIST_LOCK();
error = inm_merge(inm, imf);
if (error) {
CTR1(KTR_IGMPV3, "%s: failed to merge inm state",
- __func__);
+ __func__);
+ IN_MULTI_LIST_UNLOCK();
goto out_in_multi_locked;
}
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
error = igmp_change_state(inm);
+ IN_MULTI_LIST_UNLOCK();
if (error) {
CTR1(KTR_IGMPV3, "%s: failed igmp downcall",
__func__);
@@ -2195,7 +2250,8 @@
out_in_multi_locked:
IN_MULTI_UNLOCK();
-
+ INP_WLOCK(inp);
+ in_pcbrele_wlocked(inp);
INP_WLOCK_ASSERT(inp);
if (error) {
imf_rollback(imf);
@@ -2395,6 +2451,7 @@
(void)in_leavegroup_locked(inm, imf);
} else {
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
+ IN_MULTI_LIST_LOCK();
error = inm_merge(inm, imf);
if (error) {
CTR1(KTR_IGMPV3, "%s: failed to merge inm state",
@@ -2404,6 +2461,7 @@
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
error = igmp_change_state(inm);
+ IN_MULTI_LIST_UNLOCK();
if (error) {
CTR1(KTR_IGMPV3, "%s: failed igmp downcall",
__func__);
@@ -2639,6 +2697,7 @@
INP_WLOCK_ASSERT(inp);
IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
/*
* Begin state merge transaction at IGMP layer.
@@ -2647,11 +2706,13 @@
error = inm_merge(inm, imf);
if (error) {
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
+ IN_MULTI_LIST_UNLOCK();
goto out_in_multi_locked;
}
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
error = igmp_change_state(inm);
+ IN_MULTI_LIST_UNLOCK();
if (error)
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
@@ -2883,7 +2944,7 @@
if (retval)
return (retval);
- IN_MULTI_LOCK();
+ IN_MULTI_LIST_LOCK();
IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
@@ -2916,7 +2977,7 @@
}
IF_ADDR_RUNLOCK(ifp);
- IN_MULTI_UNLOCK();
+ IN_MULTI_LIST_UNLOCK();
return (retval);
}
Index: sys/netinet/in_var.h
===================================================================
--- sys/netinet/in_var.h
+++ sys/netinet/in_var.h
@@ -55,6 +55,7 @@
struct igmp_ifsoftc;
struct in_multi;
struct lltable;
+SLIST_HEAD(in_multi_head, in_multi);
/*
* IPv4 per-interface state.
@@ -329,11 +330,18 @@
* consumers of IN_*_MULTI() macros should acquire the locks before
* calling them; users of the in_{add,del}multi() functions should not.
*/
-extern struct mtx in_multi_mtx;
-#define IN_MULTI_LOCK() mtx_lock(&in_multi_mtx)
-#define IN_MULTI_UNLOCK() mtx_unlock(&in_multi_mtx)
-#define IN_MULTI_LOCK_ASSERT() mtx_assert(&in_multi_mtx, MA_OWNED)
-#define IN_MULTI_UNLOCK_ASSERT() mtx_assert(&in_multi_mtx, MA_NOTOWNED)
+extern struct mtx in_multi_list_mtx;
+extern struct sx in_multi_sx;
+
+#define IN_MULTI_LIST_LOCK() mtx_lock(&in_multi_list_mtx)
+#define IN_MULTI_LIST_UNLOCK() mtx_unlock(&in_multi_list_mtx)
+#define IN_MULTI_LIST_LOCK_ASSERT() mtx_assert(&in_multi_list_mtx, MA_OWNED)
+#define IN_MULTI_LIST_UNLOCK_ASSERT() mtx_assert(&in_multi_list_mtx, MA_NOTOWNED)
+
+#define IN_MULTI_LOCK() sx_xlock(&in_multi_sx)
+#define IN_MULTI_UNLOCK() sx_xunlock(&in_multi_sx)
+#define IN_MULTI_LOCK_ASSERT() sx_assert(&in_multi_sx, SA_XLOCKED)
+#define IN_MULTI_UNLOCK_ASSERT() sx_assert(&in_multi_sx, SA_XUNLOCKED)
/* Acquire an in_multi record. */
static __inline void
@@ -364,10 +372,9 @@
void inm_clear_recorded(struct in_multi *);
void inm_print(const struct in_multi *);
int inm_record_source(struct in_multi *inm, const in_addr_t);
-void inm_release(struct in_multi *);
-void inm_release_locked(struct in_multi *);
+void inm_release_deferred(struct in_multi_head *inmh);
struct in_multi *
- in_addmulti(struct in_addr *, struct ifnet *);
+in_addmulti(struct in_addr *, struct ifnet *);
void in_delmulti(struct in_multi *);
int in_joingroup(struct ifnet *, const struct in_addr *,
/*const*/ struct in_mfilter *, struct in_multi **);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, May 5, 1:17 AM (1 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28557520
Default Alt Text
D14969.1777943834.diff (25 KB)
Attached To
Mode
D14969: separate out list manipulation locking from state change in multicast
Attached
Detach File
Event Timeline
Log In to Comment