fib_algo: shift / mask by constants in dxr_lookup()

Since trie configuration remains invariant during each DXR instance
lifetime, instead of shifting and masking lookup keys by values
computed at runtime, compile upfront several dxr_lookup()
configurations with hardcoded shift / mask constants, and choose the
apropriate lookup function version after each DXR instance rebuild.

In synthetic tests this yields small but measurable (5-10%) lookup
throughput improvement, depending on FIB size and  prefix patterns.

MFC after:	3 days
This commit is contained in:
Marko Zec 2022-01-17 00:13:47 +01:00
parent d7ce88aafc
commit e7abe200c2

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012-2021 Marko Zec
* Copyright (c) 2012-2022 Marko Zec
* Copyright (c) 2005, 2018 University of Zagreb
* Copyright (c) 2005 International Computer Science Institute
*
@ -78,7 +78,6 @@ CTASSERT(DXR_TRIE_BITS >= 16 && DXR_TRIE_BITS <= 24);
#else
#define DXR_D (DXR_TRIE_BITS - 1)
#endif
#define DXR_X (DXR_TRIE_BITS - DXR_D)
#define D_TBL_SIZE (1 << DXR_D)
#define DIRECT_TBL_SIZE (1 << DXR_TRIE_BITS)
@ -211,9 +210,6 @@ struct dxr_aux {
struct dxr {
/* Lookup tables */
uint16_t d_shift;
uint16_t x_shift;
uint32_t x_mask;
void *d;
void *x;
void *r;
@ -224,6 +220,9 @@ struct dxr {
struct fib_data *fd;
struct epoch_context epoch_ctx;
uint32_t fibnum;
uint16_t d_shift;
uint16_t x_shift;
uint32_t x_mask;
};
static MALLOC_DEFINE(M_DXRLPM, "dxr", "DXR LPM");
@ -235,46 +234,6 @@ uma_zone_t trie_zone;
VNET_DEFINE_STATIC(int, frag_limit) = 100;
#define V_frag_limit VNET(frag_limit)
SYSCTL_DECL(_net_route_algo);
SYSCTL_NODE(_net_route_algo, OID_AUTO, dxr, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"DXR tunables");
static int
sysctl_dxr_frag_limit(SYSCTL_HANDLER_ARGS)
{
char buf[8];
int error, new, i;
snprintf(buf, sizeof(buf), "%d.%02d%%", V_frag_limit / 100,
V_frag_limit % 100);
error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
if (error != 0 || req->newptr == NULL)
return (error);
if (!isdigit(*buf) && *buf != '.')
return (EINVAL);
for (i = 0, new = 0; isdigit(buf[i]) && i < sizeof(buf); i++)
new = new * 10 + buf[i] - '0';
new *= 100;
if (buf[i++] == '.') {
if (!isdigit(buf[i]))
return (EINVAL);
new += (buf[i++] - '0') * 10;
if (isdigit(buf[i]))
new += buf[i++] - '0';
}
if (new > 1000)
return (EINVAL);
V_frag_limit = new;
snprintf(buf, sizeof(buf), "%d.%02d%%", V_frag_limit / 100,
V_frag_limit % 100);
return (0);
}
SYSCTL_PROC(_net_route_algo_dxr, OID_AUTO, frag_limit,
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_VNET,
0, 0, sysctl_dxr_frag_limit, "A",
"Fragmentation threshold to full rebuild");
/* Binary search for a matching address range */
#define DXR_LOOKUP_STAGE \
if (masked_dst < range[middle].start) { \
@ -290,35 +249,14 @@ SYSCTL_PROC(_net_route_algo_dxr, OID_AUTO, frag_limit,
return (range[lowerbound].nexthop);
static int
dxr_lookup(struct dxr *dxr, uint32_t dst)
range_lookup(struct range_entry_long *rt, struct direct_entry de, uint32_t dst)
{
#ifdef DXR2
uint16_t *dt = dxr->d;
struct direct_entry *xt = dxr->x;
int xi;
#else
struct direct_entry *dt = dxr->d;
#endif
struct direct_entry de;
struct range_entry_long *rt;
uint32_t base;
uint32_t upperbound;
uint32_t middle;
uint32_t lowerbound;
uint32_t masked_dst;
#ifdef DXR2
xi = (dt[dst >> dxr->d_shift] << dxr->x_shift) +
((dst >> DXR_RANGE_SHIFT) & dxr->x_mask);
de = xt[xi];
#else
de = dt[dst >> DXR_RANGE_SHIFT];
#endif
if (__predict_true(de.fragments == FRAGS_MARK_HIT))
return (de.base);
rt = dxr->r;
base = de.base;
lowerbound = 0;
masked_dst = dst & DXR_RANGE_MASK;
@ -355,6 +293,65 @@ dxr_lookup(struct dxr *dxr, uint32_t dst)
}
}
#define DXR_LOOKUP_DEFINE(D) \
static int inline \
dxr_lookup_##D(struct dxr *dxr, uint32_t dst) \
{ \
struct direct_entry de; \
uint16_t *dt = dxr->d; \
struct direct_entry *xt = dxr->x; \
\
de = xt[(dt[dst >> (32 - (D))] << (DXR_TRIE_BITS - (D))) \
+ ((dst >> DXR_RANGE_SHIFT) & \
(0xffffffffU >> (32 - DXR_TRIE_BITS + (D))))]; \
if (__predict_true(de.fragments == FRAGS_MARK_HIT)) \
return (de.base); \
return (range_lookup(dxr->r, de, dst)); \
} \
\
static struct nhop_object * \
dxr_fib_lookup_##D(void *algo_data, \
const struct flm_lookup_key key, uint32_t scopeid __unused) \
{ \
struct dxr *dxr = algo_data; \
\
return (dxr->nh_tbl[dxr_lookup_##D(dxr, \
ntohl(key.addr4.s_addr))]); \
}
#ifdef DXR2
#if DXR_TRIE_BITS > 16
DXR_LOOKUP_DEFINE(16)
#endif
DXR_LOOKUP_DEFINE(15)
DXR_LOOKUP_DEFINE(14)
DXR_LOOKUP_DEFINE(13)
DXR_LOOKUP_DEFINE(12)
DXR_LOOKUP_DEFINE(11)
DXR_LOOKUP_DEFINE(10)
DXR_LOOKUP_DEFINE(9)
#endif /* DXR2 */
static int inline
dxr_lookup(struct dxr *dxr, uint32_t dst)
{
struct direct_entry de;
#ifdef DXR2
uint16_t *dt = dxr->d;
struct direct_entry *xt = dxr->x;
de = xt[(dt[dst >> dxr->d_shift] << dxr->x_shift) +
((dst >> DXR_RANGE_SHIFT) & dxr->x_mask)];
#else /* !DXR2 */
struct direct_entry *dt = dxr->d;
de = dt[dst >> DXR_RANGE_SHIFT];
#endif /* !DXR2 */
if (__predict_true(de.fragments == FRAGS_MARK_HIT))
return (de.base);
return (range_lookup(dxr->r, de, dst));
}
static void
initheap(struct dxr_aux *da, uint32_t dst_u32, uint32_t chunk)
{
@ -1111,11 +1108,8 @@ dxr_fib_lookup(void *algo_data, const struct flm_lookup_key key,
uint32_t scopeid)
{
struct dxr *dxr = algo_data;
uint32_t nh;
nh = dxr_lookup(dxr, ntohl(key.addr4.s_addr));
return (dxr->nh_tbl[nh]);
return (dxr->nh_tbl[dxr_lookup(dxr, ntohl(key.addr4.s_addr))]);
}
static enum flm_op_result
@ -1183,6 +1177,35 @@ epoch_dxr_destroy(epoch_context_t ctx)
dxr_destroy(dxr);
}
static void *
choose_lookup_fn(struct dxr_aux *da)
{
#ifdef DXR2
switch (da->d_bits) {
#if DXR_TRIE_BITS > 16
case 16:
return (dxr_fib_lookup_16);
#endif
case 15:
return (dxr_fib_lookup_15);
case 14:
return (dxr_fib_lookup_14);
case 13:
return (dxr_fib_lookup_13);
case 12:
return (dxr_fib_lookup_12);
case 11:
return (dxr_fib_lookup_11);
case 10:
return (dxr_fib_lookup_10);
case 9:
return (dxr_fib_lookup_9);
}
#endif /* DXR2 */
return (dxr_fib_lookup);
}
static enum flm_op_result
dxr_dump_end(void *data, struct fib_dp *dp)
{
@ -1203,7 +1226,7 @@ dxr_dump_end(void *data, struct fib_dp *dp)
if (dxr->d == NULL)
return (FLM_REBUILD);
dp->f = dxr_fib_lookup;
dp->f = choose_lookup_fn(da);
dp->arg = dxr;
return (FLM_SUCCESS);
@ -1300,7 +1323,7 @@ dxr_change_rib_batch(struct rib_head *rnh, struct fib_change_queue *q,
return (FLM_REBUILD);
}
new_dp.f = dxr_fib_lookup;
new_dp.f = choose_lookup_fn(da);
new_dp.arg = new_dxr;
if (fib_set_datapath_ptr(dxr->fd, &new_dp)) {
fib_set_algo_ptr(dxr->fd, new_dxr);
@ -1320,6 +1343,46 @@ dxr_get_pref(const struct rib_rtable_info *rinfo)
return (251);
}
SYSCTL_DECL(_net_route_algo);
SYSCTL_NODE(_net_route_algo, OID_AUTO, dxr, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"DXR tunables");
static int
sysctl_dxr_frag_limit(SYSCTL_HANDLER_ARGS)
{
char buf[8];
int error, new, i;
snprintf(buf, sizeof(buf), "%d.%02d%%", V_frag_limit / 100,
V_frag_limit % 100);
error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
if (error != 0 || req->newptr == NULL)
return (error);
if (!isdigit(*buf) && *buf != '.')
return (EINVAL);
for (i = 0, new = 0; isdigit(buf[i]) && i < sizeof(buf); i++)
new = new * 10 + buf[i] - '0';
new *= 100;
if (buf[i++] == '.') {
if (!isdigit(buf[i]))
return (EINVAL);
new += (buf[i++] - '0') * 10;
if (isdigit(buf[i]))
new += buf[i++] - '0';
}
if (new > 1000)
return (EINVAL);
V_frag_limit = new;
snprintf(buf, sizeof(buf), "%d.%02d%%", V_frag_limit / 100,
V_frag_limit % 100);
return (0);
}
SYSCTL_PROC(_net_route_algo_dxr, OID_AUTO, frag_limit,
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_VNET,
0, 0, sysctl_dxr_frag_limit, "A",
"Fragmentation threshold to full rebuild");
static struct fib_lookup_module fib_dxr_mod = {
.flm_name = "dxr",
.flm_family = AF_INET,