All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiri Slaby <jslaby@suse.cz>
To: stable@vger.kernel.org
Cc: Dan Streetman <dan.streetman@canonical.com>,
	Dan Streetman <ddstreet@ieee.org>,
	Steffen Klassert <steffen.klassert@secunet.com>,
	Jiri Slaby <jslaby@suse.cz>
Subject: [patch added to 3.12-stable] xfrm: dst_entries_init() per-net dst_ops
Date: Thu, 28 Jan 2016 11:52:11 +0100	[thread overview]
Message-ID: <1453978346-20237-20-git-send-email-jslaby@suse.cz> (raw)
In-Reply-To: <1453978346-20237-1-git-send-email-jslaby@suse.cz>

From: Dan Streetman <dan.streetman@canonical.com>

This patch has been added to the 3.12 stable tree. If you have any
objections, please let us know.

===============

[ Upstream commit a8a572a6b5f2a79280d6e302cb3c1cb1fbaeb3e8 ]

Remove the dst_entries_init/destroy calls for xfrm4 and xfrm6 dst_ops
templates; their dst_entries counters will never be used.  Move the
xfrm dst_ops initialization from the common xfrm/xfrm_policy.c to
xfrm4/xfrm4_policy.c and xfrm6/xfrm6_policy.c, and call dst_entries_init
and dst_entries_destroy for each net namespace.

The ipv4 and ipv6 xfrms each create dst_ops template, and perform
dst_entries_init on the templates.  The template values are copied to each
net namespace's xfrm.xfrm*_dst_ops.  The problem there is the dst_ops
pcpuc_entries field is a percpu counter and cannot be used correctly by
simply copying it to another object.

The result of this is a very subtle bug; changes to the dst entries
counter from one net namespace may sometimes get applied to a different
net namespace dst entries counter.  This is because of how the percpu
counter works; it has a main count field as well as a pointer to the
percpu variables.  Each net namespace maintains its own main count
variable, but all point to one set of percpu variables.  When any net
namespace happens to change one of the percpu variables to outside its
small batch range, its count is moved to the net namespace's main count
variable.  So with multiple net namespaces operating concurrently, the
dst_ops entries counter can stray from the actual value that it should
be; if counts are consistently moved from one net namespace to another
(which my testing showed is likely), then one net namespace winds up
with a negative dst_ops count while another winds up with a continually
increasing count, eventually reaching its gc_thresh limit, which causes
all new traffic on the net namespace to fail with -ENOBUFS.

Signed-off-by: Dan Streetman <dan.streetman@canonical.com>
Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 net/ipv4/xfrm4_policy.c | 46 +++++++++++++++++++++++++++++++++---------
 net/ipv6/xfrm6_policy.c | 53 +++++++++++++++++++++++++++++++++++--------------
 net/xfrm/xfrm_policy.c  | 38 -----------------------------------
 3 files changed, 75 insertions(+), 62 deletions(-)

diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index adf998322bd2..bb201660bd8a 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -230,7 +230,7 @@ static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 	xfrm_dst_ifdown(dst, dev);
 }
 
