Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c15853f2 authored by Daniel Lezcano's avatar Daniel Lezcano Committed by David S. Miller
Browse files

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: default avatarDaniel Lezcano <dlezcano@fr.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fa907895
Loading
Loading
Loading
Loading
+40 −13
Original line number Diff line number Diff line
@@ -244,18 +244,6 @@ static int veth_open(struct net_device *dev)
	return 0;
}

static int veth_close(struct net_device *dev)
{
	struct veth_priv *priv;

	if (netif_carrier_ok(dev)) {
		priv = netdev_priv(dev);
		netif_carrier_off(dev);
		netif_carrier_off(priv->peer);
	}
	return 0;
}

static int veth_dev_init(struct net_device *dev)
{
	struct veth_net_stats *stats;
@@ -286,13 +274,50 @@ static void veth_setup(struct net_device *dev)
	dev->hard_start_xmit = veth_xmit;
	dev->get_stats = veth_get_stats;
	dev->open = veth_open;
	dev->stop = veth_close;
	dev->ethtool_ops = &veth_ethtool_ops;
	dev->features |= NETIF_F_LLTX;
	dev->init = veth_dev_init;
	dev->destructor = veth_dev_free;
}

static void veth_change_state(struct net_device *dev)
{
	struct net_device *peer;
	struct veth_priv *priv;

	priv = netdev_priv(dev);
	peer = priv->peer;

	if (netif_carrier_ok(peer)) {
		if (!netif_carrier_ok(dev))
			netif_carrier_on(dev);
	} else {
		if (netif_carrier_ok(dev))
			netif_carrier_off(dev);
	}
}

static int veth_device_event(struct notifier_block *unused,
			     unsigned long event, void *ptr)
{
	struct net_device *dev = ptr;

	if (dev->open != veth_open)
		goto out;

	switch (event) {
	case NETDEV_CHANGE:
		veth_change_state(dev);
		break;
	}
out:
	return NOTIFY_DONE;
}

static struct notifier_block veth_notifier_block __read_mostly = {
	.notifier_call	= veth_device_event,
};

/*
 * netlink interface
 */
@@ -454,12 +479,14 @@ static struct rtnl_link_ops veth_link_ops = {

static __init int veth_init(void)
{
	register_netdevice_notifier(&veth_notifier_block);
	return rtnl_link_register(&veth_link_ops);
}

static __exit void veth_exit(void)
{
	rtnl_link_unregister(&veth_link_ops);
	unregister_netdevice_notifier(&veth_notifier_block);
}

module_init(veth_init);