[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