diff options
author | jhb <jhb@FreeBSD.org> | 2011-12-29 20:41:16 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2011-12-29 20:41:16 +0000 |
commit | 419867b9b482ff78635860a6f0edf31b1f7b1066 (patch) | |
tree | 723cecce231d5299a535c578bc5d2a1295a1e1fb /sys/netinet | |
parent | 69a9d9792b2212389b3858aaca3b6cd5db06da58 (diff) | |
download | FreeBSD-src-419867b9b482ff78635860a6f0edf31b1f7b1066.zip FreeBSD-src-419867b9b482ff78635860a6f0edf31b1f7b1066.tar.gz |
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
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/in_mcast.c | 41 | ||||
-rw-r--r-- | sys/netinet/ip_var.h | 1 |
2 files changed, 39 insertions, 3 deletions
diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c index 6c62dca..fde0f67 100644 --- a/sys/netinet/in_mcast.c +++ b/sys/netinet/in_mcast.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include <sys/protosw.h> #include <sys/sysctl.h> #include <sys/ktr.h> +#include <sys/taskqueue.h> #include <sys/tree.h> #include <net/if.h> @@ -144,6 +145,8 @@ static void inm_purge(struct in_multi *); static void inm_reap(struct in_multi *); static struct ip_moptions * 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_join_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, "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. * 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. */ void inp_freemoptions(struct ip_moptions *imo) { - struct in_mfilter *imf; - size_t idx, nmships; 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; for (idx = 0; idx < nmships; ++idx) { @@ -1546,6 +1565,22 @@ inp_freemoptions(struct ip_moptions *imo) 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. * Called with INP lock held; returns with lock released. diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index cd30093..0ebcea5 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -93,6 +93,7 @@ struct ip_moptions { u_short imo_max_memberships; /* max memberships this socket */ struct in_multi **imo_membership; /* group memberships */ struct in_mfilter *imo_mfilters; /* source filters */ + STAILQ_ENTRY(ip_moptions) imo_link; }; struct ipstat { |