Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1256903 > unrolled thread

[PATCH] xfrm: dst_entries_init() per-net dst_ops

Started bydan.streetman@canonical.com
First post2015-10-27 17:20 +0100
Last post2015-10-29 15:00 +0100
Articles 9 — 5 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH] xfrm: dst_entries_init() per-net dst_ops dan.streetman@canonical.com - 2015-10-27 17:20 +0100
    Re: [PATCH] xfrm: dst_entries_init() per-net dst_ops Dan Streetman <dan.streetman@canonical.com> - 2015-10-28 14:40 +0100
      Re: [PATCH] xfrm: dst_entries_init() per-net dst_ops Hannes Frederic Sowa <hannes@stressinduktion.org> - 2015-10-28 14:50 +0100
        Re: [PATCH] xfrm: dst_entries_init() per-net dst_ops Dan Streetman <dan.streetman@canonical.com> - 2015-10-28 16:10 +0100
      Re: [PATCH] xfrm: dst_entries_init() per-net dst_ops David Miller <davem@davemloft.net> - 2015-10-28 15:00 +0100
    [PATCHv2] xfrm: dst_entries_init() per-net dst_ops Dan Streetman <dan.streetman@canonical.com> - 2015-10-29 12:40 +0100
      Re: [PATCHv2] xfrm: dst_entries_init() per-net dst_ops kbuild test robot <lkp@intel.com> - 2015-10-29 13:00 +0100
        Re: [PATCHv2] xfrm: dst_entries_init() per-net dst_ops Dan Streetman <dan.streetman@canonical.com> - 2015-10-29 14:50 +0100
      [PATCHv3] xfrm: dst_entries_init() per-net dst_ops Dan Streetman <dan.streetman@canonical.com> - 2015-10-29 15:00 +0100

#1256903 — [PATCH] xfrm: dst_entries_init() per-net dst_ops

Fromdan.streetman@canonical.com
Date2015-10-27 17:20 +0100
Subject[PATCH] xfrm: dst_entries_init() per-net dst_ops
Message-ID<qof8C-7mI-31@gated-at.bofh.it>
From: Dan Streetman <dan.streetman@canonical.com>

The ipv4 and ipv6 xfrms each create a template dst_ops object, and
perform dst_entries_init() on the template objects.  Then each net
namespace has its net.xfrm.xfrm[46]_dst_ops field set to the template
values.  The problem with that 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 (which is reported as 0) 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.

This removes the dst_entries_init (and dst_entries_destroy) call for
the template dst_ops objects; their counters will never be used.
Instead dst_entries_init is called for each net namespace's dst_ops
object, right after copying its values from the template, and
dst_entries_destroy is called when the net namespace is removed.

Signed-off-by: Dan Streetman <dan.streetman@canonical.com>
Signed-off-by: Dan Streetman <ddstreet@ieee.org>
---
 net/ipv4/xfrm4_policy.c |  5 +++--
 net/ipv6/xfrm6_policy.c | 10 ++++------
 net/xfrm/xfrm_policy.c  | 25 +++++++++++++++++++++++--
 3 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index f2606b9..5f747ee 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -235,6 +235,9 @@ static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 	xfrm_dst_ifdown(dst, dev);
 }
 
