diff options
Diffstat (limited to 'drivers/net/hyperv/netvsc_drv.c')
| -rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 53 | 
1 files changed, 40 insertions, 13 deletions
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 7756118c2f0a..d6fce9750b95 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -88,8 +88,12 @@ static int netvsc_open(struct net_device *net)  {  	struct net_device_context *net_device_ctx = netdev_priv(net);  	struct hv_device *device_obj = net_device_ctx->device_ctx; +	struct netvsc_device *nvdev; +	struct rndis_device *rdev;  	int ret = 0; +	netif_carrier_off(net); +  	/* Open up the device */  	ret = rndis_filter_open(device_obj);  	if (ret != 0) { @@ -99,6 +103,11 @@ static int netvsc_open(struct net_device *net)  	netif_start_queue(net); +	nvdev = hv_get_drvdata(device_obj); +	rdev = nvdev->extension; +	if (!rdev->link_state) +		netif_carrier_on(net); +  	return ret;  } @@ -229,23 +238,24 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,  	struct net_device *net;  	struct net_device_context *ndev_ctx;  	struct netvsc_device *net_device; +	struct rndis_device *rdev;  	net_device = hv_get_drvdata(device_obj); +	rdev = net_device->extension; + +	rdev->link_state = status != 1; +  	net = net_device->ndev; -	if (!net) { -		netdev_err(net, "got link status but net device " -				"not initialized yet\n"); +	if (!net || net->reg_state != NETREG_REGISTERED)  		return; -	} +	ndev_ctx = netdev_priv(net);  	if (status == 1) { -		netif_carrier_on(net); -		ndev_ctx = netdev_priv(net);  		schedule_delayed_work(&ndev_ctx->dwork, 0);  		schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20));  	} else { -		netif_carrier_off(net); +		schedule_delayed_work(&ndev_ctx->dwork, 0);  	}  } @@ -388,17 +398,35 @@ static const struct net_device_ops device_ops = {   * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add   * another netif_notify_peers() into a delayed work, otherwise GARP packet   * will not be sent after quick migration, and cause network disconnection. + * Also, we update the carrier status here.   */ -static void netvsc_send_garp(struct work_struct *w) +static void netvsc_link_change(struct work_struct *w)  {  	struct net_device_context *ndev_ctx;  	struct net_device *net;  	struct netvsc_device *net_device; +	struct rndis_device *rdev; +	bool notify; + +	rtnl_lock();  	ndev_ctx = container_of(w, struct net_device_context, dwork.work);  	net_device = hv_get_drvdata(ndev_ctx->device_ctx); +	rdev = net_device->extension;  	net = net_device->ndev; -	netdev_notify_peers(net); + +	if (rdev->link_state) { +		netif_carrier_off(net); +		notify = false; +	} else { +		netif_carrier_on(net); +		notify = true; +	} + +	rtnl_unlock(); + +	if (notify) +		netdev_notify_peers(net);  } @@ -414,13 +442,12 @@ static int netvsc_probe(struct hv_device *dev,  	if (!net)  		return -ENOMEM; -	/* Set initial state */  	netif_carrier_off(net);  	net_device_ctx = netdev_priv(net);  	net_device_ctx->device_ctx = dev;  	hv_set_drvdata(dev, net); -	INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); +	INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change);  	INIT_WORK(&net_device_ctx->work, do_set_multicast);  	net->netdev_ops = &device_ops; @@ -443,13 +470,13 @@ static int netvsc_probe(struct hv_device *dev,  	}  	memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); -	netif_carrier_on(net); -  	ret = register_netdev(net);  	if (ret != 0) {  		pr_err("Unable to register netdev.\n");  		rndis_filter_device_remove(dev);  		free_netdev(net); +	} else { +		schedule_delayed_work(&net_device_ctx->dwork, 0);  	}  	return ret;  | 
