diff mbox

[net-next] ipv6: separate out procfs code from mcast.c

Message ID 1366700820-26052-1-git-send-email-amwang@redhat.com
State Rejected, archived
Delegated to: David Miller
Headers show

Commit Message

Amerigo Wang April 23, 2013, 7:07 a.m. UTC
From: Cong Wang <amwang@redhat.com>

They well deserve a separated unit.

Cc: "David S. Miller" <davem@davemloft.net>
Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org> 
Signed-off-by: Cong Wang <amwang@redhat.com>

---
 net/ipv6/Makefile     |    2 
 net/ipv6/mcast.c      |  370 -----------------------------------------------
 net/ipv6/mcast_proc.c |  386 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 387 insertions(+), 371 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

David Miller April 23, 2013, 7:11 a.m. UTC | #1
From: Cong Wang <amwang@redhat.com>
Date: Tue, 23 Apr 2013 15:07:00 +0800

> From: Cong Wang <amwang@redhat.com>
> 
> They well deserve a separated unit.
> 
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org> 
> Signed-off-by: Cong Wang <amwang@redhat.com>

Cong, I'm not even reading these patches.

You're on this idea that you can just keep the sysfs and procfs bits
of ipv6 modular, and make the rest of ipv6 statically linked into the
kernel.

I'm not going to facilitate nor take this seriously.

Sorry.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Amerigo Wang April 23, 2013, 7:15 a.m. UTC | #2
On Tue, 2013-04-23 at 03:11 -0400, David Miller wrote:
> From: Cong Wang <amwang@redhat.com>
> Date: Tue, 23 Apr 2013 15:07:00 +0800
> 
> > From: Cong Wang <amwang@redhat.com>
> > 
> > They well deserve a separated unit.
> > 
> > Cc: "David S. Miller" <davem@davemloft.net>
> > Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org> 
> > Signed-off-by: Cong Wang <amwang@redhat.com>
> 
> Cong, I'm not even reading these patches.
> 
> You're on this idea that you can just keep the sysfs and procfs bits
> of ipv6 modular, and make the rest of ipv6 statically linked into the
> kernel.

I am trying that only for mcast.c, not for the whole IPv6.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller April 23, 2013, 7:18 a.m. UTC | #3
From: Cong Wang <amwang@redhat.com>
Date: Tue, 23 Apr 2013 15:15:02 +0800

> On Tue, 2013-04-23 at 03:11 -0400, David Miller wrote:
>> From: Cong Wang <amwang@redhat.com>
>> Date: Tue, 23 Apr 2013 15:07:00 +0800
>> 
>> > From: Cong Wang <amwang@redhat.com>
>> > 
>> > They well deserve a separated unit.
>> > 
>> > Cc: "David S. Miller" <davem@davemloft.net>
>> > Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org> 
>> > Signed-off-by: Cong Wang <amwang@redhat.com>
>> 
>> Cong, I'm not even reading these patches.
>> 
>> You're on this idea that you can just keep the sysfs and procfs bits
>> of ipv6 modular, and make the rest of ipv6 statically linked into the
>> kernel.
> 
> I am trying that only for mcast.c, not for the whole IPv6.

The multicast stuff does route lookups, how are you going to
accomplish this?

Besides you should move the statically-needed code into a new
file (mcast_core.c or something like that, following the existing
model and conventions for doing this) rather than the other way
around.

And that patch should be posted in a series with the user that
needs this (your vxlan ipv6 patches), not by itself.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Amerigo Wang April 23, 2013, 7:30 a.m. UTC | #4
On Tue, 2013-04-23 at 03:18 -0400, David Miller wrote:
> From: Cong Wang <amwang@redhat.com>
> Date: Tue, 23 Apr 2013 15:15:02 +0800
> 
> > On Tue, 2013-04-23 at 03:11 -0400, David Miller wrote:
> >> From: Cong Wang <amwang@redhat.com>
> >> Date: Tue, 23 Apr 2013 15:07:00 +0800
> >> 
> >> > From: Cong Wang <amwang@redhat.com>
> >> > 
> >> > They well deserve a separated unit.
> >> > 
> >> > Cc: "David S. Miller" <davem@davemloft.net>
> >> > Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org> 
> >> > Signed-off-by: Cong Wang <amwang@redhat.com>
> >> 
> >> Cong, I'm not even reading these patches.
> >> 
> >> You're on this idea that you can just keep the sysfs and procfs bits
> >> of ipv6 modular, and make the rest of ipv6 statically linked into the
> >> kernel.
> > 
> > I am trying that only for mcast.c, not for the whole IPv6.
> 
> The multicast stuff does route lookups, how are you going to
> accomplish this?

This is hard to do, as in the email I replied to Stephen. So I prefer to
just add a Kconfig dependency, at least for now.

