diff options
Diffstat (limited to 'drivers/net/hyperv')
| -rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 53 | ||||
| -rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 21 | 
2 files changed, 60 insertions, 14 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; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 1084e5de3ceb..b54fd257652b 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -243,6 +243,22 @@ static int rndis_filter_send_request(struct rndis_device *dev,  	return ret;  } +static void rndis_set_link_state(struct rndis_device *rdev, +				 struct rndis_request *request) +{ +	u32 link_status; +	struct rndis_query_complete *query_complete; + +	query_complete = &request->response_msg.msg.query_complete; + +	if (query_complete->status == RNDIS_STATUS_SUCCESS && +	    query_complete->info_buflen == sizeof(u32)) { +		memcpy(&link_status, (void *)((unsigned long)query_complete + +		       query_complete->info_buf_offset), sizeof(u32)); +		rdev->link_state = link_status != 0; +	} +} +  static void rndis_filter_receive_response(struct rndis_device *dev,  				       struct rndis_message *resp)  { @@ -272,6 +288,10 @@ static void rndis_filter_receive_response(struct rndis_device *dev,  		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {  			memcpy(&request->response_msg, resp,  			       resp->msg_len); +			if (request->request_msg.ndis_msg_type == +			    RNDIS_MSG_QUERY && request->request_msg.msg. +			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS) +				rndis_set_link_state(dev, request);  		} else {  			netdev_err(ndev,  				"rndis response buffer overflow " @@ -620,7 +640,6 @@ static int rndis_filter_query_device_link_status(struct rndis_device *dev)  	ret = rndis_filter_query_device(dev,  				      RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,  				      &link_status, &size); -	dev->link_state = (link_status != 0) ? true : false;  	return ret;  }  | 
