Defer the work of freeing IPv4 multicast options from a socket to an
asychronous task. This avoids tearing down multicast state including sending IGMP leave messages and reprogramming MAC filters while holding the per-protocol global pcbinfo lock that is used in the receive path of packet processing. Reviewed by: rwatson MFC after: 1 month
This commit is contained in:
parent
1ad7a2570d
commit
c23de1f45b
@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/protosw.h>
|
#include <sys/protosw.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/ktr.h>
|
#include <sys/ktr.h>
|
||||||
|
#include <sys/taskqueue.h>
|
||||||
#include <sys/tree.h>
|
#include <sys/tree.h>
|
||||||
|
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
@ -144,6 +145,8 @@ static void inm_purge(struct in_multi *);
|
|||||||
static void inm_reap(struct in_multi *);
|
static void inm_reap(struct in_multi *);
|
||||||
static struct ip_moptions *
|
static struct ip_moptions *
|
||||||
inp_findmoptions(struct inpcb *);
|
inp_findmoptions(struct inpcb *);
|
||||||
|
static void inp_freemoptions_internal(struct ip_moptions *);
|
||||||
|
static void inp_gcmoptions(void *, int);
|
||||||
static int inp_get_source_filters(struct inpcb *, struct sockopt *);
|
static int inp_get_source_filters(struct inpcb *, struct sockopt *);
|
||||||
static int inp_join_group(struct inpcb *, struct sockopt *);
|
static int inp_join_group(struct inpcb *, struct sockopt *);
|
||||||
static int inp_leave_group(struct inpcb *, struct sockopt *);
|
static int inp_leave_group(struct inpcb *, struct sockopt *);
|
||||||
@ -179,6 +182,10 @@ static SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters,
|
|||||||
CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip_mcast_filters,
|
CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip_mcast_filters,
|
||||||
"Per-interface stack-wide source filters");
|
"Per-interface stack-wide source filters");
|
||||||
|
|
||||||
|
static STAILQ_HEAD(, ip_moptions) imo_gc_list =
|
||||||
|
STAILQ_HEAD_INITIALIZER(imo_gc_list);
|
||||||
|
static struct task imo_gc_task = TASK_INITIALIZER(0, inp_gcmoptions, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inline function which wraps assertions for a valid ifp.
|
* Inline function which wraps assertions for a valid ifp.
|
||||||
* The ifnet layer will set the ifma's ifp pointer to NULL if the ifp
|
* The ifnet layer will set the ifma's ifp pointer to NULL if the ifp
|
||||||
@ -1518,17 +1525,29 @@ inp_findmoptions(struct inpcb *inp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Discard the IP multicast options (and source filters).
|
* Discard the IP multicast options (and source filters). To minimize
|
||||||
|
* the amount of work done while holding locks such as the INP's
|
||||||
|
* pcbinfo lock (which is used in the receive path), the free
|
||||||
|
* operation is performed asynchronously in a separate task.
|
||||||
*
|
*
|
||||||
* SMPng: NOTE: assumes INP write lock is held.
|
* SMPng: NOTE: assumes INP write lock is held.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
inp_freemoptions(struct ip_moptions *imo)
|
inp_freemoptions(struct ip_moptions *imo)
|
||||||
{
|
{
|
||||||
struct in_mfilter *imf;
|
|
||||||
size_t idx, nmships;
|
|
||||||
|
|
||||||
KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__));
|
KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__));
|
||||||
|
IN_MULTI_LOCK();
|
||||||
|
STAILQ_INSERT_TAIL(&imo_gc_list, imo, imo_link);
|
||||||
|
IN_MULTI_UNLOCK();
|
||||||
|
taskqueue_enqueue(taskqueue_thread, &imo_gc_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inp_freemoptions_internal(struct ip_moptions *imo)
|
||||||
|
{
|
||||||
|
struct in_mfilter *imf;
|
||||||
|
size_t idx, nmships;
|
||||||
|
|
||||||
nmships = imo->imo_num_memberships;
|
nmships = imo->imo_num_memberships;
|
||||||
for (idx = 0; idx < nmships; ++idx) {
|
for (idx = 0; idx < nmships; ++idx) {
|
||||||
@ -1546,6 +1565,22 @@ inp_freemoptions(struct ip_moptions *imo)
|
|||||||
free(imo, M_IPMOPTS);
|
free(imo, M_IPMOPTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inp_gcmoptions(void *context, int pending)
|
||||||
|
{
|
||||||
|
struct ip_moptions *imo;
|
||||||
|
|
||||||
|
IN_MULTI_LOCK();
|
||||||
|
while (!STAILQ_EMPTY(&imo_gc_list)) {
|
||||||
|
imo = STAILQ_FIRST(&imo_gc_list);
|
||||||
|
STAILQ_REMOVE_HEAD(&imo_gc_list, imo_link);
|
||||||
|
IN_MULTI_UNLOCK();
|
||||||
|
inp_freemoptions_internal(imo);
|
||||||
|
IN_MULTI_LOCK();
|
||||||
|
}
|
||||||
|
IN_MULTI_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Atomically get source filters on a socket for an IPv4 multicast group.
|
* Atomically get source filters on a socket for an IPv4 multicast group.
|
||||||
* Called with INP lock held; returns with lock released.
|
* Called with INP lock held; returns with lock released.
|
||||||
|
@ -93,6 +93,7 @@ struct ip_moptions {
|
|||||||
u_short imo_max_memberships; /* max memberships this socket */
|
u_short imo_max_memberships; /* max memberships this socket */
|
||||||
struct in_multi **imo_membership; /* group memberships */
|
struct in_multi **imo_membership; /* group memberships */
|
||||||
struct in_mfilter *imo_mfilters; /* source filters */
|
struct in_mfilter *imo_mfilters; /* source filters */
|
||||||
|
STAILQ_ENTRY(ip_moptions) imo_link;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ipstat {
|
struct ipstat {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user