Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/netinet Make IGMP and multicast group management code MP...



details:   https://anonhg.NetBSD.org/src/rev/0f182291c4a0
branches:  trunk
changeset: 329579:0f182291c4a0
user:      rmind <rmind%NetBSD.org@localhost>
date:      Thu May 29 23:02:48 2014 +0000

description:
Make IGMP and multicast group management code MP-safe.  Use a read-write
lock to protect the hash table of multicast address records; also, make it
private and eliminate some macros.  In the long term, the lookup path ought
to be optimised.

diffstat:

 sys/netinet/igmp.c      |  231 ++++++++++++++++++++++++++------------------
 sys/netinet/igmp.h      |   10 +-
 sys/netinet/igmp_var.h  |    4 +-
 sys/netinet/in.c        |  246 ++++++++++++++++++++++++++++++++++-------------
 sys/netinet/in_var.h    |   83 +++------------
 sys/netinet/ip_carp.c   |    8 +-
 sys/netinet/ip_input.c  |    8 +-
 sys/netinet/ip_output.c |   11 +-
 sys/netinet/ip_var.h    |   19 ++-
 9 files changed, 362 insertions(+), 258 deletions(-)

diffs (truncated from 1166 to 300 lines):

diff -r 1cc3205892b9 -r 0f182291c4a0 sys/netinet/igmp.c
--- a/sys/netinet/igmp.c        Thu May 29 22:05:24 2014 +0000
+++ b/sys/netinet/igmp.c        Thu May 29 23:02:48 2014 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: igmp.c,v 1.54 2014/02/25 18:30:12 pooka Exp $  */
+/*     $NetBSD: igmp.c,v 1.55 2014/05/29 23:02:48 rmind Exp $  */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -40,7 +40,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.54 2014/02/25 18:30:12 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.55 2014/05/29 23:02:48 rmind Exp $");
 
 #include "opt_mrouting.h"
 
@@ -50,6 +50,7 @@
 #include <sys/socketvar.h>
 #include <sys/protosw.h>
 #include <sys/systm.h>
+#include <sys/cprng.h>
 #include <sys/sysctl.h>
 
 #include <net/if.h>
@@ -64,83 +65,107 @@
 #include <netinet/igmp.h>
 #include <netinet/igmp_var.h>
 
-#define IP_MULTICASTOPTS       0
+/*
+ * Per-interface router version information.
+ */
+typedef struct router_info {
+       LIST_ENTRY(router_info) rti_link;
+       ifnet_t *       rti_ifp;
+       int             rti_type;       /* type of router on this interface */
+       int             rti_age;        /* time since last v1 query */
+} router_info_t;
 
-static struct pool igmp_rti_pool;
-
-static percpu_t *igmpstat_percpu;
+/*
+ * The router-info list and the timer flag are protected by in_multilock.
+ *
+ * Lock order:
+ *
+ *     softnet_lock ->
+ *             in_multilock
+ */
+static struct pool     igmp_rti_pool           __cacheline_aligned;
+static LIST_HEAD(, router_info)        rti_head        __cacheline_aligned;
+static int             igmp_timers_on          __cacheline_aligned;
+static percpu_t *      igmpstat_percpu         __read_mostly;
 
 #define        IGMP_STATINC(x)         _NET_STATINC(igmpstat_percpu, x)
 
-int igmp_timers_are_running;
-static LIST_HEAD(, router_info) rti_head = LIST_HEAD_INITIALIZER(rti_head);
+static void            igmp_sendpkt(struct in_multi *, int);
+static int             rti_fill(struct in_multi *);
+static router_info_t * rti_find(struct ifnet *);
+static void            rti_delete(struct ifnet *);
+static void            sysctl_net_inet_igmp_setup(struct sysctllog **);
 
-void igmp_sendpkt(struct in_multi *, int);
-static int rti_fill(struct in_multi *);
-static struct router_info *rti_find(struct ifnet *);
-static void rti_delete(struct ifnet *);
-
-static void sysctl_net_inet_igmp_setup(struct sysctllog **);
-
+/*
+ * rti_fill: associate router information with the given multicast group;
+ * if there is no router information for the interface, then create it.
+ */
 static int
 rti_fill(struct in_multi *inm)
 {
-       struct router_info *rti;
+       router_info_t *rti;
 
-       /* this function is called at splsoftnet() */
+       KASSERT(in_multi_lock_held());
+
        LIST_FOREACH(rti, &rti_head, rti_link) {
                if (rti->rti_ifp == inm->inm_ifp) {
                        inm->inm_rti = rti;
-                       if (rti->rti_type == IGMP_v1_ROUTER)
-                               return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
-                       else
-                               return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
+                       return rti->rti_type == IGMP_v1_ROUTER ?
+                           IGMP_v1_HOST_MEMBERSHIP_REPORT :
+                           IGMP_v2_HOST_MEMBERSHIP_REPORT;
                }
        }
-
        rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
-       if (rti == NULL)
+       if (rti == NULL) {
                return 0;
+       }
        rti->rti_ifp = inm->inm_ifp;
        rti->rti_type = IGMP_v2_ROUTER;
        LIST_INSERT_HEAD(&rti_head, rti, rti_link);
        inm->inm_rti = rti;
-       return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
+       return IGMP_v2_HOST_MEMBERSHIP_REPORT;
 }
 
