|
@@ -5192,9 +5192,6 @@ int init_dummy_netdev(struct net_device *dev)
|
|
|
*/
|
|
|
dev->reg_state = NETREG_DUMMY;
|
|
|
|
|
|
- /* initialize the ref count */
|
|
|
- atomic_set(&dev->refcnt, 1);
|
|
|
-
|
|
|
/* NAPI wants this */
|
|
|
INIT_LIST_HEAD(&dev->napi_list);
|
|
|
|
|
@@ -5202,6 +5199,11 @@ int init_dummy_netdev(struct net_device *dev)
|
|
|
set_bit(__LINK_STATE_PRESENT, &dev->state);
|
|
|
set_bit(__LINK_STATE_START, &dev->state);
|
|
|
|
|
|
+ /* Note : We dont allocate pcpu_refcnt for dummy devices,
|
|
|
+ * because users of this 'device' dont need to change
|
|
|
+ * its refcount.
|
|
|
+ */
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(init_dummy_netdev);
|
|
@@ -5243,6 +5245,16 @@ out:
|
|
|
}
|
|
|
EXPORT_SYMBOL(register_netdev);
|
|
|
|
|
|
+int netdev_refcnt_read(const struct net_device *dev)
|
|
|
+{
|
|
|
+ int i, refcnt = 0;
|
|
|
+
|
|
|
+ for_each_possible_cpu(i)
|
|
|
+ refcnt += *per_cpu_ptr(dev->pcpu_refcnt, i);
|
|
|
+ return refcnt;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(netdev_refcnt_read);
|
|
|
+
|
|
|
/*
|
|
|
* netdev_wait_allrefs - wait until all references are gone.
|
|
|
*
|
|
@@ -5257,11 +5269,14 @@ EXPORT_SYMBOL(register_netdev);
|
|
|
static void netdev_wait_allrefs(struct net_device *dev)
|
|
|
{
|
|
|
unsigned long rebroadcast_time, warning_time;
|
|
|
+ int refcnt;
|
|
|
|
|
|
linkwatch_forget_dev(dev);
|
|
|
|
|
|
rebroadcast_time = warning_time = jiffies;
|
|
|
- while (atomic_read(&dev->refcnt) != 0) {
|
|
|
+ refcnt = netdev_refcnt_read(dev);
|
|
|
+
|
|
|
+ while (refcnt != 0) {
|
|
|
if (time_after(jiffies, rebroadcast_time + 1 * HZ)) {
|
|
|
rtnl_lock();
|
|
|
|
|
@@ -5288,11 +5303,13 @@ static void netdev_wait_allrefs(struct net_device *dev)
|
|
|
|
|
|
msleep(250);
|
|
|
|
|
|
+ refcnt = netdev_refcnt_read(dev);
|
|
|
+
|
|
|
if (time_after(jiffies, warning_time + 10 * HZ)) {
|
|
|
printk(KERN_EMERG "unregister_netdevice: "
|
|
|
"waiting for %s to become free. Usage "
|
|
|
"count = %d\n",
|
|
|
- dev->name, atomic_read(&dev->refcnt));
|
|
|
+ dev->name, refcnt);
|
|
|
warning_time = jiffies;
|
|
|
}
|
|
|
}
|
|
@@ -5350,7 +5367,7 @@ void netdev_run_todo(void)
|
|
|
netdev_wait_allrefs(dev);
|
|
|
|
|
|
/* paranoia */
|
|
|
- BUG_ON(atomic_read(&dev->refcnt));
|
|
|
+ BUG_ON(netdev_refcnt_read(dev));
|
|
|
WARN_ON(rcu_dereference_raw(dev->ip_ptr));
|
|
|
WARN_ON(dev->ip6_ptr);
|
|
|
WARN_ON(dev->dn_ptr);
|
|
@@ -5520,9 +5537,13 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
|
|
|
dev = PTR_ALIGN(p, NETDEV_ALIGN);
|
|
|
dev->padded = (char *)dev - (char *)p;
|
|
|
|
|
|
- if (dev_addr_init(dev))
|
|
|
+ dev->pcpu_refcnt = alloc_percpu(int);
|
|
|
+ if (!dev->pcpu_refcnt)
|
|
|
goto free_tx;
|
|
|
|
|
|
+ if (dev_addr_init(dev))
|
|
|
+ goto free_pcpu;
|
|
|
+
|
|
|
dev_mc_init(dev);
|
|
|
dev_uc_init(dev);
|
|
|
|
|
@@ -5553,6 +5574,8 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
|
|
|
|
|
|
free_tx:
|
|
|
kfree(tx);
|
|
|
+free_pcpu:
|
|
|
+ free_percpu(dev->pcpu_refcnt);
|
|
|
free_p:
|
|
|
kfree(p);
|
|
|
return NULL;
|
|
@@ -5586,6 +5609,9 @@ void free_netdev(struct net_device *dev)
|
|
|
list_for_each_entry_safe(p, n, &dev->napi_list, dev_list)
|
|
|
netif_napi_del(p);
|
|
|
|
|
|
+ free_percpu(dev->pcpu_refcnt);
|
|
|
+ dev->pcpu_refcnt = NULL;
|
|
|
+
|
|
|
/* Compatibility with error handling in drivers */
|
|
|
if (dev->reg_state == NETREG_UNINITIALIZED) {
|
|
|
kfree((char *)dev - dev->padded);
|