[ovs-dev] [PATCH 03/12] datapath: compat: Introduce dst-cache for tunnels
Pravin B Shelar
pshelar at ovn.org
Wed May 4 23:35:30 UTC 2016
This backports dst-cache implementation from upstream implementation.
commit 911362c70df5b766c243dc297fadeaced786ffd8
Author: Paolo Abeni <pabeni at redhat.com>
Date: Fri Feb 12 15:43:53 2016 +0100
net: add dst_cache support
Signed-off-by: Pravin B Shelar <pshelar at ovn.org>
---
acinclude.m4 | 1 +
datapath/linux/Modules.mk | 3 +
datapath/linux/compat/dst_cache.c | 169 ++++++++++++++++++++++++++
datapath/linux/compat/include/linux/percpu.h | 11 ++
datapath/linux/compat/include/net/dst_cache.h | 104 ++++++++++++++++
datapath/linux/compat/include/net/ip6_fib.h | 36 ++++++
datapath/linux/compat/utils.c | 25 ++++
7 files changed, 349 insertions(+)
create mode 100644 datapath/linux/compat/dst_cache.c
create mode 100644 datapath/linux/compat/include/net/dst_cache.h
create mode 100644 datapath/linux/compat/include/net/ip6_fib.h
diff --git a/acinclude.m4 b/acinclude.m4
index 45ae15e..74cb029 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -510,6 +510,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
OVS_GREP_IFELSE([$KSRC/include/net/dst.h], [dst_discard_sk])
OVS_GREP_IFELSE([$KSRC/include/net/dst.h], [__skb_dst_copy])
+ OVS_GREP_IFELSE([$KSRC/include/net/dst_cache.h], [dst_cache])
OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [genl_has_listeners])
OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [mcgrp_offset])
diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk
index 5d38766..bde83c0 100644
--- a/datapath/linux/Modules.mk
+++ b/datapath/linux/Modules.mk
@@ -1,5 +1,6 @@
openvswitch_sources += \
linux/compat/dev-openvswitch.c \
+ linux/compat/dst_cache.c \
linux/compat/exthdrs_core.c \
linux/compat/flex_array.c \
linux/compat/flow_dissector.c \
@@ -68,6 +69,7 @@ openvswitch_headers += \
linux/compat/include/linux/workqueue.h \
linux/compat/include/net/checksum.h \
linux/compat/include/net/dst.h \
+ linux/compat/include/net/dst_cache.h \
linux/compat/include/net/dst_metadata.h \
linux/compat/include/net/flow_keys.h \
linux/compat/include/net/genetlink.h \
@@ -78,6 +80,7 @@ openvswitch_headers += \
linux/compat/include/net/inetpeer.h \
linux/compat/include/net/ip.h \
linux/compat/include/net/ip_tunnels.h \
+ linux/compat/include/net/ip6_fib.h \
linux/compat/include/net/ip6_route.h \
linux/compat/include/net/ip6_tunnel.h \
linux/compat/include/net/ipv6.h \
diff --git a/datapath/linux/compat/dst_cache.c b/datapath/linux/compat/dst_cache.c
new file mode 100644
index 0000000..6a87308
--- /dev/null
+++ b/datapath/linux/compat/dst_cache.c
@@ -0,0 +1,169 @@
+/*
+ * net/core/dst_cache.c - dst entry cache
+ *
+ * Copyright (c) 2016 Paolo Abeni <pabeni at redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/percpu.h>
+#include <net/dst_cache.h>
+#include <net/route.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ip6_fib.h>
+#endif
+#include <uapi/linux/in.h>
+
+struct dst_cache_pcpu {
+ unsigned long refresh_ts;
+ struct dst_entry *dst;
+ u32 cookie;
+ union {
+ struct in_addr in_saddr;
+ struct in6_addr in6_saddr;
+ };
+};
+
+static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache,
+ struct dst_entry *dst, u32 cookie)
+{
+ dst_release(dst_cache->dst);
+ if (dst)
+ dst_hold(dst);
+
+ dst_cache->cookie = cookie;
+ dst_cache->dst = dst;
+}
+
+static struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache,
+ struct dst_cache_pcpu *idst)
+{
+ struct dst_entry *dst;
+
+ dst = idst->dst;
+ if (!dst)
+ goto fail;
+
+ /* the cache already hold a dst reference; it can't go away */
+ dst_hold(dst);
+
+ if (unlikely(!time_after(idst->refresh_ts, dst_cache->reset_ts) ||
+ (dst->obsolete && !dst->ops->check(dst, idst->cookie)))) {
+ dst_cache_per_cpu_dst_set(idst, NULL, 0);
+ dst_release(dst);
+ goto fail;
+ }
+ return dst;
+
+fail:
+ idst->refresh_ts = jiffies;
+ return NULL;
+}
+
+struct dst_entry *ovs_dst_cache_get(struct dst_cache *dst_cache)
+{
+ if (!dst_cache->cache)
+ return NULL;
+
+ return dst_cache_per_cpu_get(dst_cache, this_cpu_ptr(dst_cache->cache));
+}
+EXPORT_SYMBOL_GPL(ovs_dst_cache_get);
+
+struct rtable *ovs_dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr)
+{
+ struct dst_cache_pcpu *idst;
+ struct dst_entry *dst;
+
+ if (!dst_cache->cache)
+ return NULL;
+
+ idst = this_cpu_ptr(dst_cache->cache);
+ dst = dst_cache_per_cpu_get(dst_cache, idst);
+ if (!dst)
+ return NULL;
+
+ *saddr = idst->in_saddr.s_addr;
+ return container_of(dst, struct rtable, dst);
+}
+EXPORT_SYMBOL_GPL(ovs_dst_cache_get_ip4);
+
+void ovs_dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst,
+ __be32 saddr)
+{
+ struct dst_cache_pcpu *idst;
+
+ if (!dst_cache->cache)
+ return;
+
+ idst = this_cpu_ptr(dst_cache->cache);
+ dst_cache_per_cpu_dst_set(idst, dst, 0);
+ idst->in_saddr.s_addr = saddr;
+}
+EXPORT_SYMBOL_GPL(ovs_dst_cache_set_ip4);
+
+#if IS_ENABLED(CONFIG_IPV6)
+void ovs_dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst,
+ const struct in6_addr *addr)
+{
+ struct dst_cache_pcpu *idst;
+
+ if (!dst_cache->cache)
+ return;
+
+ idst = this_cpu_ptr(dst_cache->cache);
+ dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst,
+ rt6_get_cookie((struct rt6_info *)dst));
+ idst->in6_saddr = *addr;
+}
+EXPORT_SYMBOL_GPL(ovs_dst_cache_set_ip6);
+
+struct dst_entry *ovs_dst_cache_get_ip6(struct dst_cache *dst_cache,
+ struct in6_addr *saddr)
+{
+ struct dst_cache_pcpu *idst;
+ struct dst_entry *dst;
+
+ if (!dst_cache->cache)
+ return NULL;
+
+ idst = this_cpu_ptr(dst_cache->cache);
+ dst = dst_cache_per_cpu_get(dst_cache, idst);
+ if (!dst)
+ return NULL;
+
+ *saddr = idst->in6_saddr;
+ return dst;
+}
+EXPORT_SYMBOL_GPL(ovs_dst_cache_get_ip6);
+
+#endif
+
+int ovs_dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp)
+{
+ dst_cache->cache = alloc_percpu_gfp(struct dst_cache_pcpu,
+ gfp | __GFP_ZERO);
+ if (!dst_cache->cache)
+ return -ENOMEM;
+
+ dst_cache_reset(dst_cache);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ovs_dst_cache_init);
+
+void ovs_dst_cache_destroy(struct dst_cache *dst_cache)
+{
+ int i;
+
+ if (!dst_cache->cache)
+ return;
+
+ for_each_possible_cpu(i)
+ dst_release(per_cpu_ptr(dst_cache->cache, i)->dst);
+
+ free_percpu(dst_cache->cache);
+}
+EXPORT_SYMBOL_GPL(ovs_dst_cache_destroy);
diff --git a/datapath/linux/compat/include/linux/percpu.h b/datapath/linux/compat/include/linux/percpu.h
index f9fcabb..871c877 100644
--- a/datapath/linux/compat/include/linux/percpu.h
+++ b/datapath/linux/compat/include/linux/percpu.h
@@ -31,4 +31,15 @@
#define get_pcpu_ptr(name) (this_cpu_ptr(&name))
#endif
+#ifndef alloc_percpu_gfp
+#define NEED_ALLOC_PERCPU_GFP
+
+void __percpu *__alloc_percpu_gfp(size_t size, size_t align, gfp_t gfp);
+
+#define alloc_percpu_gfp(type, gfp) \
+ (typeof(type) __percpu *)__alloc_percpu_gfp(sizeof(type), \
+ __alignof__(type), gfp)
+#endif
+
+
#endif
diff --git a/datapath/linux/compat/include/net/dst_cache.h b/datapath/linux/compat/include/net/dst_cache.h
new file mode 100644
index 0000000..5904f2c
--- /dev/null
+++ b/datapath/linux/compat/include/net/dst_cache.h
@@ -0,0 +1,104 @@
+#ifndef _NET_DST_CACHE_WRAPPER_H
+#define _NET_DST_CACHE_WRAPPER_H
+
+#include <linux/jiffies.h>
+#include <net/dst.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ip6_fib.h>
+#endif
+
+struct dst_cache {
+ struct dst_cache_pcpu __percpu *cache;
+ unsigned long reset_ts;
+};
+
+/**
+ * dst_cache_get - perform cache lookup
+ * @dst_cache: the cache
+ *
+ * The caller should use dst_cache_get_ip4() if it need to retrieve the
+ * source address to be used when xmitting to the cached dst.
+ * local BH must be disabled.
+ */
+#define ovs_dst_cache_get dst_cache_get
+struct dst_entry *ovs_dst_cache_get(struct dst_cache *dst_cache);
+
+/**
+ * dst_cache_get_ip4 - perform cache lookup and fetch ipv4 source address
+ * @dst_cache: the cache
+ * @saddr: return value for the retrieved source address
+ *
+ * local BH must be disabled.
+ */
+#define ovs_dst_cache_get_ip4 dst_cache_get_ip4
+struct rtable *ovs_dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr);
+
+/**
+ * dst_cache_set_ip4 - store the ipv4 dst into the cache
+ * @dst_cache: the cache
+ * @dst: the entry to be cached
+ * @saddr: the source address to be stored inside the cache
+ *
+ * local BH must be disabled.
+ */
+#define ovs_dst_cache_set_ip4 dst_cache_set_ip4
+void ovs_dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst,
+ __be32 saddr);
+
+#if IS_ENABLED(CONFIG_IPV6)
+
+/**
+ * dst_cache_set_ip6 - store the ipv6 dst into the cache
+ * @dst_cache: the cache
+ * @dst: the entry to be cached
+ * @saddr: the source address to be stored inside the cache
+ *
+ * local BH must be disabled.
+ */
+#define ovs_dst_cache_set_ip6 dst_cache_set_ip6
+void ovs_dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst,
+ const struct in6_addr *addr);
+
+/**
+ * dst_cache_get_ip6 - perform cache lookup and fetch ipv6 source address
+ * @dst_cache: the cache
+ * @saddr: return value for the retrieved source address
+ *
+ * local BH must be disabled.
+ */
+#define ovs_dst_cache_get_ip6 dst_cache_get_ip6
+struct dst_entry *ovs_dst_cache_get_ip6(struct dst_cache *dst_cache,
+ struct in6_addr *saddr);
+#endif
+
+/**
+ * dst_cache_reset - invalidate the cache contents
+ * @dst_cache: the cache
+ *
+ * This do not free the cached dst to avoid races and contentions.
+ * the dst will be freed on later cache lookup.
+ */
+static inline void dst_cache_reset(struct dst_cache *dst_cache)
+{
+ dst_cache->reset_ts = jiffies;
+}
+
+/**
+ * dst_cache_init - initialize the cache, allocating the required storage
+ * @dst_cache: the cache
+ * @gfp: allocation flags
+ */
+#define ovs_dst_cache_init dst_cache_init
+int ovs_dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp);
+
+/**
+ * dst_cache_destroy - empty the cache and free the allocated storage
+ * @dst_cache: the cache
+ *
+ * No synchronization is enforced: it must be called only when the cache
+ * is unsed.
+ */
+#define ovs_dst_cache_destroy dst_cache_destroy
+void ovs_dst_cache_destroy(struct dst_cache *dst_cache);
+
+#endif
diff --git a/datapath/linux/compat/include/net/ip6_fib.h b/datapath/linux/compat/include/net/ip6_fib.h
new file mode 100644
index 0000000..2c8a513
--- /dev/null
+++ b/datapath/linux/compat/include/net/ip6_fib.h
@@ -0,0 +1,36 @@
+/*
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque at di.fc.ul.pt>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _IP6_FIB_WRAPPER_H
+#define _IP6_FIB_WRAPPER_H
+
+#include_next <net/ip6_fib.h>
+
+#ifndef RTF_PCPU
+#define RTF_PCPU 0x40000000
+#endif
+
+#ifndef RTF_LOCAL
+#define RTF_LOCAL 0x80000000
+#endif
+
+#define rt6_get_cookie rpl_rt6_get_cookie
+static inline u32 rt6_get_cookie(const struct rt6_info *rt)
+{
+ if (rt->rt6i_flags & RTF_PCPU ||
+ (unlikely(rt->dst.flags & DST_NOCACHE) && rt->dst.from))
+ rt = (struct rt6_info *)(rt->dst.from);
+
+ return rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
+}
+
+#endif
diff --git a/datapath/linux/compat/utils.c b/datapath/linux/compat/utils.c
index 0ee6e80..7008ecf 100644
--- a/datapath/linux/compat/utils.c
+++ b/datapath/linux/compat/utils.c
@@ -65,3 +65,28 @@ bool rpl___net_get_random_once(void *buf, int nbytes, bool *done,
EXPORT_SYMBOL_GPL(rpl___net_get_random_once);
#endif
+
+#ifdef NEED_ALLOC_PERCPU_GFP
+void __percpu *__alloc_percpu_gfp(size_t size, size_t align, gfp_t gfp)
+{
+ void __percpu *p;
+ int i;
+
+ if (gfp & ~(GFP_KERNEL | __GFP_ZERO))
+ return NULL;
+ p = __alloc_percpu(size, align);
+ if (!p)
+ return p;
+
+ if (!(gfp & __GFP_ZERO))
+ return p;
+
+ for_each_possible_cpu(i) {
+ void *d;
+
+ d = per_cpu_ptr(p, i);
+ memset(d, 0, size);
+ }
+ return p;
+}
+#endif
--
2.5.5
More information about the dev
mailing list