ip: nexthop: add cache helpers

Add a static nexthop cache in a hash with 1024 buckets and helpers to
manage it (link, unlink, find, add nexthop, del nexthop). Adding new
nexthops is done by creating a new rtnl handle and using it to retrieve
the nexthop so the helper is safe to use while already reading a
response (i.e. using the global rth).

Signed-off-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David Ahern <dsahern@kernel.org>
This commit is contained in:
Nikolay Aleksandrov 2021-09-30 14:38:41 +03:00 committed by David Ahern
parent 53d7c43bd3
commit 60a9703032
2 changed files with 104 additions and 0 deletions

View File

@ -34,6 +34,8 @@ enum {
#define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \
NLMSG_ALIGN(sizeof(struct nhmsg))))
static struct hlist_head nh_cache[NH_CACHE_SIZE];
static void usage(void) __attribute__((noreturn));
static void usage(void)
@ -504,6 +506,102 @@ static int __ipnh_get_id(struct rtnl_handle *rthp, __u32 nh_id,
return rtnl_talk(rthp, &req.n, answer);
}
static struct hlist_head *ipnh_cache_head(__u32 nh_id)
{
nh_id ^= nh_id >> 20;
nh_id ^= nh_id >> 10;
return &nh_cache[nh_id % NH_CACHE_SIZE];
}
static void ipnh_cache_link_entry(struct nh_entry *nhe)
{
struct hlist_head *head = ipnh_cache_head(nhe->nh_id);
hlist_add_head(&nhe->nh_hash, head);
}
static void ipnh_cache_unlink_entry(struct nh_entry *nhe)
{
hlist_del(&nhe->nh_hash);
}
static struct nh_entry *ipnh_cache_get(__u32 nh_id)
{
struct hlist_head *head = ipnh_cache_head(nh_id);
struct nh_entry *nhe;
struct hlist_node *n;
hlist_for_each(n, head) {
nhe = container_of(n, struct nh_entry, nh_hash);
if (nhe->nh_id == nh_id)
return nhe;
}
return NULL;
}
static int __ipnh_cache_parse_nlmsg(const struct nlmsghdr *n,
struct nh_entry *nhe)
{
int err, len;
len = n->nlmsg_len - NLMSG_SPACE(sizeof(struct nhmsg));
if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -EINVAL;
}
err = ipnh_parse_nhmsg(stderr, NLMSG_DATA(n), len, nhe);
if (err) {
fprintf(stderr, "Error parsing nexthop: %s\n", strerror(-err));
return err;
}
return 0;
}
static struct nh_entry *ipnh_cache_add(__u32 nh_id)
{
struct rtnl_handle cache_rth = { .fd = -1 };
struct nlmsghdr *answer = NULL;
struct nh_entry *nhe = NULL;
if (rtnl_open(&cache_rth, 0) < 0)
goto out;
if (__ipnh_get_id(&cache_rth, nh_id, &answer) < 0)
goto out;
nhe = malloc(sizeof(*nhe));
if (!nhe)
goto out;
if (__ipnh_cache_parse_nlmsg(answer, nhe))
goto out_free_nhe;
ipnh_cache_link_entry(nhe);
out:
if (answer)
free(answer);
rtnl_close(&cache_rth);
return nhe;
out_free_nhe:
free(nhe);
nhe = NULL;
goto out;
}
static void ipnh_cache_del(struct nh_entry *nhe)
{
ipnh_cache_unlink_entry(nhe);
ipnh_destroy_entry(nhe);
free(nhe);
}
int print_nexthop(struct nlmsghdr *n, void *arg)
{
struct nhmsg *nhm = NLMSG_DATA(n);

View File

@ -2,6 +2,10 @@
#ifndef __NH_COMMON_H__
#define __NH_COMMON_H__ 1
#include <list.h>
#define NH_CACHE_SIZE 1024
struct nha_res_grp {
__u16 buckets;
__u32 idle_timer;
@ -10,6 +14,8 @@ struct nha_res_grp {
};
struct nh_entry {
struct hlist_node nh_hash;
__u32 nh_id;
__u32 nh_oif;
__u32 nh_flags;