-static struct router_info *
-rti_find(struct ifnet *ifp)
+/*
+ * rti_find: lookup or create router information for the given interface.
+ */
+static router_info_t *
+rti_find(ifnet_t *ifp)
 {
-       struct router_info *rti;
-       int s = splsoftnet();
+       router_info_t *rti;
+
+       KASSERT(in_multi_lock_held());
 
        LIST_FOREACH(rti, &rti_head, rti_link) {
                if (rti->rti_ifp == ifp)
-                       return (rti);
+                       return rti;
        }
-
        rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
        if (rti == NULL) {
-               splx(s);
                return NULL;
        }
        rti->rti_ifp = ifp;
        rti->rti_type = IGMP_v2_ROUTER;
        LIST_INSERT_HEAD(&rti_head, rti, rti_link);
-       splx(s);
-       return (rti);
+       return rti;
 }
 
+/*
+ * rti_delete: remove and free the router information entry for the
+ * given interface.
+ */
 static void
-rti_delete(struct ifnet *ifp)  /* MUST be called at splsoftnet */
+rti_delete(ifnet_t *ifp)
 {
-       struct router_info *rti;
+       router_info_t *rti;
+
+       KASSERT(in_multi_lock_held());
 
        LIST_FOREACH(rti, &rti_head, rti_link) {
                if (rti->rti_ifp == ifp) {
                        LIST_REMOVE(rti, rti_link);
                        pool_put(&igmp_rti_pool, rti);
-                       return;
+                       break;
                }
        }
 }
@@ -148,29 +173,24 @@
 void
 igmp_init(void)
 {
-
-       sysctl_net_inet_igmp_setup(NULL);
-       pool_init(&igmp_rti_pool, sizeof(struct router_info), 0, 0, 0,
+       pool_init(&igmp_rti_pool, sizeof(router_info_t), 0, 0, 0,
            "igmppl", NULL, IPL_SOFTNET);
        igmpstat_percpu = percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS);
+       sysctl_net_inet_igmp_setup(NULL);
+       LIST_INIT(&rti_head);
 }
 
 void
 igmp_input(struct mbuf *m, ...)
 {
-       int proto;
-       int iphlen;
-       struct ifnet *ifp = m->m_pkthdr.rcvif;
+       ifnet_t *ifp = m->m_pkthdr.rcvif;
        struct ip *ip = mtod(m, struct ip *);
        struct igmp *igmp;
-       u_int minlen;
+       u_int minlen, timer;
        struct in_multi *inm;
-       struct in_multistep step;
-       struct router_info *rti;
        struct in_ifaddr *ia;
-       u_int timer;
+       int proto, ip_len, iphlen;
        va_list ap;
-       u_int16_t ip_len;
 
        va_start(ap, m);
        iphlen = va_arg(ap, int);
@@ -222,11 +242,8 @@
                        break;
 
                if (igmp->igmp_code == 0) {
-                       rti = rti_find(ifp);
-                       if (rti == NULL)
-                               break;
-                       rti->rti_type = IGMP_v1_ROUTER;
-                       rti->rti_age = 0;
+                       struct in_multistep step;
+                       router_info_t *rti;
 
                        if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
                                IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
@@ -234,13 +251,23 @@
                                return;
                        }
 
+                       in_multi_lock(RW_WRITER);
+                       rti = rti_find(ifp);
+                       if (rti == NULL) {
+                               in_multi_unlock();
+                               break;
+                       }
+                       rti->rti_type = IGMP_v1_ROUTER;
+                       rti->rti_age = 0;
+
                        /*
                         * Start the timers in all of our membership records
                         * for the interface on which the query arrived,
                         * except those that are already running and those
                         * that belong to a "local" group (224.0.0.X).
                         */
-                       IN_FIRST_MULTI(step, inm);
+
+                       inm = in_first_multi(&step);
                        while (inm != NULL) {
                                if (inm->inm_ifp == ifp &&
                                    inm->inm_timer == 0 &&
@@ -248,11 +275,14 @@
                                        inm->inm_state = IGMP_DELAYING_MEMBER;
                                        inm->inm_timer = IGMP_RANDOM_DELAY(
                                            IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
-                                       igmp_timers_are_running = 1;
+                                       igmp_timers_on = true;
                                }
-                               IN_NEXT_MULTI(step, inm);
+                               inm = in_next_multi(&step);
                        }
+                       in_multi_unlock();
                } else {
+                       struct in_multistep step;
+
                        if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
                                IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
                                m_freem(m);
@@ -261,7 +291,7 @@
 
                        timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
                        if (timer == 0)
-                               timer =1;
+                               timer = 1;
 
                        /*
                         * Start the timers in all of our membership records
@@ -271,7 +301,8 @@
                         * timers already running, check if they need to be
                         * reset.
                         */
-                       IN_FIRST_MULTI(step, inm);
+                       in_multi_lock(RW_WRITER);
+                       inm = in_first_multi(&step);
                        while (inm != NULL) {
                                if (inm->inm_ifp == ifp &&
                                    !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
@@ -289,7 +320,7 @@
                                                    IGMP_DELAYING_MEMBER;
                                                inm->inm_timer =
                                                    IGMP_RANDOM_DELAY(timer);
-                                               igmp_timers_are_running = 1;
+                                               igmp_timers_on = true;
                                                break;
                                        case IGMP_SLEEPING_MEMBER:
                                                inm->inm_state =
@@ -297,8 +328,9 @@
                                                break;
                                        }
                                }
-                               IN_NEXT_MULTI(step, inm);
+                               inm = in_next_multi(&step);
                        }
+                       in_multi_unlock();
                }
 
                break;
@@ -335,7 +367,8 @@



Home | Main Index | Thread Index | Old Index