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)