To reduce contention in the fast path we can use a mutex(9) to serialize
read/write operations on the radix tree instead of the KERNEL_LOCK.

Note that the KERNEL_LOCK is still needed to serialize walk/delete/insert
because a call to rtable_delete() can be made inside rtable_walk(), and
we cannot use a mutex for such recursive problem.

I'd be interested to know if this helps MPLS fowarding.

Index: net/rtable.c
===================================================================
RCS file: /cvs/src/sys/net/rtable.c,v
retrieving revision 1.33
diff -u -p -r1.33 rtable.c
--- net/rtable.c        4 Dec 2015 13:42:48 -0000       1.33
+++ net/rtable.c        4 Dec 2015 13:45:53 -0000
@@ -292,6 +292,10 @@ rtable_l2set(unsigned int rtableid, unsi
 }
 
 #ifndef ART
+#include <sys/mutex.h>
+
+struct mutex rn_mtx = MUTEX_INITIALIZER(IPL_NONE);
+
 void
 rtable_init_backend(unsigned int keylen)
 {
@@ -319,15 +323,16 @@ rtable_lookup(unsigned int rtableid, str
 {
        struct radix_node_head  *rnh;
        struct radix_node       *rn;
-       struct rtentry          *rt;
+       struct rtentry          *rt = NULL;
 
        rnh = rtable_get(rtableid, dst->sa_family);
        if (rnh == NULL)
                return (NULL);
 
+       mtx_enter(&rn_mtx);
        rn = rn_lookup(dst, mask, rnh);
        if (rn == NULL || (rn->rn_flags & RNF_ROOT) != 0)
-               return (NULL);
+               goto out;
 
        rt = ((struct rtentry *)rn);
 
@@ -335,11 +340,13 @@ rtable_lookup(unsigned int rtableid, str
        if (rnh->rnh_multipath) {
                rt = rt_mpath_matchgate(rt, gateway, prio);
                if (rt == NULL)
-                       return (NULL);
+                       goto out;
        }
 #endif /* !SMALL_KERNEL */
 
        rtref(rt);
+out:
+       mtx_leave(&rn_mtx);
        return (rt);
 }
 
@@ -354,7 +361,7 @@ rtable_match(unsigned int rtableid, stru
        if (rnh == NULL)
                return (NULL);
 
-       KERNEL_LOCK();
+       mtx_enter(&rn_mtx);
        rn = rn_match(dst, rnh);
        if (rn == NULL || (rn->rn_flags & RNF_ROOT) != 0)
                goto out;
@@ -366,7 +373,7 @@ rtable_match(unsigned int rtableid, stru
        rt = rtable_mpath_select(rt, src);
 #endif /* SMALL_KERNEL */
 out:
-       KERNEL_UNLOCK();
+       mtx_leave(&rn_mtx);
        return (rt);
 }
 
@@ -377,29 +384,37 @@ rtable_insert(unsigned int rtableid, str
 {
        struct radix_node_head  *rnh;
        struct radix_node       *rn = (struct radix_node *)rt;
+       int                      error = 0;
+
+       KERNEL_ASSERT_LOCKED();
 
        rnh = rtable_get(rtableid, dst->sa_family);
        if (rnh == NULL)
                return (EAFNOSUPPORT);
 
+       mtx_enter(&rn_mtx);
 #ifndef SMALL_KERNEL
        if (rnh->rnh_multipath) {
                /* Do not permit exactly the same dst/mask/gw pair. */
                if (rt_mpath_conflict(rnh, dst, mask, gateway, prio,
                    ISSET(rt->rt_flags, RTF_MPATH))) {
-                       return (EEXIST);
+                       error = EEXIST;
+                       goto out;
                }
        }
 #endif /* SMALL_KERNEL */
 
        rn = rn_addroute(dst, mask, rnh, rn, prio);
-       if (rn == NULL)
-               return (ESRCH);
+       if (rn == NULL) {
+               error = ESRCH;
+               goto out;
+       }
 
        rt = ((struct rtentry *)rn);
        rtref(rt);
-
-       return (0);
+out:
+       mtx_leave(&rn_mtx);
+       return (error);
 }
 
 int
@@ -408,22 +423,31 @@ rtable_delete(unsigned int rtableid, str
 {
        struct radix_node_head  *rnh;
        struct radix_node       *rn = (struct radix_node *)rt;
+       int                      error = 0;
+
+       KERNEL_ASSERT_LOCKED();
 
        rnh = rtable_get(rtableid, dst->sa_family);
        if (rnh == NULL)
                return (EAFNOSUPPORT);
 
+       mtx_enter(&rn_mtx);
        rn = rn_delete(dst, mask, rnh, rn);
-       if (rn == NULL)
-               return (ESRCH);
+       if (rn == NULL) {
+               error = ESRCH;
+               goto out;
+       }
 
+#ifdef DIAGNOSTIC
        if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
                panic("active node flags=%x", rn->rn_flags);
+#endif /* DIAGNOSTIC */
 
        rt = ((struct rtentry *)rn);
        rtfree(rt);
-
-       return (0);
+out:
+       mtx_leave(&rn_mtx);
+       return (error);
 }
 
 int
@@ -432,6 +456,8 @@ rtable_walk(unsigned int rtableid, sa_fa
 {
        struct radix_node_head  *rnh;
        int (*f)(struct radix_node *, void *, unsigned int) = (void *)func;
+
+       KERNEL_ASSERT_LOCKED();
 
        rnh = rtable_get(rtableid, af);
        if (rnh == NULL)

Reply via email to