Commit 465edbaa authored by Hideaki Yoshifuji's avatar Hideaki Yoshifuji Committed by David S. Miller

[IPV6]: Unregister per-device snmp6 proc entry earlier.

Do it in addrconf_ifdown.  This fixes OOPSes on shutdown
with 2.6.10
Signed-off-by: default avatarHideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b6f0a1dc
...@@ -149,6 +149,8 @@ extern atomic_t inet6_sock_nr; ...@@ -149,6 +149,8 @@ extern atomic_t inet6_sock_nr;
int snmp6_register_dev(struct inet6_dev *idev); int snmp6_register_dev(struct inet6_dev *idev);
int snmp6_unregister_dev(struct inet6_dev *idev); int snmp6_unregister_dev(struct inet6_dev *idev);
int snmp6_alloc_dev(struct inet6_dev *idev);
int snmp6_free_dev(struct inet6_dev *idev);
int snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign); int snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign);
void snmp6_mib_free(void *ptr[2]); void snmp6_mib_free(void *ptr[2]);
......
...@@ -308,7 +308,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev) ...@@ -308,7 +308,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
printk("Freeing alive inet6 device %p\n", idev); printk("Freeing alive inet6 device %p\n", idev);
return; return;
} }
snmp6_unregister_dev(idev); snmp6_free_dev(idev);
kfree(idev); kfree(idev);
} }
...@@ -339,6 +339,16 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) ...@@ -339,6 +339,16 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
/* We refer to the device */ /* We refer to the device */
dev_hold(dev); dev_hold(dev);
if (snmp6_alloc_dev(ndev) < 0) {
ADBG((KERN_WARNING
"%s(): cannot allocate memory for statistics; dev=%s.\n",
__FUNCTION__, dev->name));
neigh_parms_release(&nd_tbl, ndev->nd_parms);
ndev->dead = 1;
in6_dev_finish_destroy(ndev);
return NULL;
}
if (snmp6_register_dev(ndev) < 0) { if (snmp6_register_dev(ndev) < 0) {
ADBG((KERN_WARNING ADBG((KERN_WARNING
"%s(): cannot create /proc/net/dev_snmp6/%s\n", "%s(): cannot create /proc/net/dev_snmp6/%s\n",
...@@ -2013,6 +2023,10 @@ static int addrconf_ifdown(struct net_device *dev, int how) ...@@ -2013,6 +2023,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
dev->ip6_ptr = NULL; dev->ip6_ptr = NULL;
idev->dead = 1; idev->dead = 1;
write_unlock_bh(&addrconf_lock); write_unlock_bh(&addrconf_lock);
/* Step 1.5: remove snmp6 entry */
snmp6_unregister_dev(idev);
} }
/* Step 2: clear hash table */ /* Step 2: clear hash table */
......
...@@ -652,8 +652,10 @@ snmp6_mib_free(void *ptr[2]) ...@@ -652,8 +652,10 @@ snmp6_mib_free(void *ptr[2])
{ {
if (ptr == NULL) if (ptr == NULL)
return; return;
free_percpu(ptr[0]); if (ptr[0])
free_percpu(ptr[1]); free_percpu(ptr[0]);
if (ptr[1])
free_percpu(ptr[1]);
ptr[0] = ptr[1] = NULL; ptr[0] = ptr[1] = NULL;
} }
......
...@@ -201,33 +201,23 @@ static struct file_operations snmp6_seq_fops = { ...@@ -201,33 +201,23 @@ static struct file_operations snmp6_seq_fops = {
int snmp6_register_dev(struct inet6_dev *idev) int snmp6_register_dev(struct inet6_dev *idev)
{ {
int err = -ENOMEM;
struct proc_dir_entry *p; struct proc_dir_entry *p;
if (!idev || !idev->dev) if (!idev || !idev->dev)
return -EINVAL; return -EINVAL;
if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib), if (!proc_net_devsnmp6)
__alignof__(struct icmpv6_mib)) < 0) return -ENOENT;
goto err_icmp;
if (!proc_net_devsnmp6) {
err = -ENOENT;
goto err_proc;
}
p = create_proc_entry(idev->dev->name, S_IRUGO, proc_net_devsnmp6); p = create_proc_entry(idev->dev->name, S_IRUGO, proc_net_devsnmp6);
if (!p) if (!p)
goto err_proc; return -ENOMEM;
p->data = idev; p->data = idev;
p->proc_fops = &snmp6_seq_fops; p->proc_fops = &snmp6_seq_fops;
idev->stats.proc_dir_entry = p; idev->stats.proc_dir_entry = p;
return 0; return 0;
err_proc:
snmp6_mib_free((void **)idev->stats.icmpv6);
err_icmp:
return err;
} }
int snmp6_unregister_dev(struct inet6_dev *idev) int snmp6_unregister_dev(struct inet6_dev *idev)
...@@ -238,8 +228,6 @@ int snmp6_unregister_dev(struct inet6_dev *idev) ...@@ -238,8 +228,6 @@ int snmp6_unregister_dev(struct inet6_dev *idev)
return -EINVAL; return -EINVAL;
remove_proc_entry(idev->stats.proc_dir_entry->name, remove_proc_entry(idev->stats.proc_dir_entry->name,
proc_net_devsnmp6); proc_net_devsnmp6);
snmp6_mib_free((void **)idev->stats.icmpv6);
return 0; return 0;
} }
...@@ -279,6 +267,17 @@ void ipv6_misc_proc_exit(void) ...@@ -279,6 +267,17 @@ void ipv6_misc_proc_exit(void)
int snmp6_register_dev(struct inet6_dev *idev) int snmp6_register_dev(struct inet6_dev *idev)
{
return 0;
}
int snmp6_unregister_dev(struct inet6_dev *idev)
{
return 0;
}
#endif /* CONFIG_PROC_FS */
int snmp6_alloc_dev(struct inet6_dev *idev)
{ {
int err = -ENOMEM; int err = -ENOMEM;
...@@ -295,11 +294,10 @@ int snmp6_register_dev(struct inet6_dev *idev) ...@@ -295,11 +294,10 @@ int snmp6_register_dev(struct inet6_dev *idev)
return err; return err;
} }
int snmp6_unregister_dev(struct inet6_dev *idev) int snmp6_free_dev(struct inet6_dev *idev)
{ {
snmp6_mib_free((void **)idev->stats.icmpv6); snmp6_mib_free((void **)idev->stats.icmpv6);
return 0; return 0;
} }
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment