aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDaniel Lezcano <dlezcano@fr.ibm.com>2008-02-20 03:21:47 -0500
committerDavid S. Miller <davem@davemloft.net>2008-02-20 03:21:47 -0500
commitc15853f2c1c9baaa27bbc494cd183be96f6d9bb9 (patch)
tree0f5735eb9c3fedc715f58d4aeeeed162eabb5d3a /drivers
parentfa907895b7b776208a1406efe5ba7ffe0f49f507 (diff)
veth: fix dev refcount race
When deleting the veth driver, veth_close calls netif_carrier_off for the two extremities of the network device. netif_carrier_off on the peer device will fire an event and hold a reference on the peer device. Just after, the peer is unregistered taking the rtnl_lock while the linkwatch_event is scheduled. If __linkwatch_run_queue does not occurs before the unregistering, unregister_netdevice will wait for the dev refcount to reach zero holding the rtnl_lock and linkwatch_event will wait for the rtnl_lock and hold the dev refcount. Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/veth.c53
1 files changed, 40 insertions, 13 deletions
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 3f67a29593bc..e2ad98bee6e7 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -244,18 +244,6 @@ static int veth_open(struct net_device *dev)
244 return 0; 244 return 0;
245} 245}
246 246
247static int veth_close(struct net_device *dev)
248{
249 struct veth_priv *priv;
250
251 if (netif_carrier_ok(dev)) {
252 priv = netdev_priv(dev);
253 netif_carrier_off(dev);
254 netif_carrier_off(priv->peer);
255 }
256 return 0;
257}
258
259static int veth_dev_init(struct net_device *dev) 247static int veth_dev_init(struct net_device *dev)
260{ 248{
261 struct veth_net_stats *stats; 249 struct veth_net_stats *stats;
@@ -286,13 +274,50 @@ static void veth_setup(struct net_device *dev)
286 dev->hard_start_xmit = veth_xmit; 274 dev->hard_start_xmit = veth_xmit;
287 dev->get_stats = veth_get_stats; 275 dev->get_stats = veth_get_stats;
288 dev->open = veth_open; 276 dev->open = veth_open;
289 dev->stop = veth_close;
290 dev->ethtool_ops = &veth_ethtool_ops; 277 dev->ethtool_ops = &veth_ethtool_ops;
291 dev->features |= NETIF_F_LLTX; 278 dev->features |= NETIF_F_LLTX;
292 dev->init = veth_dev_init; 279 dev->init = veth_dev_init;
293 dev->destructor = veth_dev_free; 280 dev->destructor = veth_dev_free;
294} 281}
295 282
283static void veth_change_state(struct net_device *dev)
284{
285 struct net_device *peer;
286 struct veth_priv *priv;
287
288 priv = netdev_priv(dev);
289 peer = priv->peer;
290
291 if (netif_carrier_ok(peer)) {
292 if (!netif_carrier_ok(dev))
293 netif_carrier_on(dev);
294 } else {
295 if (netif_carrier_ok(dev))
296 netif_carrier_off(dev);
297 }
298}
299
300static int veth_device_event(struct notifier_block *unused,
301 unsigned long event, void *ptr)
302{
303 struct net_device *dev = ptr;
304
305 if (dev->open != veth_open)
306 goto out;
307
308 switch (event) {
309 case NETDEV_CHANGE:
310 veth_change_state(dev);
311 break;
312 }
313out:
314 return NOTIFY_DONE;
315}
316
317static struct notifier_block veth_notifier_block __read_mostly = {
318 .notifier_call = veth_device_event,
319};
320
296/* 321/*
297 * netlink interface 322 * netlink interface
298 */ 323 */
@@ -454,12 +479,14 @@ static struct rtnl_link_ops veth_link_ops = {
454 479
455static __init int veth_init(void) 480static __init int veth_init(void)
456{ 481{
482 register_netdevice_notifier(&veth_notifier_block);
457 return rtnl_link_register(&veth_link_ops); 483 return rtnl_link_register(&veth_link_ops);
458} 484}
459 485
460static __exit void veth_exit(void) 486static __exit void veth_exit(void)
461{ 487{
462 rtnl_link_unregister(&veth_link_ops); 488 rtnl_link_unregister(&veth_link_ops);
489 unregister_netdevice_notifier(&veth_notifier_block);
463} 490}
464 491
465module_init(veth_init); 492module_init(veth_init);