> 
> Besides you should move the statically-needed code into a new
> file (mcast_core.c or something like that, following the existing
> model and conventions for doing this) rather than the other way
> around.

Yes, this is half-done and is also why I sent it alone rather than in
the VXLAN series.

Thanks.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller April 23, 2013, 7:36 a.m. UTC | #5
From: Cong Wang <amwang@redhat.com>
Date: Tue, 23 Apr 2013 15:30:20 +0800

> This is hard to do, as in the email I replied to Stephen. So I
> prefer to just add a Kconfig dependency, at least for now.

A Kconfig hack is exactly what I've told you is an unacceptable
solution.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Amerigo Wang April 23, 2013, 7:41 a.m. UTC | #6
On Tue, 2013-04-23 at 03:36 -0400, David Miller wrote:
> From: Cong Wang <amwang@redhat.com>
> Date: Tue, 23 Apr 2013 15:30:20 +0800
> 
> > This is hard to do, as in the email I replied to Stephen. So I
> > prefer to just add a Kconfig dependency, at least for now.
> 
> A Kconfig hack is exactly what I've told you is an unacceptable
> solution.

Please enlighten me for a third solution. :)


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bjørn Mork April 23, 2013, 12:33 p.m. UTC | #7
Cong Wang <amwang@redhat.com> writes:

> On Tue, 2013-04-23 at 03:36 -0400, David Miller wrote:
>> From: Cong Wang <amwang@redhat.com>
>> Date: Tue, 23 Apr 2013 15:30:20 +0800
>> 
>> > This is hard to do, as in the email I replied to Stephen. So I
>> > prefer to just add a Kconfig dependency, at least for now.
>> 
>> A Kconfig hack is exactly what I've told you is an unacceptable
>> solution.
>
> Please enlighten me for a third solution. :)

This is an completely untested idea....

I note that vxlan_init_net requires a successful sock_create_kern().
That implies a request_module("net-pf-...") followed by try_module_get.

So if the sock_create_kern(PF_INET6, ..) succeeds then you *know* you
have IPv6. If it fails with -EAFNOSUPPORT, then you could fall back to
sock_create_kern(PF_INET, ..) and set a flag indicating that runtime
IPv6 support is disabled.  Then use this flag to allow/deny configuring
any IPv6 destinations.

You may also have to protect the IPv6 modular symbols you use with
symbol_request() or similar to prevent vxlan from depending on IPv6.  I
dunno...

If nothing else, I believe this is a crazy enough hack that David may
want to reconsider one of your other two solutions :)



Bjørn
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings April 23, 2013, 4:13 p.m. UTC | #8
On Tue, 2013-04-23 at 14:33 +0200, Bjørn Mork wrote:
> Cong Wang <amwang@redhat.com> writes:
> 
> > On Tue, 2013-04-23 at 03:36 -0400, David Miller wrote:
> >> From: Cong Wang <amwang@redhat.com>
> >> Date: Tue, 23 Apr 2013 15:30:20 +0800
> >> 
> >> > This is hard to do, as in the email I replied to Stephen. So I
> >> > prefer to just add a Kconfig dependency, at least for now.
> >> 
> >> A Kconfig hack is exactly what I've told you is an unacceptable
> >> solution.
> >
> > Please enlighten me for a third solution. :)
> 
> This is an completely untested idea....
> 
> I note that vxlan_init_net requires a successful sock_create_kern().
> That implies a request_module("net-pf-...") followed by try_module_get.
> 
> So if the sock_create_kern(PF_INET6, ..) succeeds then you *know* you
> have IPv6. If it fails with -EAFNOSUPPORT, then you could fall back to
> sock_create_kern(PF_INET, ..) and set a flag indicating that runtime
> IPv6 support is disabled.  Then use this flag to allow/deny configuring
> any IPv6 destinations.
> 
> You may also have to protect the IPv6 modular symbols you use with
> symbol_request() or similar to prevent vxlan from depending on IPv6.  I
> dunno...
> 
> If nothing else, I believe this is a crazy enough hack that David may
> want to reconsider one of your other two solutions :)

A more type-safe approach would be to define something like:

/* net/ipv6.h */
struct ipv6_mcast_ops {
	...
};
extern const struct ipv6_mcast_ops *ipv6_mcast_ops;

/* net/ipv6/addrconf_core.c */
const struct ipv6_mcast_ops *ipv6_mcast_ops;
EXPORT_SYMBOL_GPL(ipv6_mcast_ops);

/* net/ipv6/af_inet6.c */
static const struct ipv6_mcast_ops ipv6_mcast_ops_impl = {
	...
};
static int __init inet6_init(void)
{
	...
	ipv6_mcast_ops = &ipv6_mcast_ops_impl;
	return 0;
	...
}
static void __exit inet6_exit(void)
{
	...
	ipv6_mcast_ops = NULL;
	...
}

