[IPV4]: Store the net pointer on devinet's ctl tables
authorPavel Emelyanov <xemul@openvz.org>
Sun, 16 Dec 2007 21:31:14 +0000 (13:31 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Jan 2008 22:58:10 +0000 (14:58 -0800)
Some handers and strategies of devinet sysctl tables need
to know the net to propagate the ctl change to all the
net devices.

I use the (currently unused) extra2 pointer on the tables
to get it.

Holding the reference on the struct net is not possible,
because otherwise we'll get a net->ctl_table->net circular
dependency. But since the ctl tables are unregistered during
the net destruction, this is safe to get it w/o additional
protection.

Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/devinet.c

index 82def2c1c65050db75ea0a7163d00d0bfa72582c..8b2a44c4f82a17a45fa81e86031691169073c6a1 100644 (file)
@@ -1236,12 +1236,12 @@ errout:
 
 #ifdef CONFIG_SYSCTL
 
-static void devinet_copy_dflt_conf(int i)
+static void devinet_copy_dflt_conf(struct net *net, int i)
 {
        struct net_device *dev;
 
        read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       for_each_netdev(net, dev) {
                struct in_device *in_dev;
                rcu_read_lock();
                in_dev = __in_dev_get_rcu(dev);
@@ -1252,7 +1252,7 @@ static void devinet_copy_dflt_conf(int i)
        read_unlock(&dev_base_lock);
 }
 
-static void inet_forward_change(void)
+static void inet_forward_change(struct net *net)
 {
        struct net_device *dev;
        int on = IPV4_DEVCONF_ALL(FORWARDING);
@@ -1261,7 +1261,7 @@ static void inet_forward_change(void)
        IPV4_DEVCONF_DFLT(FORWARDING) = on;
 
        read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, dev) {
+       for_each_netdev(net, dev) {
                struct in_device *in_dev;
                rcu_read_lock();
                in_dev = __in_dev_get_rcu(dev);
@@ -1282,12 +1282,13 @@ static int devinet_conf_proc(ctl_table *ctl, int write,
 
        if (write) {
                struct ipv4_devconf *cnf = ctl->extra1;
+               struct net *net = ctl->extra2;
                int i = (int *)ctl->data - cnf->data;
 
                set_bit(i, cnf->state);
 
                if (cnf == &ipv4_devconf_dflt)
-                       devinet_copy_dflt_conf(i);
+                       devinet_copy_dflt_conf(net, i);
        }
 
        return ret;
@@ -1298,6 +1299,7 @@ static int devinet_conf_sysctl(ctl_table *table, int __user *name, int nlen,
                               void __user *newval, size_t newlen)
 {
        struct ipv4_devconf *cnf;
+       struct net *net;
        int *valp = table->data;
        int new;
        int i;
@@ -1333,12 +1335,13 @@ static int devinet_conf_sysctl(ctl_table *table, int __user *name, int nlen,
        *valp = new;
 
        cnf = table->extra1;
+       net = table->extra2;
        i = (int *)table->data - cnf->data;
 
        set_bit(i, cnf->state);
 
        if (cnf == &ipv4_devconf_dflt)
-               devinet_copy_dflt_conf(i);
+               devinet_copy_dflt_conf(net, i);
 
        return 1;
 }
@@ -1352,8 +1355,10 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
        int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
 
        if (write && *valp != val) {
+               struct net *net = ctl->extra2;
+
                if (valp == &IPV4_DEVCONF_ALL(FORWARDING))
-                       inet_forward_change();
+                       inet_forward_change(net);
                else if (valp != &IPV4_DEVCONF_DFLT(FORWARDING))
                        rt_cache_flush(0);
        }
@@ -1477,6 +1482,7 @@ static int __devinet_sysctl_register(struct net *net, char *dev_name,
        for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
                t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
                t->devinet_vars[i].extra1 = p;
+               t->devinet_vars[i].extra2 = net;
        }
 
        /*
@@ -1524,8 +1530,8 @@ static void devinet_sysctl_register(struct in_device *idev)
 {
        neigh_sysctl_register(idev->dev, idev->arp_parms, NET_IPV4,
                        NET_IPV4_NEIGH, "ipv4", NULL, NULL);
-       __devinet_sysctl_register(idev->dev->name, idev->dev->ifindex,
-                       &idev->cnf);
+       __devinet_sysctl_register(idev->dev->nd_net, idev->dev->name,
+                       idev->dev->ifindex, &idev->cnf);
 }
 
 static void devinet_sysctl_unregister(struct in_device *idev)
@@ -1546,6 +1552,7 @@ static struct ctl_table ctl_forward_entry[] = {
                .proc_handler   = devinet_sysctl_forward,
                .strategy       = devinet_conf_sysctl,
                .extra1         = &ipv4_devconf,
+               .extra2         = &init_net,
        },
        { },
 };
@@ -1565,9 +1572,9 @@ void __init devinet_init(void)
        rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
        rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
 #ifdef CONFIG_SYSCTL
-       __devinet_sysctl_register("all", NET_PROTO_CONF_ALL,
+       __devinet_sysctl_register(&init_net, "all", NET_PROTO_CONF_ALL,
                        &ipv4_devconf);
-       __devinet_sysctl_register("default", NET_PROTO_CONF_DEFAULT,
+       __devinet_sysctl_register(&init_net, "default", NET_PROTO_CONF_DEFAULT,
                        &ipv4_devconf_dflt);
        register_sysctl_paths(net_ipv4_path, ctl_forward_entry);
 #endif