Invalidate rtcache based on a global generation counter

The change introduces a global generation counter that is incremented when any
routes have been added or deleted. When a rtcache caches a rtentry into itself,
it also stores a snapshot of the generation counter. If the snapshot equals to
the global counter, the cache is still valid, otherwise invalidated.

One drawback of the change is that all rtcaches of all protocol families are
invalidated when any routes of any protocol families are added or deleted.
If that matters, we should have separate generation counters based on
protocol families.

This change removes LIST_ENTRY from struct route, which fixes a part of
PR kern/52515.
This commit is contained in:
ozaki-r 2017-09-21 07:15:34 +00:00
parent 598b83d451
commit 0092eb7df6
9 changed files with 42 additions and 121 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: route.c,v 1.198 2017/09/21 04:44:32 ozaki-r Exp $ */
/* $NetBSD: route.c,v 1.199 2017/09/21 07:15:34 ozaki-r Exp $ */
/*-
* Copyright (c) 1998, 2008 The NetBSD Foundation, Inc.
@ -97,7 +97,7 @@
#endif
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.198 2017/09/21 04:44:32 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.199 2017/09/21 07:15:34 ozaki-r Exp $");
#include <sys/param.h>
#ifdef RTFLUSH_DEBUG
@ -203,6 +203,13 @@ static void rt_timer_timer(void *);
* - if the caller runs in softint, the caller fails to fetch
* - otherwise, the caller waits for the update completed and retries
* to fetch (probably succeed to fetch for the second time)
* - rtcache invalidation
* - There is a global generation counter that is incremented when
* any routes have been added or deleted
* - When a rtcache caches a rtentry into itself, it also stores
* a snapshot of the generation counter
* - If the snapshot equals to the global counter, the cache is valid,
* otherwise the cache is invalidated
*/
/*
@ -239,6 +246,8 @@ static krwlock_t rtcache_lock __cacheline_aligned;
#define RTCACHE_WLOCKED() false
#endif
static uint64_t rtcache_generation;
/*
* mutex and cv that are used to wait for references to a rtentry left
* before updating the rtentry.
@ -271,14 +280,11 @@ static int _rtcache_debug = 0;
static kauth_listener_t route_listener;
static int rtdeletemsg(struct rtentry *);
static void rtflushall(int);
static void rt_maskedcopy(const struct sockaddr *,
struct sockaddr *, const struct sockaddr *);
static void rtcache_clear(struct route *);
static void rtcache_clear_rtentry(int, struct rtentry *);
static void rtcache_invalidate(struct dom_rtlist *);
static void rtcache_invalidate(void);
static void rt_ref(struct rtentry *);
@ -491,40 +497,17 @@ rt_init(void)
}
static void
rtflushall(int family)
rtcache_invalidate(void)
{
struct domain *dom;
if (rtcache_debug())
printf("%s: enter\n", __func__);
if ((dom = pffinddomain(family)) == NULL)
return;
RTCACHE_WLOCK();
rtcache_invalidate(&dom->dom_rtcache);
rtcache_generation++;
RTCACHE_UNLOCK();
}
static void
rtcache(struct route *ro)
{
struct domain *dom;
RTCACHE_ASSERT_WLOCK();
rtcache_invariants(ro);
KASSERT(ro->_ro_rt != NULL);
KASSERT(ro->ro_invalid == false);
KASSERT(rtcache_getdst(ro) != NULL);
if ((dom = pffinddomain(rtcache_getdst(ro)->sa_family)) == NULL)
return;
LIST_INSERT_HEAD(&dom->dom_rtcache, ro, ro_rtcache_next);
rtcache_invariants(ro);
}
#ifdef RT_DEBUG
static void
dump_rt(const struct rtentry *rt)
@ -1251,7 +1234,7 @@ rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt)
RT_UNLOCK();
need_unlock = false;
rt_timer_remove_all(rt);
rtcache_clear_rtentry(dst->sa_family, rt);
rtcache_invalidate();
#if defined(INET) || defined(INET6)
if (netmask != NULL)
lltable_prefix_free(dst->sa_family, dst, netmask, 0);
@ -1346,7 +1329,7 @@ rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt)
}
RT_UNLOCK();
need_unlock = false;
rtflushall(dst->sa_family);
rtcache_invalidate();
break;
case RTM_GET:
if (netmask != NULL) {
@ -1893,14 +1876,13 @@ _rtcache_init(struct route *ro, int flag)
if (rtcache_getdst(ro) == NULL)
return NULL;
ro->ro_invalid = false;
rt = rtalloc1(rtcache_getdst(ro), flag);
if (rt != NULL && ISSET(rt->rt_flags, RTF_UP)) {
ro->_ro_rt = rt;
ro->ro_rtcache_generation = rtcache_generation;
KASSERT(!ISSET(rt->rt_flags, RTF_UPDATING));
rtcache_ref(rt, ro);
rt_unref(rt);
rtcache(ro);
} else if (rt != NULL)
rt_unref(rt);
@ -1933,7 +1915,7 @@ rtcache_update(struct route *ro, int clone)
{
struct rtentry *rt;
RTCACHE_WLOCK();
rtcache_clear(ro);
ro->_ro_rt = NULL;
rt = _rtcache_init(ro, clone);
RTCACHE_UNLOCK();
return rt;
@ -1958,9 +1940,8 @@ rtcache_copy(struct route *new_ro, struct route *old_ro)
goto out;
RTCACHE_WLOCK();
new_ro->ro_invalid = false;
if ((new_ro->_ro_rt = rt) != NULL)
rtcache(new_ro);
new_ro->_ro_rt = rt;
new_ro->ro_rtcache_generation = rtcache_generation;
rtcache_invariants(new_ro);
RTCACHE_UNLOCK();
out:
@ -1968,8 +1949,6 @@ out:
return;
}
static struct dom_rtlist invalid_routes = LIST_HEAD_INITIALIZER(dom_rtlist);
#if defined(RT_DEBUG) && defined(NET_MPSAFE)
static void
rtcache_trace(const char *func, struct rtentry *rt, struct route *ro)
@ -2023,7 +2002,8 @@ retry:
rt = ro->_ro_rt;
rtcache_invariants(ro);
if (ro->ro_invalid) {
if (ro->ro_rtcache_generation != rtcache_generation) {
/* The cache is invalidated */
rt = NULL;
goto out;
}
@ -2063,57 +2043,6 @@ rtcache_validate(struct route *ro)
return rt;
}
static void
rtcache_invalidate(struct dom_rtlist *rtlist)
{
struct route *ro;
RTCACHE_ASSERT_WLOCK();
while ((ro = LIST_FIRST(rtlist)) != NULL) {
rtcache_invariants(ro);
KASSERT(ro->_ro_rt != NULL);
ro->ro_invalid = true;
LIST_REMOVE(ro, ro_rtcache_next);
LIST_INSERT_HEAD(&invalid_routes, ro, ro_rtcache_next);
rtcache_invariants(ro);
}
}
static void
rtcache_clear_rtentry(int family, struct rtentry *rt)
{
struct domain *dom;
struct route *ro, *nro;
if ((dom = pffinddomain(family)) == NULL)
return;
RTCACHE_WLOCK();
LIST_FOREACH_SAFE(ro, &dom->dom_rtcache, ro_rtcache_next, nro) {
if (ro->_ro_rt == rt)
rtcache_clear(ro);
}
RTCACHE_UNLOCK();
}
static void
rtcache_clear(struct route *ro)
{
RTCACHE_ASSERT_WLOCK();
rtcache_invariants(ro);
if (ro->_ro_rt == NULL)
return;
LIST_REMOVE(ro, ro_rtcache_next);
ro->_ro_rt = NULL;
ro->ro_invalid = false;
rtcache_invariants(ro);
}
struct rtentry *
rtcache_lookup2(struct route *ro, const struct sockaddr *dst,
int clone, int *hitp)
@ -2140,7 +2069,7 @@ rtcache_lookup2(struct route *ro, const struct sockaddr *dst,
if (rt == NULL) {
RTCACHE_UNLOCK();
RTCACHE_WLOCK();
rtcache_clear(ro);
ro->_ro_rt = NULL;
goto miss;
}
@ -2167,7 +2096,8 @@ rtcache_free_locked(struct route *ro)
{
RTCACHE_ASSERT_WLOCK();
rtcache_clear(ro);
ro->_ro_rt = NULL;
if (ro->ro_sa != NULL) {
sockaddr_free(ro->ro_sa);
ro->ro_sa = NULL;
@ -2194,7 +2124,7 @@ rtcache_setdst_locked(struct route *ro, const struct sockaddr *sa)
rtcache_invariants(ro);
if (ro->ro_sa != NULL) {
if (ro->ro_sa->sa_family == sa->sa_family) {
rtcache_clear(ro);
ro->_ro_rt = NULL;
sockaddr_copy(ro->ro_sa, ro->ro_sa->sa_len, sa);
rtcache_invariants(ro);
return 0;

View File

@ -1,4 +1,4 @@
/* $NetBSD: route.h,v 1.113 2017/06/16 02:24:54 ozaki-r Exp $ */
/* $NetBSD: route.h,v 1.114 2017/09/21 07:15:34 ozaki-r Exp $ */
/*
* Copyright (c) 1980, 1986, 1993
@ -64,8 +64,7 @@
struct route {
struct rtentry *_ro_rt;
struct sockaddr *ro_sa;
LIST_ENTRY(route) ro_rtcache_next;
bool ro_invalid;
uint64_t ro_rtcache_generation;
struct psref ro_psref;
int ro_bound;
};
@ -458,8 +457,8 @@ struct rtentry *
static inline void
rtcache_invariants(const struct route *ro)
{
KASSERT(ro->ro_sa != NULL || ro->_ro_rt == NULL);
KASSERT(!ro->ro_invalid || ro->_ro_rt != NULL);
}
static inline struct rtentry *

View File

@ -1,4 +1,4 @@
/* $NetBSD: at_proto.c,v 1.21 2016/01/21 15:41:30 riastradh Exp $ */
/* $NetBSD: at_proto.c,v 1.22 2017/09/21 07:15:34 ozaki-r Exp $ */
/*
* Copyright (c) 1990,1991 Regents of The University of Michigan.
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: at_proto.c,v 1.21 2016/01/21 15:41:30 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: at_proto.c,v 1.22 2017/09/21 07:15:34 ozaki-r Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -78,7 +78,6 @@ struct domain atalkdomain = {
.dom_mowner = MOWNER_INIT("",""),
.dom_sa_cmpofs = offsetof(struct sockaddr_at, sat_addr),
.dom_sa_cmplen = sizeof(struct at_addr),
.dom_rtcache = LIST_HEAD_INITIALIZER(atalkdomain.dom_rtcache)
};
int

View File

@ -1,4 +1,4 @@
/* $NetBSD: in_proto.c,v 1.123 2017/04/14 02:43:27 ozaki-r Exp $ */
/* $NetBSD: in_proto.c,v 1.124 2017/09/21 07:15:34 ozaki-r Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -61,7 +61,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in_proto.c,v 1.123 2017/04/14 02:43:27 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: in_proto.c,v 1.124 2017/09/21 07:15:34 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_mrouting.h"
@ -489,7 +489,6 @@ struct domain inetdomain = {
.dom_sa_any = (const struct sockaddr *)&in_any,
.dom_sockaddr_const_addr = sockaddr_in_const_addr,
.dom_sockaddr_addr = sockaddr_in_addr,
.dom_rtcache = LIST_HEAD_INITIALIZER(inetdomain.dom_rtcache)
};
u_char ip_protox[IPPROTO_MAX];

View File

@ -1,4 +1,4 @@
/* $NetBSD: in6_proto.c,v 1.117 2017/04/14 02:43:28 ozaki-r Exp $ */
/* $NetBSD: in6_proto.c,v 1.118 2017/09/21 07:15:35 ozaki-r Exp $ */
/* $KAME: in6_proto.c,v 1.66 2000/10/10 15:35:47 itojun Exp $ */
/*
@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: in6_proto.c,v 1.117 2017/04/14 02:43:28 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: in6_proto.c,v 1.118 2017/09/21 07:15:35 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_gateway.h"
@ -490,7 +490,6 @@ struct domain inet6domain = {
.dom_sa_cmplen = sizeof(struct in6_addr),
.dom_sa_any = (const struct sockaddr *)&in6_any,
.dom_sockaddr_externalize = sockaddr_in6_externalize,
.dom_rtcache = LIST_HEAD_INITIALIZER(inet6domain.dom_rtcache)
};
#if 0

View File

@ -1,4 +1,4 @@
/* $NetBSD: mpls_proto.c,v 1.30 2016/10/03 11:06:06 ozaki-r Exp $ */
/* $NetBSD: mpls_proto.c,v 1.31 2017/09/21 07:15:35 ozaki-r Exp $ */
/*
* Copyright (c) 2010 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mpls_proto.c,v 1.30 2016/10/03 11:06:06 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: mpls_proto.c,v 1.31 2017/09/21 07:15:35 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@ -391,5 +391,4 @@ struct domain mplsdomain = {
.dom_mowner = MOWNER_INIT("MPLS", ""),
.dom_sa_cmpofs = offsetof(struct sockaddr_mpls, smpls_addr),
.dom_sa_cmplen = sizeof(union mpls_shim),
.dom_rtcache = LIST_HEAD_INITIALIZER(mplsdomain.dom_rtcache)
};

View File

@ -1,4 +1,4 @@
/* $NetBSD: natm_proto.c,v 1.17 2016/10/03 11:06:06 ozaki-r Exp $ */
/* $NetBSD: natm_proto.c,v 1.18 2017/09/21 07:15:35 ozaki-r Exp $ */
/*
* Copyright (c) 1996 Charles D. Cranor and Washington University.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: natm_proto.c,v 1.17 2016/10/03 11:06:06 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: natm_proto.c,v 1.18 2017/09/21 07:15:35 ozaki-r Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -105,7 +105,6 @@ struct domain natmdomain = {
.dom_protosw = natmsw,
.dom_protoswNPROTOSW = &natmsw[sizeof(natmsw)/sizeof(natmsw[0])],
.dom_ifqueues = { &natmintrq, NULL },
.dom_rtcache = LIST_HEAD_INITIALIZER(natmdomain.dom_rtcache)
};
#ifdef NATM_STAT
u_int natm_sodropcnt = 0; /* # mbufs dropped due to full sb */

View File

@ -1,4 +1,4 @@
/* $NetBSD: sockin.c,v 1.64 2016/11/15 09:04:30 ozaki-r Exp $ */
/* $NetBSD: sockin.c,v 1.65 2017/09/21 07:15:35 ozaki-r Exp $ */
/*
* Copyright (c) 2008, 2009 Antti Kantee. All Rights Reserved.
@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sockin.c,v 1.64 2016/11/15 09:04:30 ozaki-r Exp $");
__KERNEL_RCSID(0, "$NetBSD: sockin.c,v 1.65 2017/09/21 07:15:35 ozaki-r Exp $");
#include <sys/param.h>
#include <sys/condvar.h>
@ -162,7 +162,6 @@ struct domain sockindomain = {
.dom_ifqueues = { NULL },
.dom_link = { NULL },
.dom_mowner = MOWNER_INIT("",""),
.dom_rtcache = { NULL },
.dom_sockaddr_cmp = NULL
};
struct domain sockin6domain = {
@ -181,7 +180,6 @@ struct domain sockin6domain = {
.dom_ifqueues = { NULL },
.dom_link = { NULL },
.dom_mowner = MOWNER_INIT("",""),
.dom_rtcache = { NULL },
.dom_sockaddr_cmp = NULL
};

View File

@ -1,4 +1,4 @@
/* $NetBSD: domain.h,v 1.32 2015/04/22 19:46:08 roy Exp $ */
/* $NetBSD: domain.h,v 1.33 2017/09/21 07:15:35 ozaki-r Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
@ -90,7 +90,6 @@ struct domain {
struct mowner dom_mowner;
uint_fast8_t dom_sa_cmpofs;
uint_fast8_t dom_sa_cmplen;
struct dom_rtlist dom_rtcache;
};
STAILQ_HEAD(domainhead,domain);