+/* This is used as a template only; the dst_entries counter is not
+ * initialized for this, but must be on per-net copies of this
+ */
 static struct dst_ops xfrm4_dst_ops = {
 	.family =		AF_INET,
 	.gc =			xfrm4_garbage_collect,
@@ -325,8 +328,6 @@ static void __init xfrm4_policy_init(void)
 
 void __init xfrm4_init(void)
 {
-	dst_entries_init(&xfrm4_dst_ops);
-
 	xfrm4_state_init();
 	xfrm4_policy_init();
 	xfrm4_protocol_init();
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 2cc5840..b895ec1 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -279,6 +279,9 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 	xfrm_dst_ifdown(dst, dev);
 }
 
+/* This is used as a template only; the dst_entries counter is not
+ * initialized for this, but must be on per-net copies of this
+ */
 static struct dst_ops xfrm6_dst_ops = {
 	.family =		AF_INET6,
 	.gc =			xfrm6_garbage_collect,
@@ -376,13 +379,9 @@ 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;
@@ -411,5 +410,4 @@ void xfrm6_fini(void)
 	xfrm6_protocol_fini();
 	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 09bfcba..5381719 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -2896,12 +2896,32 @@ static void __net_init xfrm_dst_ops_init(struct net *net)
 
 	rcu_read_lock();
 	afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
-	if (afinfo)
+	if (afinfo) {
 		net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops;
+		dst_entries_init(&net->xfrm.xfrm4_dst_ops);
+	}
 #if IS_ENABLED(CONFIG_IPV6)
 	afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
-	if (afinfo)
+	if (afinfo) {
 		net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops;
+		dst_entries_init(&net->xfrm.xfrm6_dst_ops);
+	}
+#endif
+	rcu_read_unlock();
+}
+
+static void xfrm_dst_ops_fini(struct net *net)
+{
+	struct xfrm_policy_afinfo *afinfo;
+
+	rcu_read_lock();
+	afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
+	if (afinfo)
+		dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
+#if IS_ENABLED(CONFIG_IPV6)
+	afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
+	if (afinfo)
+		dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
 #endif
 	rcu_read_unlock();
 }
@@ -3085,6 +3105,7 @@ static void __net_exit xfrm_net_exit(struct net *net)
 {
 	flow_cache_fini(net);
 	xfrm_sysctl_fini(net);
+	xfrm_dst_ops_fini(net);
 	xfrm_policy_fini(net);
 	xfrm_state_fini(net);
 	xfrm_statistics_fini(net);
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [next] | [standalone]


#1258032

FromDan Streetman <dan.streetman@canonical.com>
Date2015-10-28 14:40 +0100
Message-ID<qoz7m-3cS-55@gated-at.bofh.it>
In reply to#1256903
On Tue, Oct 27, 2015 at 12:15 PM,  <dan.streetman@canonical.com> wrote:
> From: Dan Streetman <dan.streetman@canonical.com>
>
> The ipv4 and ipv6 xfrms each create a template dst_ops object, and
> perform dst_entries_init() on the template objects.  Then each net
> namespace has its net.xfrm.xfrm[46]_dst_ops field set to the template
> values.  The problem with that 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 (which is reported as 0) 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.
>
> This removes the dst_entries_init (and dst_entries_destroy) call for
> the template dst_ops objects; their counters will never be used.
> Instead dst_entries_init is called for each net namespace's dst_ops
> object, right after copying its values from the template, and

Well I'm not sure why my test kernel booted, while the test robot
found the bug of GFP_KERNEL percpu counter alloc during atomic
context.  Thanks test robot!

I'll update the patch and resend.


> dst_entries_destroy is called when the net namespace is removed.
>
> Signed-off-by: Dan Streetman <dan.streetman@canonical.com>
> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
> ---
>  net/ipv4/xfrm4_policy.c |  5 +++--
>  net/ipv6/xfrm6_policy.c | 10 ++++------
>  net/xfrm/xfrm_policy.c  | 25 +++++++++++++++++++++++--
>  3 files changed, 30 insertions(+), 10 deletions(-)
>
> diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
> index f2606b9..5f747ee 100644
> --- a/net/ipv4/xfrm4_policy.c
> +++ b/net/ipv4/xfrm4_policy.c
> @@ -235,6 +235,9 @@ static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
>         xfrm_dst_ifdown(dst, dev);
>  }
>
> +/* This is used as a template only; the dst_entries counter is not
> + * initialized for this, but must be on per-net copies of this
> + */
>  static struct dst_ops xfrm4_dst_ops = {
>         .family =               AF_INET,
>         .gc =                   xfrm4_garbage_collect,
> @@ -325,8 +328,6 @@ static void __init xfrm4_policy_init(void)
>
>  void __init xfrm4_init(void)
>  {
> -       dst_entries_init(&xfrm4_dst_ops);
> -
>         xfrm4_state_init();
>         xfrm4_policy_init();
>         xfrm4_protocol_init();
> diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
> index 2cc5840..b895ec1 100644
> --- a/net/ipv6/xfrm6_policy.c
> +++ b/net/ipv6/xfrm6_policy.c
> @@ -279,6 +279,9 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
>         xfrm_dst_ifdown(dst, dev);
>  }
>
> +/* This is used as a template only; the dst_entries counter is not
> + * initialized for this, but must be on per-net copies of this
> + */
>  static struct dst_ops xfrm6_dst_ops = {
>         .family =               AF_INET6,
>         .gc =                   xfrm6_garbage_collect,
> @@ -376,13 +379,9 @@ 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;
> @@ -411,5 +410,4 @@ void xfrm6_fini(void)
>         xfrm6_protocol_fini();
>         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 09bfcba..5381719 100644
> --- a/net/xfrm/xfrm_policy.c
> +++ b/net/xfrm/xfrm_policy.c
> @@ -2896,12 +2896,32 @@ static void __net_init xfrm_dst_ops_init(struct net *net)
>
>         rcu_read_lock();
>         afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
> -       if (afinfo)
> +       if (afinfo) {
>                 net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops;
> +               dst_entries_init(&net->xfrm.xfrm4_dst_ops);
> +       }
>  #if IS_ENABLED(CONFIG_IPV6)
>         afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
> -       if (afinfo)
> +       if (afinfo) {
>                 net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops;
> +               dst_entries_init(&net->xfrm.xfrm6_dst_ops);
> +       }
> +#endif
> +       rcu_read_unlock();
> +}
> +
> +static void xfrm_dst_ops_fini(struct net *net)
> +{
> +       struct xfrm_policy_afinfo *afinfo;
> +
> +       rcu_read_lock();
> +       afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
> +       if (afinfo)
> +               dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
> +#if IS_ENABLED(CONFIG_IPV6)
> +       afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
> +       if (afinfo)
> +               dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
>  #endif
>         rcu_read_unlock();
>  }
> @@ -3085,6 +3105,7 @@ static void __net_exit xfrm_net_exit(struct net *net)
>  {
>         flow_cache_fini(net);
>         xfrm_sysctl_fini(net);
> +       xfrm_dst_ops_fini(net);
>         xfrm_policy_fini(net);
>         xfrm_state_fini(net);
>         xfrm_statistics_fini(net);
> --
> 2.5.0
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1258035

FromHannes Frederic Sowa <hannes@stressinduktion.org>
Date2015-10-28 14:50 +0100
Message-ID<qozgZ-3gf-13@gated-at.bofh.it>
In reply to#1258032
Hello,

On Wed, Oct 28, 2015, at 14:32, Dan Streetman wrote:
> On Tue, Oct 27, 2015 at 12:15 PM,  <dan.streetman@canonical.com> wrote:
> > From: Dan Streetman <dan.streetman@canonical.com>
> >
> > The ipv4 and ipv6 xfrms each create a template dst_ops object, and
> > perform dst_entries_init() on the template objects.  Then each net
> > namespace has its net.xfrm.xfrm[46]_dst_ops field set to the template
> > values.  The problem with that is the dst_ops.pcpuc_entries field is
> > a percpu counter and cannot be used correctly by simply copying it to
> > another object.

How hard would it be to split of the counters from the dst_ops struct?
We could make dst_ops instances const and have normal pointers to them
and keep the dst_entries as a small array in net namespace?

Bye,
Hannes
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1258228

FromDan Streetman <dan.streetman@canonical.com>
Date2015-10-28 16:10 +0100
Message-ID<qoAwp-4gt-15@gated-at.bofh.it>
In reply to#1258035
On Wed, Oct 28, 2015 at 9:42 AM, Hannes Frederic Sowa
<hannes@stressinduktion.org> wrote:
> Hello,
>
> On Wed, Oct 28, 2015, at 14:32, Dan Streetman wrote:
>> On Tue, Oct 27, 2015 at 12:15 PM,  <dan.streetman@canonical.com> wrote:
>> > From: Dan Streetman <dan.streetman@canonical.com>
>> >
>> > The ipv4 and ipv6 xfrms each create a template dst_ops object, and
>> > perform dst_entries_init() on the template objects.  Then each net
>> > namespace has its net.xfrm.xfrm[46]_dst_ops field set to the template
>> > values.  The problem with that is the dst_ops.pcpuc_entries field is
>> > a percpu counter and cannot be used correctly by simply copying it to
>> > another object.
>
> How hard would it be to split of the counters from the dst_ops struct?
> We could make dst_ops instances const and have normal pointers to them
> and keep the dst_entries as a small array in net namespace?

Well, the dst_ops->pcpuc_entries counter is used in dst.c which just
gets a struct dst_ops *, so it doesn't have access to the owning net
namespace.  And, not all dst_ops users have a per-net-namespace
dst_ops; ipv4/route.c for example uses a global "ipv4_dst_ops" object.
So it probably does need to stay owned by dst_ops.


>
> Bye,
> Hannes
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1258066

FromDavid Miller <davem@davemloft.net>
Date2015-10-28 15:00 +0100
Message-ID<qozqI-3k6-67@gated-at.bofh.it>
In reply to#1258032
From: Dan Streetman <dan.streetman@canonical.com>
Date: Wed, 28 Oct 2015 09:32:47 -0400

> Well I'm not sure why my test kernel booted, while the test robot
> found the bug of GFP_KERNEL percpu counter alloc during atomic
> context.  Thanks test robot!

It's because of the kernel config options you (don't) have
enabled.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1258740 — [PATCHv2] xfrm: dst_entries_init() per-net dst_ops

FromDan Streetman <dan.streetman@canonical.com>
Date2015-10-29 12:40 +0100
Subject[PATCHv2] xfrm: dst_entries_init() per-net dst_ops
Message-ID<qoTIJ-82i-15@gated-at.bofh.it>
In reply to#1256903
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>
---
Changes since v1:
 move dst copying and entries_init from xfrm/xfrm_policy into
   ipv[46]/xfrm[46]_policy
 add dst_entries_init and destroy to xfrm[46]_policy pernet init/exit,
   so no for_each_net() is required

 net/ipv4/xfrm4_policy.c | 46 +++++++++++++++++++++++++++++++++---------
 net/ipv6/xfrm6_policy.c | 53 +++++++++++++++++++++++++++++++++++--------------
 net/xfrm/xfrm_policy.c  | 37 ----------------------------------
 3 files changed, 75 insertions(+), 61 deletions(-)

diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index f2606b9..59ce2b9 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -235,7 +235,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,
 	.gc =			xfrm4_garbage_collect,
 	.update_pmtu =		xfrm4_update_pmtu,
@@ -249,7 +249,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,
@@ -271,7 +271,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;
@@ -299,7 +299,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;
 
@@ -311,12 +311,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)
 {
@@ -325,13 +357,9 @@ static void __init xfrm4_policy_init(void)
 
 void __init xfrm4_init(void)
 {
-	dst_entries_init(&xfrm4_dst_ops);
-
 	xfrm4_state_init();
 	xfrm4_policy_init();
 	xfrm4_protocol_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 2cc5840..7b0bbb8 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,
 	.gc =			xfrm6_garbage_collect,
 	.update_pmtu =		xfrm6_update_pmtu,
@@ -293,7 +293,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,
@@ -325,7 +325,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;
@@ -353,7 +353,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;
 
@@ -365,24 +365,52 @@ 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;
@@ -391,9 +419,7 @@ int __init xfrm6_init(void)
 	if (ret)
 		goto out_state;
 
-#ifdef CONFIG_SYSCTL
 	register_pernet_subsys(&xfrm6_net_ops);
-#endif
 out:
 	return ret;
 out_state:
@@ -405,11 +431,8 @@ out_policy:
 
 void xfrm6_fini(void)
 {
-#ifdef CONFIG_SYSCTL
 	unregister_pernet_subsys(&xfrm6_net_ops);
-#endif
 	xfrm6_protocol_fini();
 	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 09bfcba..836229c 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -2835,26 +2835,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);
@@ -2890,22 +2870,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);
@@ -3054,7 +3018,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.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1258744 — Re: [PATCHv2] xfrm: dst_entries_init() per-net dst_ops

Fromkbuild test robot <lkp@intel.com>
Date2015-10-29 13:00 +0100
SubjectRe: [PATCHv2] xfrm: dst_entries_init() per-net dst_ops
Message-ID<qoU25-89o-3@gated-at.bofh.it>
In reply to#1258740

[Multipart message — attachments visible in raw view] — view raw

Hi Dan,

[auto build test WARNING on ipsec/master -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Dan-Streetman/xfrm-dst_entries_init-per-net-dst_ops/20151029-193245
config: x86_64-randconfig-x019-201543 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   net/xfrm/xfrm_policy.c: In function 'xfrm_policy_register_afinfo':
>> net/xfrm/xfrm_policy.c:2810:14: warning: unused variable 'net' [-Wunused-variable]
     struct net *net;
                 ^

vim +/net +2810 net/xfrm/xfrm_policy.c

ebb762f27 Steffen Klassert 2011-11-23  2794  static unsigned int xfrm_mtu(const struct dst_entry *dst)
d33e45533 David S. Miller  2010-12-14  2795  {
618f9bc74 Steffen Klassert 2011-11-23  2796  	unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
618f9bc74 Steffen Klassert 2011-11-23  2797  
618f9bc74 Steffen Klassert 2011-11-23  2798  	return mtu ? : dst_mtu(dst->path);
d33e45533 David S. Miller  2010-12-14  2799  }
d33e45533 David S. Miller  2010-12-14  2800  
f894cbf84 David S. Miller  2012-07-02  2801  static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
f894cbf84 David S. Miller  2012-07-02  2802  					   struct sk_buff *skb,
f894cbf84 David S. Miller  2012-07-02  2803  					   const void *daddr)
d3aaeb38c David S. Miller  2011-07-18  2804  {
f894cbf84 David S. Miller  2012-07-02  2805  	return dst->path->ops->neigh_lookup(dst, skb, daddr);
d3aaeb38c David S. Miller  2011-07-18  2806  }
d3aaeb38c David S. Miller  2011-07-18  2807  
^1da177e4 Linus Torvalds   2005-04-16  2808  int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
^1da177e4 Linus Torvalds   2005-04-16  2809  {
d7c7544c3 Alexey Dobriyan  2010-01-24 @2810  	struct net *net;
^1da177e4 Linus Torvalds   2005-04-16  2811  	int err = 0;
^1da177e4 Linus Torvalds   2005-04-16  2812  	if (unlikely(afinfo == NULL))
^1da177e4 Linus Torvalds   2005-04-16  2813  		return -EINVAL;
^1da177e4 Linus Torvalds   2005-04-16  2814  	if (unlikely(afinfo->family >= NPROTO))
^1da177e4 Linus Torvalds   2005-04-16  2815  		return -EAFNOSUPPORT;
ef8531b64 Eric Dumazet     2012-08-19  2816  	spin_lock(&xfrm_policy_afinfo_lock);
^1da177e4 Linus Torvalds   2005-04-16  2817  	if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
f31e8d4f7 Li RongQing      2015-04-23  2818  		err = -EEXIST;

:::::: The code at line 2810 was first introduced by commit
:::::: d7c7544c3d5f59033d1bf3236bc7b289f5f26b75 netns xfrm: deal with dst entries in netns

:::::: TO: Alexey Dobriyan <adobriyan@gmail.com>
:::::: CC: David S. Miller <davem@davemloft.net>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[toc] | [prev] | [next] | [standalone]


#1258794 — Re: [PATCHv2] xfrm: dst_entries_init() per-net dst_ops

FromDan Streetman <dan.streetman@canonical.com>
Date2015-10-29 14:50 +0100
SubjectRe: [PATCHv2] xfrm: dst_entries_init() per-net dst_ops
Message-ID<qoVKx-Od-1@gated-at.bofh.it>
In reply to#1258744
On Thu, Oct 29, 2015 at 7:57 AM, kbuild test robot <lkp@intel.com> wrote:
> Hi Dan,
>
> [auto build test WARNING on ipsec/master -- if it's inappropriate base, please suggest rules for selecting the more suitable base]
>
> url:    https://github.com/0day-ci/linux/commits/Dan-Streetman/xfrm-dst_entries_init-per-net-dst_ops/20151029-193245
> config: x86_64-randconfig-x019-201543 (attached as .config)
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=x86_64
>
> All warnings (new ones prefixed by >>):
>
>    net/xfrm/xfrm_policy.c: In function 'xfrm_policy_register_afinfo':
>>> net/xfrm/xfrm_policy.c:2810:14: warning: unused variable 'net' [-Wunused-variable]
>      struct net *net;
>                  ^

well, i should have caught that one.  resending patch.

thnx!

>
> vim +/net +2810 net/xfrm/xfrm_policy.c
>
> ebb762f27 Steffen Klassert 2011-11-23  2794  static unsigned int xfrm_mtu(const struct dst_entry *dst)
> d33e45533 David S. Miller  2010-12-14  2795  {
> 618f9bc74 Steffen Klassert 2011-11-23  2796     unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
> 618f9bc74 Steffen Klassert 2011-11-23  2797
> 618f9bc74 Steffen Klassert 2011-11-23  2798     return mtu ? : dst_mtu(dst->path);
> d33e45533 David S. Miller  2010-12-14  2799  }
> d33e45533 David S. Miller  2010-12-14  2800
> f894cbf84 David S. Miller  2012-07-02  2801  static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
> f894cbf84 David S. Miller  2012-07-02  2802                                        struct sk_buff *skb,
> f894cbf84 David S. Miller  2012-07-02  2803                                        const void *daddr)
> d3aaeb38c David S. Miller  2011-07-18  2804  {
> f894cbf84 David S. Miller  2012-07-02  2805     return dst->path->ops->neigh_lookup(dst, skb, daddr);
> d3aaeb38c David S. Miller  2011-07-18  2806  }
> d3aaeb38c David S. Miller  2011-07-18  2807
> ^1da177e4 Linus Torvalds   2005-04-16  2808  int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
> ^1da177e4 Linus Torvalds   2005-04-16  2809  {
> d7c7544c3 Alexey Dobriyan  2010-01-24 @2810     struct net *net;
> ^1da177e4 Linus Torvalds   2005-04-16  2811     int err = 0;
> ^1da177e4 Linus Torvalds   2005-04-16  2812     if (unlikely(afinfo == NULL))
> ^1da177e4 Linus Torvalds   2005-04-16  2813             return -EINVAL;
> ^1da177e4 Linus Torvalds   2005-04-16  2814     if (unlikely(afinfo->family >= NPROTO))
> ^1da177e4 Linus Torvalds   2005-04-16  2815             return -EAFNOSUPPORT;
> ef8531b64 Eric Dumazet     2012-08-19  2816     spin_lock(&xfrm_policy_afinfo_lock);
> ^1da177e4 Linus Torvalds   2005-04-16  2817     if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
> f31e8d4f7 Li RongQing      2015-04-23  2818             err = -EEXIST;
>
> :::::: The code at line 2810 was first introduced by commit
> :::::: d7c7544c3d5f59033d1bf3236bc7b289f5f26b75 netns xfrm: deal with dst entries in netns
>
> :::::: TO: Alexey Dobriyan <adobriyan@gmail.com>
> :::::: CC: David S. Miller <davem@davemloft.net>
>
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1258796 — [PATCHv3] xfrm: dst_entries_init() per-net dst_ops

FromDan Streetman <dan.streetman@canonical.com>
Date2015-10-29 15:00 +0100
Subject[PATCHv3] xfrm: dst_entries_init() per-net dst_ops
Message-ID<qoVUd-RM-5@gated-at.bofh.it>
In reply to#1258740
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>
---
Changes since v2:
 remove no longer needed struct net *net stack var
Changes since v1:
 move dst copying and entries_init from xfrm/xfrm_policy into
   ipv[46]/xfrm[46]_policy
 add dst_entries_init and destroy to xfrm[46]_policy pernet init/exit,
   so no for_each_net() is required

 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 f2606b9..59ce2b9 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -235,7 +235,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,
 	.gc =			xfrm4_garbage_collect,
 	.update_pmtu =		xfrm4_update_pmtu,
@@ -249,7 +249,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,
@@ -271,7 +271,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;
@@ -299,7 +299,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;
 
@@ -311,12 +311,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)
 {
@@ -325,13 +357,9 @@ static void __init xfrm4_policy_init(void)
 
 void __init xfrm4_init(void)
 {
-	dst_entries_init(&xfrm4_dst_ops);
-
 	xfrm4_state_init();
 	xfrm4_policy_init();
 	xfrm4_protocol_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 2cc5840..9c4c96b 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,
 	.gc =			xfrm6_garbage_collect,
 	.update_pmtu =		xfrm6_update_pmtu,
@@ -293,7 +293,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,
@@ -325,7 +325,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;
@@ -353,7 +353,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;
 
@@ -365,24 +365,52 @@ 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;
@@ -391,9 +419,7 @@ int __init xfrm6_init(void)
 	if (ret)
 		goto out_state;
 
-#ifdef CONFIG_SYSCTL
 	register_pernet_subsys(&xfrm6_net_ops);
-#endif
 out:
 	return ret;
 out_state:
@@ -405,11 +431,8 @@ out_policy:
 
 void xfrm6_fini(void)
 {
-#ifdef CONFIG_SYSCTL
 	unregister_pernet_subsys(&xfrm6_net_ops);
-#endif
 	xfrm6_protocol_fini();
 	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 09bfcba..aed6a84 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -2804,7 +2804,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;
@@ -2835,26 +2834,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);
@@ -2890,22 +2869,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);
@@ -3054,7 +3017,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.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web