Ben.
David Stevens April 23, 2013, 5:26 p.m. UTC | #9
netdev-owner@vger.kernel.org wrote on 04/23/2013 08:33:57 AM:

> From: Bjørn Mork <bjorn@mork.no>
 
> Cong Wang <amwang@redhat.com> writes:

> I note that vxlan_init_net requires a successful sock_create_kern().
> That implies a request_module("net-pf-...") followed by try_module_get.
> 
> So if the sock_create_kern(PF_INET6, ..) succeeds then you *know* you
> have IPv6. If it fails with -EAFNOSUPPORT, then you could fall back to
> sock_create_kern(PF_INET, ..) and set a flag indicating that runtime
> IPv6 support is disabled.  Then use this flag to allow/deny configuring
> any IPv6 destinations.

BTW, another thing I thought of after I sent my patch comments -- you
also need to consider the state of net.ipv6.bindv6only. When v6 is
enabled, you use one socket for v4 and v6, but if bindv6only=1, you
actually need a separate socket for v4 receives. If you want to support
both v4 and v6 with bindv6only=1, you need two sockets. Or even if you
want to support v4-only VXLAN and the sysadmin has set bindv6only=1.

                                                                +-DLS

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Amerigo Wang April 24, 2013, 3:29 a.m. UTC | #10
On Tue, 2013-04-23 at 14:33 +0200, Bjørn Mork wrote:
> Cong Wang <amwang@redhat.com> writes:
> 
> > On Tue, 2013-04-23 at 03:36 -0400, David Miller wrote:
> >> From: Cong Wang <amwang@redhat.com>
> >> Date: Tue, 23 Apr 2013 15:30:20 +0800
> >> 
> >> > This is hard to do, as in the email I replied to Stephen. So I
> >> > prefer to just add a Kconfig dependency, at least for now.
> >> 
> >> A Kconfig hack is exactly what I've told you is an unacceptable
> >> solution.
> >
> > Please enlighten me for a third solution. :)
> 
> This is an completely untested idea....
> 
> I note that vxlan_init_net requires a successful sock_create_kern().
> That implies a request_module("net-pf-...") followed by try_module_get.
> 
> So if the sock_create_kern(PF_INET6, ..) succeeds then you *know* you
> have IPv6. If it fails with -EAFNOSUPPORT, then you could fall back to
> sock_create_kern(PF_INET, ..) and set a flag indicating that runtime
> IPv6 support is disabled.  Then use this flag to allow/deny configuring
> any IPv6 destinations.

Excellent point! You are certainly right. I will give it a try.

> 
> You may also have to protect the IPv6 modular symbols you use with
> symbol_request() or similar to prevent vxlan from depending on IPv6.  I
> dunno...
> 

This sounds a bit crazy.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Amerigo Wang April 24, 2013, 3:33 a.m. UTC | #11
On Tue, 2013-04-23 at 17:13 +0100, Ben Hutchings wrote:
> 
> A more type-safe approach would be to define something like:
> 
> /* net/ipv6.h */
> struct ipv6_mcast_ops {
> 	...
> };
> extern const struct ipv6_mcast_ops *ipv6_mcast_ops;
> 
> /* net/ipv6/addrconf_core.c */
> const struct ipv6_mcast_ops *ipv6_mcast_ops;
> EXPORT_SYMBOL_GPL(ipv6_mcast_ops);
> 
> /* net/ipv6/af_inet6.c */
> static const struct ipv6_mcast_ops ipv6_mcast_ops_impl = {
> 	...
> };
> static int __init inet6_init(void)
> {
> 	...
> 	ipv6_mcast_ops = &ipv6_mcast_ops_impl;
> 	return 0;
> 	...
> }
> static void __exit inet6_exit(void)
> {
> 	...
> 	ipv6_mcast_ops = NULL;
> 	...
> }

Yeah! That's a nice stub! I think this is the only doable and acceptable
solution so far.

Thanks a lot!

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Amerigo Wang April 24, 2013, 5:26 a.m. UTC | #12
On Tue, 2013-04-23 at 13:26 -0400, David Stevens wrote:
> BTW, another thing I thought of after I sent my patch comments -- you
> also need to consider the state of net.ipv6.bindv6only. When v6 is
> enabled, you use one socket for v4 and v6, but if bindv6only=1, you
> actually need a separate socket for v4 receives. If you want to support
> both v4 and v6 with bindv6only=1, you need two sockets. Or even if you
> want to support v4-only VXLAN and the sysadmin has set bindv6only=1.
> 

Good point!

Actually the initial version of my patch did use two sockets for v4 and
v6. I am adding this to my TODO list.