-static struct dst_ops xfrm4_dst_ops = {
+static struct dst_ops xfrm4_dst_ops_template = {
 	.family =		AF_INET,
 	.protocol =		cpu_to_be16(ETH_P_IP),
 	.gc =			xfrm4_garbage_collect,
@@ -245,7 +245,7 @@ static struct dst_ops xfrm4_dst_ops = {
 
 static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
 	.family = 		AF_INET,
-	.dst_ops =		&xfrm4_dst_ops,
+	.dst_ops =		&xfrm4_dst_ops_template,
 	.dst_lookup =		xfrm4_dst_lookup,
 	.get_saddr =		xfrm4_get_saddr,
 	.decode_session =	_decode_session4,
@@ -267,7 +267,7 @@ static struct ctl_table xfrm4_policy_table[] = {
 	{ }
 };
 
-static int __net_init xfrm4_net_init(struct net *net)
+static int __net_init xfrm4_net_sysctl_init(struct net *net)
 {
 	struct ctl_table *table;
 	struct ctl_table_header *hdr;
@@ -295,7 +295,7 @@ err_alloc:
 	return -ENOMEM;
 }
 
-static void __net_exit xfrm4_net_exit(struct net *net)
+static void __net_exit xfrm4_net_sysctl_exit(struct net *net)
 {
 	struct ctl_table *table;
 
@@ -307,12 +307,44 @@ static void __net_exit xfrm4_net_exit(struct net *net)
 	if (!net_eq(net, &init_net))
 		kfree(table);
 }
+#else /* CONFIG_SYSCTL */
+static int inline xfrm4_net_sysctl_init(struct net *net)
+{
+	return 0;
+}
+
+static void inline xfrm4_net_sysctl_exit(struct net *net)
+{
+}
+#endif
+
+static int __net_init xfrm4_net_init(struct net *net)
+{
+	int ret;
+
+	memcpy(&net->xfrm.xfrm4_dst_ops, &xfrm4_dst_ops_template,
+	       sizeof(xfrm4_dst_ops_template));
+	ret = dst_entries_init(&net->xfrm.xfrm4_dst_ops);
+	if (ret)
+		return ret;
+
+	ret = xfrm4_net_sysctl_init(net);
+	if (ret)
+		dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
+
+	return ret;
+}
+
+static void __net_exit xfrm4_net_exit(struct net *net)
+{
+	xfrm4_net_sysctl_exit(net);
+	dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
+}
 
 static struct pernet_operations __net_initdata xfrm4_net_ops = {
 	.init	= xfrm4_net_init,
 	.exit	= xfrm4_net_exit,
 };
-#endif
 
 static void __init xfrm4_policy_init(void)
 {
@@ -321,12 +353,8 @@ static void __init xfrm4_policy_init(void)
 
 void __init xfrm4_init(void)
 {
-	dst_entries_init(&xfrm4_dst_ops);
-
 	xfrm4_state_init();
 	xfrm4_policy_init();
-#ifdef CONFIG_SYSCTL
 	register_pernet_subsys(&xfrm4_net_ops);
-#endif
 }
 
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 550b195bb2fc..b2ea43167633 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -279,7 +279,7 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 	xfrm_dst_ifdown(dst, dev);
 }
 
-static struct dst_ops xfrm6_dst_ops = {
+static struct dst_ops xfrm6_dst_ops_template = {
 	.family =		AF_INET6,
 	.protocol =		cpu_to_be16(ETH_P_IPV6),
 	.gc =			xfrm6_garbage_collect,
@@ -294,7 +294,7 @@ static struct dst_ops xfrm6_dst_ops = {
 
 static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
 	.family =		AF_INET6,
-	.dst_ops =		&xfrm6_dst_ops,
+	.dst_ops =		&xfrm6_dst_ops_template,
 	.dst_lookup =		xfrm6_dst_lookup,
 	.get_saddr = 		xfrm6_get_saddr,
 	.decode_session =	_decode_session6,
@@ -327,7 +327,7 @@ static struct ctl_table xfrm6_policy_table[] = {
 	{ }
 };
 
-static int __net_init xfrm6_net_init(struct net *net)
+static int __net_init xfrm6_net_sysctl_init(struct net *net)
 {
 	struct ctl_table *table;
 	struct ctl_table_header *hdr;
@@ -355,7 +355,7 @@ err_alloc:
 	return -ENOMEM;
 }
 
-static void __net_exit xfrm6_net_exit(struct net *net)
+static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
 {
 	struct ctl_table *table;
 
@@ -367,31 +367,57 @@ static void __net_exit xfrm6_net_exit(struct net *net)
 	if (!net_eq(net, &init_net))
 		kfree(table);
 }
+#else /* CONFIG_SYSCTL */
+static int inline xfrm6_net_sysctl_init(struct net *net)
+{
+	return 0;
+}
+
+static void inline xfrm6_net_sysctl_exit(struct net *net)
+{
+}
+#endif
+
+static int __net_init xfrm6_net_init(struct net *net)
+{
+	int ret;
+
+	memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
+	       sizeof(xfrm6_dst_ops_template));
+	ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
+	if (ret)
+		return ret;
+
+	ret = xfrm6_net_sysctl_init(net);
+	if (ret)
+		dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
+
+	return ret;
+}
+
+static void __net_exit xfrm6_net_exit(struct net *net)
+{
+	xfrm6_net_sysctl_exit(net);
+	dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
+}
 
 static struct pernet_operations xfrm6_net_ops = {
 	.init	= xfrm6_net_init,
 	.exit	= xfrm6_net_exit,
 };
-#endif
 
 int __init xfrm6_init(void)
 {
 	int ret;
 
-	dst_entries_init(&xfrm6_dst_ops);
-
 	ret = xfrm6_policy_init();
-	if (ret) {
-		dst_entries_destroy(&xfrm6_dst_ops);
+	if (ret)
 		goto out;
-	}
 	ret = xfrm6_state_init();
 	if (ret)
 		goto out_policy;
 
-#ifdef CONFIG_SYSCTL
 	register_pernet_subsys(&xfrm6_net_ops);
-#endif
 out:
 	return ret;
 out_policy:
@@ -401,10 +427,7 @@ out_policy:
 
 void xfrm6_fini(void)
 {
-#ifdef CONFIG_SYSCTL
 	unregister_pernet_subsys(&xfrm6_net_ops);
-#endif
 	xfrm6_policy_fini();
 	xfrm6_state_fini();
-	dst_entries_destroy(&xfrm6_dst_ops);
 }
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 57674ddc683d..5606e994f56e 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -2724,7 +2724,6 @@ static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
 
 int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
 {
-	struct net *net;
 	int err = 0;
 	if (unlikely(afinfo == NULL))
 		return -EINVAL;
@@ -2755,26 +2754,6 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
 	}
 	spin_unlock(&xfrm_policy_afinfo_lock);
 
-	rtnl_lock();
-	for_each_net(net) {
-		struct dst_ops *xfrm_dst_ops;
-
-		switch (afinfo->family) {
-		case AF_INET:
-			xfrm_dst_ops = &net->xfrm.xfrm4_dst_ops;
-			break;
-#if IS_ENABLED(CONFIG_IPV6)
-		case AF_INET6:
-			xfrm_dst_ops = &net->xfrm.xfrm6_dst_ops;
-			break;
-#endif
-		default:
-			BUG();
-		}
-		*xfrm_dst_ops = *afinfo->dst_ops;
-	}
-	rtnl_unlock();
-
 	return err;
 }
 EXPORT_SYMBOL(xfrm_policy_register_afinfo);
@@ -2810,22 +2789,6 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
 }
 EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
 
-static void __net_init xfrm_dst_ops_init(struct net *net)
-{
-	struct xfrm_policy_afinfo *afinfo;
-
-	rcu_read_lock();
-	afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
-	if (afinfo)
-		net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops;
-#if IS_ENABLED(CONFIG_IPV6)
-	afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
-	if (afinfo)
-		net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops;
-#endif
-	rcu_read_unlock();
-}
-
 static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
@@ -2971,7 +2934,6 @@ static int __net_init xfrm_net_init(struct net *net)
 	rv = xfrm_policy_init(net);
 	if (rv < 0)
 		goto out_policy;
-	xfrm_dst_ops_init(net);
 	rv = xfrm_sysctl_init(net);
 	if (rv < 0)
 		goto out_sysctl;
-- 
2.7.0


  parent reply	other threads:[~2016-01-28 10:52 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-28 10:51 [patch added to 3.12-stable] ARM: 8158/1: LLVMLinux: use static inline in ARM ftrace.h Jiri Slaby
2016-01-28 10:51 ` [patch added to 3.12-stable] ARM: 8160/1: drop warning about return_address not using unwind tables Jiri Slaby
2016-01-28 11:07   ` Uwe Kleine-König
2016-01-28 10:51 ` [patch added to 3.12-stable] drm/radeon: cypress_dpm: Fix unused variable warning when CONFIG_ACPI=n Jiri Slaby
2016-01-28 10:51 ` [patch added to 3.12-stable] drm: radeon: ni_dpm: " Jiri Slaby
2016-01-28 10:51 ` [patch added to 3.12-stable] lkdtm: adjust recursion size to avoid warnings Jiri Slaby
2016-01-28 10:51 ` [patch added to 3.12-stable] RDMA/cxgb4: Fix gcc warning on 32-bit arch Jiri Slaby
2016-01-28 10:51 ` [patch added to 3.12-stable] mISDN: avoid arch specific __builtin_return_address call Jiri Slaby
2016-01-28 10:51 ` [patch added to 3.12-stable] veth: don’t modify ip_summed; doing so treats packets with bad checksums as good Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] ipv6/addrlabel: fix ip6addrlbl_get() Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] sctp: sctp should release assoc when sctp_make_abort_user return NULL in sctp_close Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] connector: bump skb->users before callback invocation Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] unix: properly account for FDs passed over unix sockets Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] bridge: Only call /sbin/bridge-stp for the initial network namespace Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] net: sctp: prevent writes to cookie_hmac_alg from accessing invalid memory Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] ipv6: tcp: add rcu locking in tcp_v6_send_synack() Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] tcp_yeah: don't set ssthresh below 2 Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] phonet: properly unshare skbs in phonet_rcv() Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] ipv6: update skb->csum when CE mark is propagated Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] team: Replace rcu_read_lock with a mutex in team_vlan_rx_kill_vid Jiri Slaby
2016-01-28 10:52 ` Jiri Slaby [this message]
2016-01-28 10:52 ` [patch added to 3.12-stable] powerpc/tm: Block signal return setting invalid MSR state Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] powerpc: Make value-returning atomics fully ordered Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] powerpc: Make {cmp}xchg* and their atomic_ versions " Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] scripts/recordmcount.pl: support data in text section on powerpc Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] arm64: KVM: Fix AArch32 to AArch64 register mapping Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] arm64: fix building without CONFIG_UID16 Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] arm64: Clear out any singlestep state on a ptrace detach operation Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] arm64: mm: ensure that the zero page is visible to the page table walker Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] parisc iommu: fix panic due to trying to allocate too large region Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] HID: core: Avoid uninitialized buffer access Jiri Slaby
2016-01-28 10:52 ` [patch added to 3.12-stable] mn10300: Select CONFIG_HAVE_UID16 to fix build failure Jiri Slaby

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1453978346-20237-20-git-send-email-jslaby@suse.cz \
    --to=jslaby@suse.cz \
    --cc=dan.streetman@canonical.com \
    --cc=ddstreet@ieee.org \
    --cc=stable@vger.kernel.org \
    --cc=steffen.klassert@secunet.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.