This removes ART's reliance on the kernel lock to serialise updates.
I sent out an earlier version of this prior to 6.0, but it didn't make it in.
Since then, we've decided to go with rwlocks in the packet processing path,
so here's a version with an rwlock instead of a mutex.


Index: art.c
===================================================================
RCS file: /cvs/src/sys/net/art.c,v
retrieving revision 1.22
diff -u -p -r1.22 art.c
--- art.c       19 Jul 2016 10:51:44 -0000      1.22
+++ art.c       14 Aug 2016 11:10:45 -0000
@@ -152,6 +152,7 @@ art_alloc(unsigned int rtableid, unsigne
 
        ar->ar_off = off;
        ar->ar_rtableid = rtableid;
+       rw_init(&ar->ar_lock, "art");
 
        return (ar);
 }
@@ -378,7 +379,7 @@ art_insert(struct art_root *ar, struct a
        struct art_node         *node;
        int                      i, j;
 
-       KERNEL_ASSERT_LOCKED();
+       rw_assert_wrlock(&ar->ar_lock);
        KASSERT(plen >= 0 && plen <= ar->ar_alen);
 
        at = srp_get_locked(&ar->ar_root);
@@ -482,7 +483,7 @@ art_delete(struct art_root *ar, struct a
        struct art_node         *node;
        int                      i, j;
 
-       KERNEL_ASSERT_LOCKED();
+       rw_assert_wrlock(&ar->ar_lock);
        KASSERT(plen >= 0 && plen <= ar->ar_alen);
 
        at = srp_get_locked(&ar->ar_root);
@@ -613,7 +614,7 @@ art_walk(struct art_root *ar, int (*f)(s
        struct art_node         *node;
        int                      error = 0;
 
-       KERNEL_LOCK();
+       rw_enter_write(&ar->ar_lock);
        at = srp_get_locked(&ar->ar_root);
        if (at != NULL) {
                art_table_ref(ar, at);
@@ -631,7 +632,7 @@ art_walk(struct art_root *ar, int (*f)(s
 
                art_table_free(ar, at);
        }
-       KERNEL_UNLOCK();
+       rw_exit_write(&ar->ar_lock);
 
        return (error);
 }
@@ -708,9 +709,9 @@ art_walk_apply(struct art_root *ar,
 
        if ((an != NULL) && (an != next)) {
                /* this assumes an->an_dst is not used by f */
-               KERNEL_UNLOCK();
+               rw_exit_write(&ar->ar_lock);
                error = (*f)(an, arg);
-               KERNEL_LOCK();
+               rw_enter_write(&ar->ar_lock);
        }
 
        return (error);
Index: art.h
===================================================================
RCS file: /cvs/src/sys/net/art.h,v
retrieving revision 1.14
diff -u -p -r1.14 art.h
--- art.h       14 Jun 2016 04:42:02 -0000      1.14
+++ art.h       14 Aug 2016 11:10:45 -0000
@@ -19,6 +19,8 @@
 #ifndef _NET_ART_H_
 #define _NET_ART_H_
 
+#include <sys/rwlock.h>
+
 #define ART_MAXLVL     32      /* We currently use 32 levels for IPv6. */
 
 /*
@@ -26,6 +28,7 @@
  */
 struct art_root {
        struct srp               ar_root;       /* First table */
+       struct rwlock            ar_lock;       /* Serialise modifications */
        uint8_t                  ar_bits[ART_MAXLVL];   /* Per level stride */
        uint8_t                  ar_nlvl;       /* Number of levels */
        uint8_t                  ar_alen;       /* Address length in bits */
Index: rtable.c
===================================================================
RCS file: /cvs/src/sys/net/rtable.c,v
retrieving revision 1.50
diff -u -p -r1.50 rtable.c
--- rtable.c    19 Jul 2016 10:51:44 -0000      1.50
+++ rtable.c    14 Aug 2016 11:10:45 -0000
@@ -515,6 +515,10 @@ static inline uint8_t      *satoaddr(struct a
 void   rtentry_ref(void *, void *);
 void   rtentry_unref(void *, void *);
 
+#ifndef SMALL_KERNEL
+void   rtable_mpath_insert(struct art_node *, struct rtentry *);
+#endif
+
 struct srpl_rc rt_rc = SRPL_RC_INITIALIZER(rtentry_ref, rtentry_unref, NULL);
 
 void
@@ -679,8 +683,6 @@ rtable_insert(unsigned int rtableid, str
        unsigned int                     rt_flags;
        int                              error = 0;
 
-       KERNEL_ASSERT_LOCKED();
-
        ar = rtable_get(rtableid, dst->sa_family);
        if (ar == NULL)
                return (EAFNOSUPPORT);
@@ -691,6 +693,7 @@ rtable_insert(unsigned int rtableid, str
                return (EINVAL);
 
        rtref(rt); /* guarantee rtfree won't do anything during insert */
+       rw_enter_write(&ar->ar_lock);
 
 #ifndef SMALL_KERNEL
        /* Do not permit exactly the same dst/mask/gw pair. */
@@ -766,15 +769,14 @@ rtable_insert(unsigned int rtableid, str
                        }
                }
 
-               SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next);
-
                /* Put newly inserted entry at the right place. */
-               rtable_mpath_reprio(rtableid, dst, mask, rt->rt_priority, rt);
+               rtable_mpath_insert(an, rt);
 #else
                error = EEXIST;
 #endif /* SMALL_KERNEL */
        }
 leave:
+       rw_exit_write(&ar->ar_lock);
        rtfree(rt);
        return (error);
 }
@@ -792,6 +794,7 @@ rtable_delete(unsigned int rtableid, str
        struct rtentry                  *mrt;
        int                              npaths = 0;
 #endif /* SMALL_KERNEL */
+       int                              error = 0;
 
        ar = rtable_get(rtableid, dst->sa_family);
        if (ar == NULL)
@@ -800,15 +803,17 @@ rtable_delete(unsigned int rtableid, str
        addr = satoaddr(ar, dst);
        plen = rtable_satoplen(dst->sa_family, mask);
 
-       KERNEL_ASSERT_LOCKED();
-
+       rtref(rt); /* guarantee rtfree won't do anything under ar_lock */
+       rw_enter_write(&ar->ar_lock);
        an = art_lookup(ar, addr, plen, &sr);
        srp_leave(&sr); /* an can't go away while we have the lock */
 
        /* Make sure we've got a perfect match. */
        if (an == NULL || an->an_plen != plen ||
-           memcmp(an->an_dst, dst, dst->sa_len))
-               return (ESRCH);
+           memcmp(an->an_dst, dst, dst->sa_len)) {
+               error = ESRCH;
+               goto leave;
+       }
 
 #ifndef SMALL_KERNEL
        /*
@@ -827,18 +832,23 @@ rtable_delete(unsigned int rtableid, str
                an->an_dst = mrt->rt_dest;
                if (npaths == 2)
                        mrt->rt_flags &= ~RTF_MPATH;
-               return (0);
+
+               goto leave;
        }
 #endif /* SMALL_KERNEL */
 
        if (art_delete(ar, an, addr, plen) == NULL)
-               return (ESRCH);
+               panic("art_delete failed to find node %p", an);
 
        KASSERT(rt->rt_refcnt >= 1);
        SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next);
-
        art_put(an);
-       return (0);
+
+leave:
+       rw_exit_write(&ar->ar_lock);
+       rtfree(rt);
+
+       return (error);
 }
 
 struct rtable_walk_cookie {
@@ -905,7 +915,7 @@ rtable_mpath_reprio(unsigned int rtablei
        struct srp_ref                   sr;
        uint8_t                         *addr;
        int                              plen;
-       struct rtentry                  *mrt, *prt = NULL;
+       int                              error = 0;
 
        ar = rtable_get(rtableid, dst->sa_family);
        if (ar == NULL)
@@ -914,19 +924,32 @@ rtable_mpath_reprio(unsigned int rtablei
        addr = satoaddr(ar, dst);
        plen = rtable_satoplen(dst->sa_family, mask);
 
-       KERNEL_ASSERT_LOCKED();
-
+       rw_enter_write(&ar->ar_lock);
        an = art_lookup(ar, addr, plen, &sr);
        srp_leave(&sr); /* an can't go away while we have the lock */
 
        /* Make sure we've got a perfect match. */
        if (an == NULL || an->an_plen != plen ||
            memcmp(an->an_dst, dst, dst->sa_len))
-               return (ESRCH);
+               error = ESRCH;
+       else {
+               rtref(rt); /* keep rt alive in between remove and insert */
+               SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist,
+                   rt, rtentry, rt_next);
+               rt->rt_priority = prio;
+               rtable_mpath_insert(an, rt);
+               rtfree(rt);
+       }
+       rw_exit_write(&ar->ar_lock);
 
-       rtref(rt); /* keep rt alive in between remove and add */
-       SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next);
-       rt->rt_priority = prio;
+       return (error);
+}
+
+void
+rtable_mpath_insert(struct art_node *an, struct rtentry *rt)
+{
+       struct rtentry                  *mrt, *prt = NULL;
+       uint8_t                          prio = rt->rt_priority;
 
        if ((mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)) != NULL) {
                /*
@@ -957,9 +980,6 @@ rtable_mpath_reprio(unsigned int rtablei
        } else {
                SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next);
        }
-       rtfree(rt);
-
-       return (0);
 }
 
 struct rtentry *

Reply via email to