Thanks!

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 309af19..157b5e7 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -19,7 +19,7 @@  ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
 	xfrm6_output.o
 ipv6-$(CONFIG_NETFILTER) += netfilter.o
 ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
-ipv6-$(CONFIG_PROC_FS) += proc.o
+ipv6-$(CONFIG_PROC_FS) += proc.o mcast_proc.o
 ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o
 
 ipv6-objs += $(ipv6-y)
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index bfa6cc3..821665b 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -26,7 +26,6 @@ 
  *		- MLDv2 support
  */
 
-#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/string.h>
@@ -41,8 +40,6 @@ 
 #include <linux/if_arp.h>
 #include <linux/route.h>
 #include <linux/init.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <net/mld.h>
 
@@ -2307,370 +2304,3 @@  void ipv6_mc_destroy_dev(struct inet6_dev *idev)
 	write_unlock_bh(&idev->lock);
 }
 
-#ifdef CONFIG_PROC_FS
-struct igmp6_mc_iter_state {
-	struct seq_net_private p;
-	struct net_device *dev;
-	struct inet6_dev *idev;
-};
-
-#define igmp6_mc_seq_private(seq)	((struct igmp6_mc_iter_state *)(seq)->private)
-
-static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
-{
-	struct ifmcaddr6 *im = NULL;
-	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
-	struct net *net = seq_file_net(seq);
-
-	state->idev = NULL;
-	for_each_netdev_rcu(net, state->dev) {
-		struct inet6_dev *idev;
-		idev = __in6_dev_get(state->dev);
-		if (!idev)
-			continue;
-		read_lock_bh(&idev->lock);
-		im = idev->mc_list;
-		if (im) {
-			state->idev = idev;
-			break;
-		}
-		read_unlock_bh(&idev->lock);
-	}
-	return im;
-}
-
-static struct ifmcaddr6 *igmp6_mc_get_next(struct seq_file *seq, struct ifmcaddr6 *im)
-{
-	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
-
-	im = im->next;
-	while (!im) {
-		if (likely(state->idev != NULL))
-			read_unlock_bh(&state->idev->lock);
-
-		state->dev = next_net_device_rcu(state->dev);
-		if (!state->dev) {
-			state->idev = NULL;
-			break;
-		}
-		state->idev = __in6_dev_get(state->dev);
-		if (!state->idev)
-			continue;
-		read_lock_bh(&state->idev->lock);
-		im = state->idev->mc_list;
-	}
-	return im;
-}
-
-static struct ifmcaddr6 *igmp6_mc_get_idx(struct seq_file *seq, loff_t pos)
-{
-	struct ifmcaddr6 *im = igmp6_mc_get_first(seq);
-	if (im)
-		while (pos && (im = igmp6_mc_get_next(seq, im)) != NULL)
-			--pos;
-	return pos ? NULL : im;
-}
-
-static void *igmp6_mc_seq_start(struct seq_file *seq, loff_t *pos)
-	__acquires(RCU)
-{
-	rcu_read_lock();
-	return igmp6_mc_get_idx(seq, *pos);
-}
-
-static void *igmp6_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-	struct ifmcaddr6 *im = igmp6_mc_get_next(seq, v);
-
-	++*pos;
-	return im;
-}
-
-static void igmp6_mc_seq_stop(struct seq_file *seq, void *v)
-	__releases(RCU)
-{
-	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
-
-	if (likely(state->idev != NULL)) {
-		read_unlock_bh(&state->idev->lock);
-		state->idev = NULL;
-	}
-	state->dev = NULL;
-	rcu_read_unlock();
-}
-
-static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
-{
-	struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;
-	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
-
-	seq_printf(seq,
-		   "%-4d %-15s %pi6 %5d %08X %ld\n",
-		   state->dev->ifindex, state->dev->name,
-		   &im->mca_addr,
-		   im->mca_users, im->mca_flags,
-		   (im->mca_flags&MAF_TIMER_RUNNING) ?
-		   jiffies_to_clock_t(im->mca_timer.expires-jiffies) : 0);
-	return 0;
-}
-
-static const struct seq_operations igmp6_mc_seq_ops = {
-	.start	=	igmp6_mc_seq_start,
-	.next	=	igmp6_mc_seq_next,
-	.stop	=	igmp6_mc_seq_stop,
-	.show	=	igmp6_mc_seq_show,
-};
-
-static int igmp6_mc_seq_open(struct inode *inode, struct file *file)
-{
-	return seq_open_net(inode, file, &igmp6_mc_seq_ops,
-			    sizeof(struct igmp6_mc_iter_state));
-}
-
-static const struct file_operations igmp6_mc_seq_fops = {
-	.owner		=	THIS_MODULE,
-	.open		=	igmp6_mc_seq_open,
-	.read		=	seq_read,
-	.llseek		=	seq_lseek,
-	.release	=	seq_release_net,
-};
-
-struct igmp6_mcf_iter_state {
-	struct seq_net_private p;
-	struct net_device *dev;
-	struct inet6_dev *idev;
-	struct ifmcaddr6 *im;
-};
-
-#define igmp6_mcf_seq_private(seq)	((struct igmp6_mcf_iter_state *)(seq)->private)
-
-static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
-{
-	struct ip6_sf_list *psf = NULL;
-	struct ifmcaddr6 *im = NULL;
-	struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
-	struct net *net = seq_file_net(seq);
-
-	state->idev = NULL;
-	state->im = NULL;
-	for_each_netdev_rcu(net, state->dev) {
-		struct inet6_dev *idev;
-		idev = __in6_dev_get(state->dev);
-		if (unlikely(idev == NULL))
-			continue;
-		read_lock_bh(&idev->lock);
-		im = idev->mc_list;
-		if (likely(im != NULL)) {
-			spin_lock_bh(&im->mca_lock);
-			psf = im->mca_sources;
-			if (likely(psf != NULL)) {
-				state->im = im;
-				state->idev = idev;
-				break;
-			}
-			spin_unlock_bh(&im->mca_lock);
-		}
-		read_unlock_bh(&idev->lock);
-	}
-	return psf;
-}
-
-static struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_sf_list *psf)
-{
-	struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
-
-	psf = psf->sf_next;
-	while (!psf) {
-		spin_unlock_bh(&state->im->mca_lock);
-		state->im = state->im->next;
-		while (!state->im) {
-			if (likely(state->idev != NULL))
-				read_unlock_bh(&state->idev->lock);
-
-			state->dev = next_net_device_rcu(state->dev);
-			if (!state->dev) {
-				state->idev = NULL;
-				goto out;
-			}
-			state->idev = __in6_dev_get(state->dev);
-			if (!state->idev)
-				continue;
-			read_lock_bh(&state->idev->lock);
-			state->im = state->idev->mc_list;
-		}
-		if (!state->im)
-			break;
-		spin_lock_bh(&state->im->mca_lock);
-		psf = state->im->mca_sources;
-	}
-out:
-	return psf;
-}
-
-static struct ip6_sf_list *igmp6_mcf_get_idx(struct seq_file *seq, loff_t pos)
-{
-	struct ip6_sf_list *psf = igmp6_mcf_get_first(seq);
-	if (psf)
-		while (pos && (psf = igmp6_mcf_get_next(seq, psf)) != NULL)
-			--pos;
-	return pos ? NULL : psf;
-}
-
-static void *igmp6_mcf_seq_start(struct seq_file *seq, loff_t *pos)
-	__acquires(RCU)
-{
-	rcu_read_lock();
-	return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
-}
-
-static void *igmp6_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-	struct ip6_sf_list *psf;
-	if (v == SEQ_START_TOKEN)
-		psf = igmp6_mcf_get_first(seq);
-	else
-		psf = igmp6_mcf_get_next(seq, v);
-	++*pos;
-	return psf;
-}
-
-static void igmp6_mcf_seq_stop(struct seq_file *seq, void *v)
-	__releases(RCU)
-{
-	struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
-	if (likely(state->im != NULL)) {
-		spin_unlock_bh(&state->im->mca_lock);
-		state->im = NULL;
-	}
-	if (likely(state->idev != NULL)) {
-		read_unlock_bh(&state->idev->lock);
-		state->idev = NULL;
-	}
-	state->dev = NULL;
-	rcu_read_unlock();
-}
-
-static int igmp6_mcf_seq_show(struct seq_file *seq, void *v)
-{
-	struct ip6_sf_list *psf = (struct ip6_sf_list *)v;
-	struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
-
-	if (v == SEQ_START_TOKEN) {
-		seq_printf(seq,
-			   "%3s %6s "
-			   "%32s %32s %6s %6s\n", "Idx",
-			   "Device", "Multicast Address",
-			   "Source Address", "INC", "EXC");
-	} else {
-		seq_printf(seq,
-			   "%3d %6.6s %pi6 %pi6 %6lu %6lu\n",
-			   state->dev->ifindex, state->dev->name,
-			   &state->im->mca_addr,
-			   &psf->sf_addr,
-			   psf->sf_count[MCAST_INCLUDE],
-			   psf->sf_count[MCAST_EXCLUDE]);
-	}
-	return 0;
-}
-
-static const struct seq_operations igmp6_mcf_seq_ops = {
-	.start	=	igmp6_mcf_seq_start,
-	.next	=	igmp6_mcf_seq_next,
-	.stop	=	igmp6_mcf_seq_stop,
-	.show	=	igmp6_mcf_seq_show,
-};
-
-static int igmp6_mcf_seq_open(struct inode *inode, struct file *file)
-{
-	return seq_open_net(inode, file, &igmp6_mcf_seq_ops,
-			    sizeof(struct igmp6_mcf_iter_state));
-}
-
-static const struct file_operations igmp6_mcf_seq_fops = {
-	.owner		=	THIS_MODULE,
-	.open		=	igmp6_mcf_seq_open,
-	.read		=	seq_read,
-	.llseek		=	seq_lseek,
-	.release	=	seq_release_net,
-};
-
-static int __net_init igmp6_proc_init(struct net *net)
-{
-	int err;
-
-	err = -ENOMEM;
-	if (!proc_create("igmp6", S_IRUGO, net->proc_net, &igmp6_mc_seq_fops))
-		goto out;
-	if (!proc_create("mcfilter6", S_IRUGO, net->proc_net,
-			 &igmp6_mcf_seq_fops))
-		goto out_proc_net_igmp6;
-
-	err = 0;
-out:
-	return err;
-
-out_proc_net_igmp6:
-	remove_proc_entry("igmp6", net->proc_net);
-	goto out;
-}
-
-static void __net_exit igmp6_proc_exit(struct net *net)
-{
-	remove_proc_entry("mcfilter6", net->proc_net);
-	remove_proc_entry("igmp6", net->proc_net);
-}
-#else
-static inline int igmp6_proc_init(struct net *net)
-{
-	return 0;
-}
-static inline void igmp6_proc_exit(struct net *net)
-{
-}
-#endif
-
-static int __net_init igmp6_net_init(struct net *net)
-{
-	int err;
-
-	err = inet_ctl_sock_create(&net->ipv6.igmp_sk, PF_INET6,
-				   SOCK_RAW, IPPROTO_ICMPV6, net);
-	if (err < 0) {
-		pr_err("Failed to initialize the IGMP6 control socket (err %d)\n",
-		       err);
-		goto out;
-	}
-
-	inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1;
-
-	err = igmp6_proc_init(net);
-	if (err)
-		goto out_sock_create;
-out:
-	return err;
-
-out_sock_create:
-	inet_ctl_sock_destroy(net->ipv6.igmp_sk);
-	goto out;
-}
-
-static void __net_exit igmp6_net_exit(struct net *net)
-{
-	inet_ctl_sock_destroy(net->ipv6.igmp_sk);
-	igmp6_proc_exit(net);
-}
-
-static struct pernet_operations igmp6_net_ops = {
-	.init = igmp6_net_init,
-	.exit = igmp6_net_exit,
-};
-
-int __init igmp6_init(void)
-{
-	return register_pernet_subsys(&igmp6_net_ops);
-}
-
-void igmp6_cleanup(void)
-{
-	unregister_pernet_subsys(&igmp6_net_ops);
-}
diff --git a/net/ipv6/mcast_proc.c b/net/ipv6/mcast_proc.c
new file mode 100644
index 0000000..887af797
--- /dev/null
+++ b/net/ipv6/mcast_proc.c
@@ -0,0 +1,386 @@ 
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/jiffies.h>
+#include <linux/times.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/route.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <net/mld.h>
+
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/if_inet6.h>
+#include <net/addrconf.h>
+#include <net/inet_common.h>
+
+struct igmp6_mc_iter_state {
+	struct seq_net_private p;
+	struct net_device *dev;
+	struct inet6_dev *idev;
+};
+
+#define igmp6_mc_seq_private(seq)	((struct igmp6_mc_iter_state *)(seq)->private)
+
+static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
+{
+	struct ifmcaddr6 *im = NULL;
+	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+	struct net *net = seq_file_net(seq);
+
+	state->idev = NULL;
+	for_each_netdev_rcu(net, state->dev) {
+		struct inet6_dev *idev;
+		idev = __in6_dev_get(state->dev);
+		if (!idev)
+			continue;
+		read_lock_bh(&idev->lock);
+		im = idev->mc_list;
+		if (im) {
+			state->idev = idev;
+			break;
+		}
+		read_unlock_bh(&idev->lock);
+	}
+	return im;
+}
+
+static struct ifmcaddr6 *igmp6_mc_get_next(struct seq_file *seq, struct ifmcaddr6 *im)
+{
+	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+
+	im = im->next;
+	while (!im) {
+		if (likely(state->idev != NULL))
+			read_unlock_bh(&state->idev->lock);
+
+		state->dev = next_net_device_rcu(state->dev);
+		if (!state->dev) {
+			state->idev = NULL;
+			break;
+		}
+		state->idev = __in6_dev_get(state->dev);
+		if (!state->idev)
+			continue;
+		read_lock_bh(&state->idev->lock);
+		im = state->idev->mc_list;
+	}
+	return im;
+}
+
+static struct ifmcaddr6 *igmp6_mc_get_idx(struct seq_file *seq, loff_t pos)
+{
+	struct ifmcaddr6 *im = igmp6_mc_get_first(seq);
+	if (im)
+		while (pos && (im = igmp6_mc_get_next(seq, im)) != NULL)
+			--pos;
+	return pos ? NULL : im;
+}
+
+static void *igmp6_mc_seq_start(struct seq_file *seq, loff_t *pos)
+	__acquires(RCU)
+{
+	rcu_read_lock();
+	return igmp6_mc_get_idx(seq, *pos);
+}
+
+static void *igmp6_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct ifmcaddr6 *im = igmp6_mc_get_next(seq, v);
+
+	++*pos;
+	return im;
+}
+
+static void igmp6_mc_seq_stop(struct seq_file *seq, void *v)
+	__releases(RCU)
+{
+	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+
+	if (likely(state->idev != NULL)) {
+		read_unlock_bh(&state->idev->lock);
+		state->idev = NULL;
+	}
+	state->dev = NULL;
+	rcu_read_unlock();
+}
+
+static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
+{
+	struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;
+	struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+
+	seq_printf(seq,
+		   "%-4d %-15s %pi6 %5d %08X %ld\n",
+		   state->dev->ifindex, state->dev->name,
+		   &im->mca_addr,
+		   im->mca_users, im->mca_flags,
+		   (im->mca_flags&MAF_TIMER_RUNNING) ?
+		   jiffies_to_clock_t(im->mca_timer.expires-jiffies) : 0);
+	return 0;
+}
+
+static const struct seq_operations igmp6_mc_seq_ops = {
+	.start	=	igmp6_mc_seq_start,
+	.next	=	igmp6_mc_seq_next,
+	.stop	=	igmp6_mc_seq_stop,
+	.show	=	igmp6_mc_seq_show,
+};
+
+static int igmp6_mc_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open_net(inode, file, &igmp6_mc_seq_ops,
+			    sizeof(struct igmp6_mc_iter_state));
+}
+
+static const struct file_operations igmp6_mc_seq_fops = {
+	.owner		=	THIS_MODULE,
+	.open		=	igmp6_mc_seq_open,
+	.read		=	seq_read,
+	.llseek		=	seq_lseek,
+	.release	=	seq_release_net,
+};
+
+struct igmp6_mcf_iter_state {
+	struct seq_net_private p;
+	struct net_device *dev;
+	struct inet6_dev *idev;
+	struct ifmcaddr6 *im;
+};
+
+#define igmp6_mcf_seq_private(seq)	((struct igmp6_mcf_iter_state *)(seq)->private)
+
+static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
+{
+	struct ip6_sf_list *psf = NULL;
+	struct ifmcaddr6 *im = NULL;
+	struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
+	struct net *net = seq_file_net(seq);
+
+	state->idev = NULL;
+	state->im = NULL;
+	for_each_netdev_rcu(net, state->dev) {
+		struct inet6_dev *idev;
+		idev = __in6_dev_get(state->dev);
+		if (unlikely(idev == NULL))
+			continue;
+		read_lock_bh(&idev->lock);
+		im = idev->mc_list;
+		if (likely(im != NULL)) {
+			spin_lock_bh(&im->mca_lock);
+			psf = im->mca_sources;
+			if (likely(psf != NULL)) {
+				state->im = im;
+				state->idev = idev;
+				break;
+			}
+			spin_unlock_bh(&im->mca_lock);
+		}
+		read_unlock_bh(&idev->lock);
+	}
+	return psf;
+}
+
+static struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_sf_list *psf)
+{
+	struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
+
+	psf = psf->sf_next;
+	while (!psf) {
+		spin_unlock_bh(&state->im->mca_lock);
+		state->im = state->im->next;
+		while (!state->im) {
+			if (likely(state->idev != NULL))
+				read_unlock_bh(&state->idev->lock);
+
+			state->dev = next_net_device_rcu(state->dev);
+			if (!state->dev) {
+				state->idev = NULL;
+				goto out;
+			}
+			state->idev = __in6_dev_get(state->dev);
+			if (!state->idev)
+				continue;
+			read_lock_bh(&state->idev->lock);
+			state->im = state->idev->mc_list;
+		}
+		if (!state->im)
+			break;
+		spin_lock_bh(&state->im->mca_lock);
+		psf = state->im->mca_sources;
+	}
+out:
+	return psf;
+}
+
+static struct ip6_sf_list *igmp6_mcf_get_idx(struct seq_file *seq, loff_t pos)
+{
+	struct ip6_sf_list *psf = igmp6_mcf_get_first(seq);
+	if (psf)
+		while (pos && (psf = igmp6_mcf_get_next(seq, psf)) != NULL)
+			--pos;
+	return pos ? NULL : psf;
+}
+
+static void *igmp6_mcf_seq_start(struct seq_file *seq, loff_t *pos)
+	__acquires(RCU)
+{
+	rcu_read_lock();
+	return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
+}
+
+static void *igmp6_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct ip6_sf_list *psf;
+	if (v == SEQ_START_TOKEN)
+		psf = igmp6_mcf_get_first(seq);
+	else
+		psf = igmp6_mcf_get_next(seq, v);
+	++*pos;
+	return psf;
+}
+
+static void igmp6_mcf_seq_stop(struct seq_file *seq, void *v)
+	__releases(RCU)
+{
+	struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
+	if (likely(state->im != NULL)) {
+		spin_unlock_bh(&state->im->mca_lock);
+		state->im = NULL;
+	}
+	if (likely(state->idev != NULL)) {
+		read_unlock_bh(&state->idev->lock);
+		state->idev = NULL;
+	}
+	state->dev = NULL;
+	rcu_read_unlock();
+}
+
+static int igmp6_mcf_seq_show(struct seq_file *seq, void *v)
+{
+	struct ip6_sf_list *psf = (struct ip6_sf_list *)v;
+	struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
+
+	if (v == SEQ_START_TOKEN) {
+		seq_printf(seq,
+			   "%3s %6s "
+			   "%32s %32s %6s %6s\n", "Idx",
+			   "Device", "Multicast Address",
+			   "Source Address", "INC", "EXC");
+	} else {
+		seq_printf(seq,
+			   "%3d %6.6s %pi6 %pi6 %6lu %6lu\n",
+			   state->dev->ifindex, state->dev->name,
+			   &state->im->mca_addr,
+			   &psf->sf_addr,
+			   psf->sf_count[MCAST_INCLUDE],
+			   psf->sf_count[MCAST_EXCLUDE]);
+	}
+	return 0;
+}
+
+static const struct seq_operations igmp6_mcf_seq_ops = {
+	.start	=	igmp6_mcf_seq_start,
+	.next	=	igmp6_mcf_seq_next,
+	.stop	=	igmp6_mcf_seq_stop,
+	.show	=	igmp6_mcf_seq_show,
+};
+
+static int igmp6_mcf_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open_net(inode, file, &igmp6_mcf_seq_ops,
+			    sizeof(struct igmp6_mcf_iter_state));
+}
+
+static const struct file_operations igmp6_mcf_seq_fops = {
+	.owner		=	THIS_MODULE,
+	.open		=	igmp6_mcf_seq_open,
+	.read		=	seq_read,
+	.llseek		=	seq_lseek,
+	.release	=	seq_release_net,
+};
+
+static int __net_init igmp6_proc_init(struct net *net)
+{
+	int err;
+
+	err = -ENOMEM;
+	if (!proc_create("igmp6", S_IRUGO, net->proc_net, &igmp6_mc_seq_fops))
+		goto out;
+	if (!proc_create("mcfilter6", S_IRUGO, net->proc_net,
+			 &igmp6_mcf_seq_fops))
+		goto out_proc_net_igmp6;
+
+	err = 0;
+out:
+	return err;
+
+out_proc_net_igmp6:
+	remove_proc_entry("igmp6", net->proc_net);
+	goto out;
+}
+
+static void __net_exit igmp6_proc_exit(struct net *net)
+{
+	remove_proc_entry("mcfilter6", net->proc_net);
+	remove_proc_entry("igmp6", net->proc_net);
+}
+
+static int __net_init igmp6_net_init(struct net *net)
+{
+	int err;
+
+	err = inet_ctl_sock_create(&net->ipv6.igmp_sk, PF_INET6,
+				   SOCK_RAW, IPPROTO_ICMPV6, net);
+	if (err < 0) {
+		pr_err("Failed to initialize the IGMP6 control socket (err %d)\n",
+		       err);
+		goto out;
+	}
+
+	inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1;
+
+	err = igmp6_proc_init(net);
+	if (err)
+		goto out_sock_create;
+out:
+	return err;
+
+out_sock_create:
+	inet_ctl_sock_destroy(net->ipv6.igmp_sk);
+	goto out;
+}
+
+static void __net_exit igmp6_net_exit(struct net *net)
+{
+	inet_ctl_sock_destroy(net->ipv6.igmp_sk);
+	igmp6_proc_exit(net);
+}
+
+static struct pernet_operations igmp6_net_ops = {
+	.init = igmp6_net_init,
+	.exit = igmp6_net_exit,
+};
+
+int __init igmp6_init(void)
+{
+	return register_pernet_subsys(&igmp6_net_ops);
+}
+
+void igmp6_cleanup(void)
+{
+	unregister_pernet_subsys(&igmp6_net_ops);
+}