We've finally got srp and art to the point where we can use srp to manage the
internal links in the art data structures. This allows us to do route lookups
without holding any locks, which is kind of nice.
As we're not doing unlocked insert, delete or walk (yet), those art functions
use the locked variants of the srp functions. Currently the lock that
protects those operations is the kernel lock.
Callers to art_lookup and art_match now pass in a pointer to an srp_ref, which
is always active on return, even if no route is found. In some situations we
use these functions while modifying the routing table, in which case the
kernel lock already protects against concurrent modifications so the srp_ref
is unnecessary, so we srp_leave it immediately.
I'd also like to draw attention to the comment in rtable_match. Is it OK that
we might choose a multipath route at random if the set of available routes
changes while we're choosing?
Index: art.h
===================================================================
RCS file: /cvs/src/sys/net/art.h,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 art.h
--- art.h 3 Jun 2016 03:59:43 -0000 1.13
+++ art.h 6 Jun 2016 00:32:31 -0000
@@ -19,13 +19,15 @@
#ifndef _NET_ART_H_
#define _NET_ART_H_
+#include <sys/srp.h>
+
#define ART_MAXLVL 32 /* We currently use 32 levels for IPv6. */
/*
* Root of the ART tables, equivalent to the radix head.
*/
struct art_root {
- struct art_table *ar_root; /* First table */
+ struct srp ar_root; /* First table */
uint8_t ar_bits[ART_MAXLVL]; /* Per level stride */
uint8_t ar_nlvl; /* Number of levels */
uint8_t ar_alen; /* Address length in bits */
@@ -59,8 +61,9 @@ struct art_node *art_insert(struct art_r
int);
struct art_node *art_delete(struct art_root *, struct art_node *, uint8_t *,
int);
-struct art_node *art_match(struct art_root *, uint8_t *);
-struct art_node *art_lookup(struct art_root *, uint8_t *, int);
+struct art_node *art_match(struct art_root *, uint8_t *, struct srp_ref
*);
+struct art_node *art_lookup(struct art_root *, uint8_t *, int,
+ struct srp_ref *);
int art_walk(struct art_root *,
int (*)(struct art_node *, void *), void *);
Index: art.c
===================================================================
RCS file: /cvs/src/sys/net/art.c,v
retrieving revision 1.18
diff -u -p -u -p -r1.18 art.c
--- art.c 3 Jun 2016 03:59:43 -0000 1.18
+++ art.c 6 Jun 2016 00:32:31 -0000
@@ -37,8 +37,8 @@
#include <net/art.h>
-#define ISLEAF(e) (((unsigned long)(e).node & 1) == 0)
-#define SUBTABLE(e) (((struct art_table *)((unsigned long)(e).child & ~1)))
+#define ISLEAF(e) (((unsigned long)(e) & 1) == 0)
+#define SUBTABLE(e) ((struct art_table *)((unsigned long)(e) & ~1))
#define ASNODE(t) ((struct art_node *)((unsigned long)(t) | 1))
/*
@@ -58,8 +58,7 @@ struct art_table {
* is a route counter.
*/
union {
- struct art_node *node;
- struct art_table *child;
+ struct srp node;
unsigned long count;
} *at_heap; /* Array of 2^(slen+1) items */
};
@@ -249,41 +248,60 @@ art_findex(struct art_table *at, uint8_t
* Return the best existing match for a destination.
*/
struct art_node *
-art_match(struct art_root *ar, uint8_t *addr)
+art_match(struct art_root *ar, uint8_t *addr, struct srp_ref *nsr)
{
+ struct srp_ref dsr, ndsr;
+ void *entry;
struct art_table *at;
- struct art_node *dflt = NULL;
- int j;
+ struct art_node *dflt, *ndflt;
+ int j;
+
+ entry = srp_enter(nsr, &ar->ar_root);
+ at = entry;
- at = ar->ar_root;
if (at == NULL)
- return (NULL);
+ goto done;
+
+ /*
+ * Remember the default route of each table we visit in case
+ * we do not find a better matching route.
+ */
+ dflt = srp_enter(&dsr, &at->at_default);
/*
* Iterate until we find a leaf.
*/
while (1) {
- /*
- * Rember the default route of this table in case
- * we do not find a better matching route.
- */
- if (at->at_default != NULL)
- dflt = at->at_default;
-
/* Do a single level route lookup. */
j = art_findex(at, addr);
+ entry = srp_follow(nsr, &at->at_heap[j].node);
- /* If this is a leaf we're done. */
- if (ISLEAF(at->at_heap[j]))
+ /* If this is a leaf (NULL is a leaf) we're done. */
+ if (ISLEAF(entry))
break;
- at = SUBTABLE(at->at_heap[j]);
+ at = SUBTABLE(entry);
+
+ ndflt = srp_enter(&ndsr, &at->at_default);
+ if (ndflt != NULL) {
+ srp_leave(&dsr);
+ dsr = ndsr;
+ dflt = ndflt;
+ } else
+ srp_leave(&ndsr);
}
- if (at->at_heap[j].node != NULL)
- return (at->at_heap[j].node);
+ if (entry == NULL) {
+ srp_leave(nsr);
+ *nsr = dsr;
+ KASSERT(ISLEAF(dflt));
+ return (dflt);
+ }
- return (dflt);
+ srp_leave(&dsr);
+done:
+ KASSERT(ISLEAF(entry));
+ return (entry);
}
/*
@@ -293,21 +311,25 @@ art_match(struct art_root *ar, uint8_t *
* it does not exist.
*/
struct art_node *
-art_lookup(struct art_root *ar, uint8_t *addr, int plen)
+art_lookup(struct art_root *ar, uint8_t *addr, int plen, struct srp_ref *nsr)
{
+ void *entry;
struct art_table *at;
- struct art_node *an;
int i, j;
KASSERT(plen >= 0 && plen <= ar->ar_alen);
- at = ar->ar_root;
+ entry = srp_enter(nsr, &ar->ar_root);
+ at = entry;
+
if (at == NULL)
- return (NULL);
+ goto done;
/* Default route */
- if (plen == 0)
- return (at->at_default);
+ if (plen == 0) {
+ entry = srp_follow(nsr, &at->at_default);
+ goto done;
+ }
/*
* If the prefix length is smaller than the sum of
@@ -317,24 +339,26 @@ art_lookup(struct art_root *ar, uint8_t
while (plen > (at->at_offset + at->at_bits)) {
/* Do a single level route lookup. */
j = art_findex(at, addr);
+ entry = srp_follow(nsr, &at->at_heap[j].node);
/* A leaf is a match, but not a perfect one. */
- if (ISLEAF(at->at_heap[j]))
+ if (ISLEAF(entry))
return (NULL);
- at = SUBTABLE(at->at_heap[j]);
+ at = SUBTABLE(entry);
}
i = art_bindex(at, addr, plen);
if (i == -1)
return (NULL);
- if (!ISLEAF(at->at_heap[i]))
- an = SUBTABLE(at->at_heap[i])->at_default;
- else
- an = at->at_heap[i].node;
-
- return (an);
+ entry = srp_follow(nsr, &at->at_heap[i].node);
+ if (!ISLEAF(entry))
+ entry = srp_follow(nsr, &SUBTABLE(entry)->at_default);
+
+done:
+ KASSERT(ISLEAF(entry));
+ return (entry);
}
@@ -347,27 +371,30 @@ art_lookup(struct art_root *ar, uint8_t
struct art_node *
art_insert(struct art_root *ar, struct art_node *an, uint8_t *addr, int plen)
{
- struct art_table *at;
+ struct art_table *at, *child;
+ struct art_node *node;
int i, j;
+ KERNEL_ASSERT_LOCKED();
KASSERT(plen >= 0 && plen <= ar->ar_alen);
- at = ar->ar_root;
+ at = srp_get_locked(&ar->ar_root);
if (at == NULL) {
at = art_table_get(ar, NULL, -1);
if (at == NULL)
return (NULL);
- ar->ar_root = at;
+ srp_swap_locked(&ar->ar_root, at);
}
/* Default route */
if (plen == 0) {
- if (at->at_default != NULL)
- return (at->at_default);
+ node = srp_get_locked(&at->at_default);
+ if (node != NULL)
+ return (node);
art_table_ref(ar, at);
- at->at_default = an;
+ srp_swap_locked(&at->at_default, an);
return (an);
}
@@ -379,6 +406,7 @@ art_insert(struct art_root *ar, struct a
while (plen > (at->at_offset + at->at_bits)) {
/* Do a single level route lookup. */
j = art_findex(at, addr);
+ node = srp_get_locked(&at->at_heap[j].node);
/*
* If the node corresponding to the fringe index is
@@ -386,18 +414,16 @@ art_insert(struct art_root *ar, struct a
* entry of this node will then become the default
* route of the subtable.
*/
- if (ISLEAF(at->at_heap[j])) {
- struct art_table *child;
-
+ if (ISLEAF(node)) {
child = art_table_get(ar, at, j);
if (child == NULL)
return (NULL);
art_table_ref(ar, at);
- at->at_heap[j].node = ASNODE(child);
- }
-
- at = SUBTABLE(at->at_heap[j]);
+ srp_swap_locked(&at->at_heap[j].node, ASNODE(child));
+ at = child;
+ } else
+ at = SUBTABLE(node);
}
i = art_bindex(at, addr, plen);
@@ -414,12 +440,13 @@ struct art_node *
art_table_insert(struct art_root *ar, struct art_table *at, int i,
struct art_node *an)
{
- struct art_node *prev;
+ struct art_node *prev, *node;
- if (!ISLEAF(at->at_heap[i]))
- prev = SUBTABLE(at->at_heap[i])->at_default;
+ node = srp_get_locked(&at->at_heap[i].node);
+ if (!ISLEAF(node))
+ prev = srp_get_locked(&SUBTABLE(node)->at_default);
else
- prev = at->at_heap[i].node;
+ prev = node;
if (art_check_duplicate(ar, prev, an))
return (prev);
@@ -433,10 +460,10 @@ art_table_insert(struct art_root *ar, st
*/
if (i < at->at_minfringe)
art_allot(at, i, prev, an);
- else if (!ISLEAF(at->at_heap[i]))
- SUBTABLE(at->at_heap[i])->at_default = an;
+ else if (!ISLEAF(node))
+ srp_swap_locked(&SUBTABLE(node)->at_default, an);
else
- at->at_heap[i].node = an;
+ srp_swap_locked(&at->at_heap[i].node, an);
return (an);
}
@@ -449,21 +476,22 @@ struct art_node *
art_delete(struct art_root *ar, struct art_node *an, uint8_t *addr, int plen)
{
struct art_table *at;
- struct art_node *dflt;
+ struct art_node *node;
int i, j;
+ KERNEL_ASSERT_LOCKED();
KASSERT(plen >= 0 && plen <= ar->ar_alen);
- at = ar->ar_root;
+ at = srp_get_locked(&ar->ar_root);
if (at == NULL)
return (NULL);
/* Default route */
if (plen == 0) {
- dflt = at->at_default;
- at->at_default = NULL;
+ node = srp_get_locked(&at->at_default);
+ srp_swap_locked(&at->at_default, NULL);
art_table_free(ar, at);
- return (dflt);
+ return (node);
}
/*
@@ -474,12 +502,13 @@ art_delete(struct art_root *ar, struct a
while (plen > (at->at_offset + at->at_bits)) {
/* Do a single level route lookup. */
j = art_findex(at, addr);
+ node = srp_get_locked(&at->at_heap[j].node);
/* If this is a leaf, there is no route to delete. */
- if (ISLEAF(at->at_heap[j]))
+ if (ISLEAF(node))
return (NULL);
- at = SUBTABLE(at->at_heap[j]);
+ at = SUBTABLE(node);
}
i = art_bindex(at, addr, plen);
@@ -494,24 +523,27 @@ art_delete(struct art_root *ar, struct a
*/
struct art_node *
art_table_delete(struct art_root *ar, struct art_table *at, int i,
- struct art_node *node)
+ struct art_node *an)
{
- struct art_node *next;
+ struct art_node *next, *node;
#ifdef DIAGNOSTIC
struct art_node *prev;
+#endif
- if (!ISLEAF(at->at_heap[i]))
- prev = SUBTABLE(at->at_heap[i])->at_default;
+ node = srp_get_locked(&at->at_heap[i].node);
+#ifdef DIAGNOSTIC
+ if (!ISLEAF(node))
+ prev = srp_get_locked(&SUBTABLE(node)->at_default);
else
- prev = at->at_heap[i].node;
+ prev = node;
- KASSERT(prev == node);
+ KASSERT(prev == an);
#endif
/* Get the next most specific route for the index `i'. */
if ((i >> 1) > 1)
- next = at->at_heap[i >> 1].node;
+ next = srp_get_locked(&at->at_heap[i >> 1].node);
else
next = NULL;
@@ -521,16 +553,16 @@ art_table_delete(struct art_root *ar, st
* route pointer to all the corresponding fringe indices.
*/
if (i < at->at_minfringe)
- art_allot(at, i, node, next);
- else if (!ISLEAF(at->at_heap[i]))
- SUBTABLE(at->at_heap[i])->at_default = next;
+ art_allot(at, i, an, next);
+ else if (!ISLEAF(node))
+ srp_swap_locked(&SUBTABLE(node)->at_default, next);
else
- at->at_heap[i].node = next;
+ srp_swap_locked(&at->at_heap[i].node, next);
/* We have removed an entry from this table. */
art_table_free(ar, at);
- return (node);
+ return (an);
}
void
@@ -539,17 +571,26 @@ art_table_ref(struct art_root *ar, struc
at->at_refcnt++;
}
+static inline int
+art_table_rele(struct art_table *at)
+{
+ if (at == NULL)
+ return (0);
+
+ return (--at->at_refcnt == 0);
+}
+
int
art_table_free(struct art_root *ar, struct art_table *at)
{
- if (--at->at_refcnt == 0) {
+ if (art_table_rele(at)) {
/*
* Garbage collect this table and all its parents
* that are empty.
*/
do {
at = art_table_put(ar, at);
- } while (at != NULL && --at->at_refcnt == 0);
+ } while (art_table_rele(at));
return (1);
}
@@ -564,9 +605,12 @@ int
art_walk(struct art_root *ar, int (*f)(struct art_node *, void *), void *arg)
{
struct art_table *at;
+ struct art_node *node;
int error;
- at = ar->ar_root;
+ KERNEL_ASSERT_LOCKED();
+
+ at = srp_get_locked(&ar->ar_root);
if (at == NULL)
return (0);
@@ -574,8 +618,9 @@ art_walk(struct art_root *ar, int (*f)(s
* The default route should be processed here because the root
* table does not have a parent.
*/
- if (at->at_default != NULL) {
- error = (*f)(at->at_default, arg);
+ node = srp_get_locked(&at->at_default);
+ if (node != NULL) {
+ error = (*f)(node, arg);
if (error)
return (error);
}
@@ -588,6 +633,7 @@ art_table_walk(struct art_root *ar, stru
int (*f)(struct art_node *, void *), void *arg)
{
struct art_node *next, *an = NULL;
+ struct art_node *node;
int i, j, error = 0;
uint32_t maxfringe = (at->at_minfringe << 1);
@@ -604,8 +650,8 @@ art_table_walk(struct art_root *ar, stru
* be processed more than once.
*/
for (i = max(j, 2); i < at->at_minfringe; i <<= 1) {
- next = at->at_heap[i >> 1].node;
- an = at->at_heap[i].node;
+ next = srp_get_locked(&at->at_heap[i >> 1].node);
+ an = srp_get_locked(&at->at_heap[i].node);
if ((an != NULL) && (an != next)) {
error = (*f)(an, arg);
if (error)
@@ -618,21 +664,23 @@ art_table_walk(struct art_root *ar, stru
* Iterate fringe nodes.
*/
for (i = at->at_minfringe; i < maxfringe; i++) {
- next = at->at_heap[i >> 1].node;
- if (!ISLEAF(at->at_heap[i]))
- an = SUBTABLE(at->at_heap[i])->at_default;
+ next = srp_get_locked(&at->at_heap[i >> 1].node);
+ node = srp_get_locked(&at->at_heap[i].node);
+ if (!ISLEAF(node))
+ an = srp_get_locked(&SUBTABLE(node)->at_default);
else
- an = at->at_heap[i].node;
+ an = node;
+
if ((an != NULL) && (an != next)) {
error = (*f)(an, arg);
if (error)
goto out;
}
- if (ISLEAF(at->at_heap[i]))
+ if (ISLEAF(node))
continue;
- error = art_table_walk(ar, SUBTABLE(at->at_heap[i]), f, arg);
+ error = art_table_walk(ar, SUBTABLE(node), f, arg);
if (error)
break;
}
@@ -652,6 +700,7 @@ struct art_table *
art_table_get(struct art_root *ar, struct art_table *parent, int j)
{
struct art_table *at;
+ struct art_node *node;
void *at_heap;
uint32_t lvl;
@@ -694,7 +743,9 @@ art_table_get(struct art_root *ar, struc
at->at_refcnt = 0;
if (parent != NULL) {
- at->at_default = parent->at_heap[j].node;
+ node = srp_get_locked(&parent->at_heap[j].node);
+ /* node isn't being deleted, no srp_finalize needed */
+ srp_swap_locked(&at->at_default, node);
at->at_offset = (parent->at_offset + parent->at_bits);
}
@@ -711,20 +762,24 @@ struct art_table *
art_table_put(struct art_root *ar, struct art_table *at)
{
struct art_table *parent = at->at_parent;
- uint32_t lvl = at->at_level;
+ struct art_node *node;
uint32_t j = at->at_index;
+ KASSERT(at->at_refcnt == 0);
KASSERT(j != 0 && j != 1);
- KASSERT(parent != NULL || j == -1);
if (parent != NULL) {
- KASSERT(lvl == parent->at_level + 1);
+ KASSERT(j != -1);
+ KASSERT(at->at_level == parent->at_level + 1);
KASSERT(parent->at_refcnt >= 1);
/* Give the route back to its parent. */
- parent->at_heap[j].node = at->at_default;
+ node = srp_get_locked(&at->at_default);
+ srp_swap_locked(&parent->at_heap[j].node, node);
} else {
- ar->ar_root = NULL;
+ KASSERT(j == -1);
+ KASSERT(at->at_level == 0);
+ srp_swap_locked(&ar->ar_root, NULL);
}
mtx_enter(&art_table_gc_mtx);
@@ -750,6 +805,11 @@ art_table_gc(void *null)
while (at != NULL) {
next = at->at_parent;
+ if (at->at_level == 0)
+ srp_finalize(at, "arttfini");
+ else
+ srp_finalize(ASNODE(at), "arttfini");
+
switch (AT_HEAPSIZE(at->at_bits)) {
case AT_HEAPSIZE(4):
pool_put(&at_heap_4_pool, at->at_heap);
@@ -794,7 +854,8 @@ void
art_allot(struct art_table *at, int i, struct art_node *old,
struct art_node *new)
{
- int k = i;
+ struct art_node *node, *dflt;
+ int k = i;
KASSERT(i < at->at_minfringe);
@@ -805,12 +866,15 @@ again:
/* Change fringe nodes. */
while (1) {
- if (!ISLEAF(at->at_heap[k])) {
- if (SUBTABLE(at->at_heap[k])->at_default == old) {
- SUBTABLE(at->at_heap[k])->at_default = new;
+ node = srp_get_locked(&at->at_heap[k].node);
+ if (!ISLEAF(node)) {
+ dflt = srp_get_locked(&SUBTABLE(node)->at_default);
+ if (dflt == old) {
+ srp_swap_locked(&SUBTABLE(node)->at_default,
+ new);
}
- } else if (at->at_heap[k].node == old) {
- at->at_heap[k].node = new;
+ } else if (node == old) {
+ srp_swap_locked(&at->at_heap[k].node, new);
}
if (k % 2)
goto moveup;
@@ -818,7 +882,8 @@ again:
}
nonfringe:
- if (at->at_heap[k].node == old)
+ node = srp_get_locked(&at->at_heap[k].node);
+ if (node == old)
goto again;
moveon:
if (k % 2)
@@ -827,7 +892,7 @@ moveon:
goto nonfringe;
moveup:
k >>= 1;
- at->at_heap[k].node = new;
+ srp_swap_locked(&at->at_heap[k].node, new);
/* Change non-fringe node. */
if (k != i)
@@ -875,6 +940,8 @@ art_gc(void *null)
while (an != NULL) {
next = an->an_gc;
+
+ srp_finalize(an, "artnfini");
pool_put(&an_pool, an);
Index: rtable.c
===================================================================
RCS file: /cvs/src/sys/net/rtable.c,v
retrieving revision 1.45
diff -u -p -u -p -r1.45 rtable.c
--- rtable.c 1 Jun 2016 09:46:19 -0000 1.45
+++ rtable.c 6 Jun 2016 00:32:31 -0000
@@ -535,8 +535,8 @@ rtable_lookup(unsigned int rtableid, str
{
struct art_root *ar;
struct art_node *an;
- struct rtentry *rt;
- struct srp_ref sr;
+ struct rtentry *rt = NULL;
+ struct srp_ref sr, nsr;
uint8_t *addr;
int plen;
@@ -548,19 +548,20 @@ rtable_lookup(unsigned int rtableid, str
/* No need for a perfect match. */
if (mask == NULL) {
- an = art_match(ar, addr);
+ an = art_match(ar, addr, &nsr);
if (an == NULL)
- return (NULL);
+ goto out;
} else {
plen = rtable_satoplen(dst->sa_family, mask);
if (plen == -1)
return (NULL);
- an = art_lookup(ar, addr, plen);
+ an = art_lookup(ar, addr, plen, &nsr);
+
/* Make sure we've got a perfect match. */
if (an == NULL || an->an_plen != plen ||
memcmp(an->an_dst, dst, dst->sa_len))
- return (NULL);
+ goto out;
}
#ifdef SMALL_KERNEL
@@ -578,14 +579,13 @@ rtable_lookup(unsigned int rtableid, str
memcmp(rt->rt_gateway, gateway, gateway->sa_len) == 0)
break;
}
- if (rt == NULL) {
- SRPL_LEAVE(&sr);
- return (NULL);
- }
#endif /* SMALL_KERNEL */
+ if (rt != NULL)
+ rtref(rt);
- rtref(rt);
SRPL_LEAVE(&sr);
+out:
+ srp_leave(&nsr);
return (rt);
}
@@ -596,7 +596,7 @@ rtable_match(unsigned int rtableid, stru
struct art_root *ar;
struct art_node *an;
struct rtentry *rt = NULL;
- struct srp_ref sr;
+ struct srp_ref sr, nsr;
uint8_t *addr;
#ifndef SMALL_KERNEL
int hash;
@@ -608,8 +608,7 @@ rtable_match(unsigned int rtableid, stru
addr = satoaddr(ar, dst);
- KERNEL_LOCK();
- an = art_match(ar, addr);
+ an = art_match(ar, addr, &nsr);
if (an == NULL)
goto out;
@@ -634,6 +633,16 @@ rtable_match(unsigned int rtableid, stru
threshold = (0xffff / npaths) + 1;
+ /*
+ * we have no protection against concurrent modification of the
+ * route list attached to the node, so we won't necessarily
+ * have the same number of routes. for most modifications,
+ * we'll pick a route that we wouldn't have if we only saw the
+ * list before or after the change. if we were going to use
+ * the last available route, but it got removed, we'll hit
+ * the end of the list and then pick the first route.
+ */
+
mrt = SRPL_ENTER(&sr, &an->an_rtlist);
while (hash > threshold && mrt != NULL) {
if (mrt->rt_priority == rt->rt_priority)
@@ -650,7 +659,7 @@ rtable_match(unsigned int rtableid, stru
}
#endif /* SMALL_KERNEL */
out:
- KERNEL_UNLOCK();
+ srp_leave(&nsr);
return (rt);
}
@@ -661,12 +670,14 @@ rtable_insert(unsigned int rtableid, str
{
#ifndef SMALL_KERNEL
struct rtentry *mrt;
+ struct srp_ref sr;
#endif /* SMALL_KERNEL */
struct art_root *ar;
struct art_node *an, *prev;
uint8_t *addr;
int plen;
unsigned int rt_flags;
+ int error = 0;
KERNEL_ASSERT_LOCKED();
@@ -679,12 +690,15 @@ rtable_insert(unsigned int rtableid, str
if (plen == -1)
return (EINVAL);
+ rtref(rt); /* guarantee rtfree won't do anything during insert */
+
#ifndef SMALL_KERNEL
/* Do not permit exactly the same dst/mask/gw pair. */
- an = art_lookup(ar, addr, plen);
+ an = art_lookup(ar, addr, plen, &sr);
+ srp_leave(&sr); /* an can't go away while we have the lock */
if (an != NULL && an->an_plen == plen &&
!memcmp(an->an_dst, dst, dst->sa_len)) {
- struct rtentry *mrt;
+ struct rtentry *mrt;
int mpathok = ISSET(rt->rt_flags, RTF_MPATH);
SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next) {
@@ -695,15 +709,18 @@ rtable_insert(unsigned int rtableid, str
if (!mpathok ||
(mrt->rt_gateway->sa_len == gateway->sa_len &&
!memcmp(mrt->rt_gateway, gateway,
gateway->sa_len))){
- return (EEXIST);
+ error = EEXIST;
+ goto leave;
}
}
}
#endif /* SMALL_KERNEL */
an = art_get(dst, plen);
- if (an == NULL)
- return (ENOBUFS);
+ if (an == NULL) {
+ error = ENOBUFS;
+ goto leave;
+ }
/* prepare for immediate operation if insert succeeds */
rt_flags = rt->rt_flags;
@@ -718,9 +735,11 @@ rtable_insert(unsigned int rtableid, str
rt_next);
rt->rt_flags = rt_flags;
art_put(an);
-
- if (prev == NULL)
- return ESRCH;
+
+ if (prev == NULL) {
+ error = ESRCH;
+ goto leave;
+ }
#ifndef SMALL_KERNEL
an = prev;
@@ -752,11 +771,12 @@ rtable_insert(unsigned int rtableid, str
/* Put newly inserted entry at the right place. */
rtable_mpath_reprio(rtableid, dst, mask, rt->rt_priority, rt);
#else
- return (EEXIST);
+ error = EEXIST;
#endif /* SMALL_KERNEL */
}
-
- return (0);
+leave:
+ rtfree(rt);
+ return (error);
}
int
@@ -765,6 +785,7 @@ rtable_delete(unsigned int rtableid, str
{
struct art_root *ar;
struct art_node *an;
+ struct srp_ref sr;
uint8_t *addr;
int plen;
#ifndef SMALL_KERNEL
@@ -772,8 +793,6 @@ rtable_delete(unsigned int rtableid, str
int npaths = 0;
#endif /* SMALL_KERNEL */
- KERNEL_ASSERT_LOCKED();
-
ar = rtable_get(rtableid, dst->sa_family);
if (ar == NULL)
return (EAFNOSUPPORT);
@@ -781,7 +800,11 @@ rtable_delete(unsigned int rtableid, str
addr = satoaddr(ar, dst);
plen = rtable_satoplen(dst->sa_family, mask);
- an = art_lookup(ar, addr, plen);
+ KERNEL_ASSERT_LOCKED();
+
+ 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))
@@ -796,7 +819,7 @@ rtable_delete(unsigned int rtableid, str
npaths++;
if (npaths > 1) {
- KASSERT(rt->rt_refcnt >= 2);
+ KASSERT(rt->rt_refcnt >= 1);
SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry,
rt_next);
@@ -811,7 +834,7 @@ rtable_delete(unsigned int rtableid, str
if (art_delete(ar, an, addr, plen) == NULL)
return (ESRCH);
- KASSERT(rt->rt_refcnt >= 2);
+ KASSERT(rt->rt_refcnt >= 1);
SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next);
art_put(an);
@@ -879,6 +902,7 @@ rtable_mpath_reprio(unsigned int rtablei
{
struct art_root *ar;
struct art_node *an;
+ struct srp_ref sr;
uint8_t *addr;
int plen;
struct rtentry *mrt, *prt = NULL;
@@ -890,13 +914,15 @@ rtable_mpath_reprio(unsigned int rtablei
addr = satoaddr(ar, dst);
plen = rtable_satoplen(dst->sa_family, mask);
- an = art_lookup(ar, addr, plen);
+ KERNEL_ASSERT_LOCKED();
+
+ 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);
-
- KERNEL_ASSERT_LOCKED();
rtref(rt); /* keep rt alive in between remove and add */
SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next);