@@ -211,6 +211,38 @@ static inline void inet_free_ifa(struct in_ifaddr *ifa)
call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
}
+static int snmp_alloc_dev(struct in_device *idev)
+{
+ if (snmp_mib_init((void __percpu **)idev->stats.ip,
+ sizeof(struct ipstats_mib),
+ __alignof__(struct ipstats_mib)) < 0)
+ goto err_ip;
+ idev->stats.icmpdev = kzalloc(sizeof(struct icmp_mib_dev),
+ GFP_KERNEL);
+ if (!idev->stats.icmpdev)
+ goto err_icmp;
+ idev->stats.icmpmsgdev = kzalloc(sizeof(struct icmpmsg_mib_dev),
+ GFP_KERNEL);
+ if (!idev->stats.icmpmsgdev)
+ goto err_icmpmsg;
+
+ return 0;
+
+err_icmpmsg:
+ kfree(idev->stats.icmpdev);
+err_icmp:
+ snmp_mib_free((void __percpu **)idev->stats.ip);
+err_ip:
+ return -ENOMEM;
+}
+
+static void snmp_free_dev(struct in_device *idev)
+{
+ kfree(idev->stats.icmpmsgdev);
+ kfree(idev->stats.icmpdev);
+ snmp_mib_free((void __percpu **)idev->stats.ip);
+}
+
void in_dev_finish_destroy(struct in_device *idev)
{
struct net_device *dev = idev->dev;
@@ -224,8 +256,10 @@ void in_dev_finish_destroy(struct in_device *idev)
dev_put(dev);
if (!idev->dead)
pr_err("Freeing alive in_device %p\n", idev);
- else
+ else {
+ snmp_free_dev(idev);
kfree(idev);
+ }
}
EXPORT_SYMBOL(in_dev_finish_destroy);
@@ -249,6 +283,22 @@ static struct in_device *inetdev_init(struct net_device *dev)
dev_disable_lro(dev);
/* Reference in_dev->dev */
dev_hold(dev);
+
+ if (snmp_alloc_dev(in_dev) < 0) {
+ printk(KERN_CRIT
+ "%s(): cannot allocate memory for statistics; dev=%s.\n",
+ __func__, dev->name);
+ neigh_parms_release(&arp_tbl, in_dev->arp_parms);
+ dev_put(dev);
+ kfree(in_dev);
+ return NULL;
+ }
+
+ if (snmp_register_dev(in_dev) < 0)
+ printk(KERN_WARNING
+ "%s(): cannot create /proc/net/dev_snmp/%s\n",
+ __func__, dev->name);
+
/* Account for reference dev->ip_ptr (below) */
in_dev_hold(in_dev);
@@ -292,6 +342,7 @@ static void inetdev_destroy(struct in_device *in_dev)
}
RCU_INIT_POINTER(dev->ip_ptr, NULL);
+ snmp_unregister_dev(in_dev);
devinet_sysctl_unregister(in_dev);
neigh_parms_release(&arp_tbl, in_dev->arp_parms);
@@ -1222,14 +1273,20 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
case NETDEV_UNREGISTER:
inetdev_destroy(in_dev);
break;
- case NETDEV_CHANGENAME:
- /* Do not notify about label change, this event is
- * not interesting to applications using netlink.
- */
- inetdev_changename(dev, in_dev);
-
- devinet_sysctl_unregister(in_dev);
- devinet_sysctl_register(in_dev);
+ case NETDEV_CHANGENAME: {
+ int err;
+ /* Do not notify about label change, this event is
+ * not interesting to applications using netlink.
+ */
+ inetdev_changename(dev, in_dev);
+
+ snmp_unregister_dev(in_dev);
+ devinet_sysctl_unregister(in_dev);
+ devinet_sysctl_register(in_dev);
+ err = snmp_register_dev(in_dev);
+ if (err)
+ return notifier_from_errno(err);
+ }
break;
}
out: