Commit 322de39f authored by Stephen Hemminger's avatar Stephen Hemminger

[NET]: free_netdev - free network device on last class_device_usage

This patch adds the free_netdev function and associated
changes so that net_device structures are not freed until
last reference to the network device class is released.
parent ae41b279
...@@ -384,6 +384,7 @@ struct net_device ...@@ -384,6 +384,7 @@ struct net_device
NETREG_REGISTERED, /* completed register todo */ NETREG_REGISTERED, /* completed register todo */
NETREG_UNREGISTERING, /* called unregister_netdevice */ NETREG_UNREGISTERING, /* called unregister_netdevice */
NETREG_UNREGISTERED, /* completed unregister todo */ NETREG_UNREGISTERED, /* completed unregister todo */
NETREG_RELEASED, /* called free_netdev */
} reg_state; } reg_state;
/* Net device features */ /* Net device features */
...@@ -516,6 +517,7 @@ extern int dev_close(struct net_device *dev); ...@@ -516,6 +517,7 @@ extern int dev_close(struct net_device *dev);
extern int dev_queue_xmit(struct sk_buff *skb); extern int dev_queue_xmit(struct sk_buff *skb);
extern int register_netdevice(struct net_device *dev); extern int register_netdevice(struct net_device *dev);
extern int unregister_netdevice(struct net_device *dev); extern int unregister_netdevice(struct net_device *dev);
extern void free_netdev(struct net_device *dev);
extern void synchronize_net(void); extern void synchronize_net(void);
extern int register_netdevice_notifier(struct notifier_block *nb); extern int register_netdevice_notifier(struct notifier_block *nb);
extern int unregister_netdevice_notifier(struct notifier_block *nb); extern int unregister_netdevice_notifier(struct notifier_block *nb);
......
...@@ -2643,7 +2643,7 @@ int register_netdevice(struct net_device *dev) ...@@ -2643,7 +2643,7 @@ int register_netdevice(struct net_device *dev)
ASSERT_RTNL(); ASSERT_RTNL();
/* When net_device's are persistent, this will be fatal. */ /* When net_device's are persistent, this will be fatal. */
WARN_ON(dev->reg_state != NETREG_UNINITIALIZED); BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
spin_lock_init(&dev->queue_lock); spin_lock_init(&dev->queue_lock);
spin_lock_init(&dev->xmit_lock); spin_lock_init(&dev->xmit_lock);
...@@ -2788,6 +2788,8 @@ static void netdev_wait_allrefs(struct net_device *dev) ...@@ -2788,6 +2788,8 @@ static void netdev_wait_allrefs(struct net_device *dev)
* unregister_netdevice(y2); * unregister_netdevice(y2);
* ... * ...
* rtnl_unlock(); * rtnl_unlock();
* free_netdev(y1);
* free_netdev(y2);
* *
* We are invoked by rtnl_unlock() after it drops the semaphore. * We are invoked by rtnl_unlock() after it drops the semaphore.
* This allows us to deal with problems: * This allows us to deal with problems:
...@@ -2827,7 +2829,7 @@ void netdev_run_todo(void) ...@@ -2827,7 +2829,7 @@ void netdev_run_todo(void)
break; break;
case NETREG_UNREGISTERING: case NETREG_UNREGISTERING:
class_device_unregister(&dev->class_dev); class_device_del(&dev->class_dev);
dev->reg_state = NETREG_UNREGISTERED; dev->reg_state = NETREG_UNREGISTERED;
netdev_wait_allrefs(dev); netdev_wait_allrefs(dev);
...@@ -2856,6 +2858,29 @@ void netdev_run_todo(void) ...@@ -2856,6 +2858,29 @@ void netdev_run_todo(void)
up(&net_todo_run_mutex); up(&net_todo_run_mutex);
} }
/**
* free_netdev - free network device
* @dev: device
*
* This function does the last stage of destroying an allocated device
* interface. The reference to the device object is released.
* If this is the last reference then it will be freed.
*/
void free_netdev(struct net_device *dev)
{
/* Compatiablity with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED) {
kfree(dev);
return;
}
BUG_ON(dev->reg_state != NETREG_UNREGISTERED);
dev->reg_state = NETREG_RELEASED;
/* will free via class release */
class_device_put(&dev->class_dev);
}
/* Synchronize with packet receive processing. */ /* Synchronize with packet receive processing. */
void synchronize_net(void) void synchronize_net(void)
{ {
......
...@@ -361,8 +361,23 @@ static int netdev_hotplug(struct class_device *cd, char **envp, ...@@ -361,8 +361,23 @@ static int netdev_hotplug(struct class_device *cd, char **envp,
} }
#endif #endif
/*
* netdev_release -- destroy and free a dead device.
* Called when last reference to class_device kobject is gone.
*/
static void netdev_release(struct class_device *cd)
{
struct net_device *dev
= container_of(cd, struct net_device, class_dev);
BUG_ON(dev->reg_state != NETREG_RELEASED);
kfree(dev);
}
static struct class net_class = { static struct class net_class = {
.name = "net", .name = "net",
.release = netdev_release,
#ifdef CONFIG_HOTPLUG #ifdef CONFIG_HOTPLUG
.hotplug = netdev_hotplug, .hotplug = netdev_hotplug,
#endif #endif
......
...@@ -480,6 +480,7 @@ EXPORT_SYMBOL(call_netdevice_notifiers); ...@@ -480,6 +480,7 @@ EXPORT_SYMBOL(call_netdevice_notifiers);
EXPORT_SYMBOL(loopback_dev); EXPORT_SYMBOL(loopback_dev);
EXPORT_SYMBOL(register_netdevice); EXPORT_SYMBOL(register_netdevice);
EXPORT_SYMBOL(unregister_netdevice); EXPORT_SYMBOL(unregister_netdevice);
EXPORT_SYMBOL(free_netdev);
EXPORT_SYMBOL(synchronize_net); EXPORT_SYMBOL(synchronize_net);
EXPORT_SYMBOL(netdev_state_change); EXPORT_SYMBOL(netdev_state_change);
EXPORT_SYMBOL(netdev_boot_setup_check); EXPORT_SYMBOL(netdev_boot_setup_check);
......
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