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) | |||
88 | { | 88 | { |
89 | struct net_device_context *net_device_ctx = netdev_priv(net); | 89 | struct net_device_context *net_device_ctx = netdev_priv(net); |
90 | struct hv_device *device_obj = net_device_ctx->device_ctx; | 90 | struct hv_device *device_obj = net_device_ctx->device_ctx; |
91 | struct netvsc_device *nvdev; | ||
92 | struct rndis_device *rdev; | ||
91 | int ret = 0; | 93 | int ret = 0; |
92 | 94 | ||
95 | netif_carrier_off(net); | ||
96 | |||
93 | /* Open up the device */ | 97 | /* Open up the device */ |
94 | ret = rndis_filter_open(device_obj); | 98 | ret = rndis_filter_open(device_obj); |
95 | if (ret != 0) { | 99 | if (ret != 0) { |
@@ -99,6 +103,11 @@ static int netvsc_open(struct net_device *net) | |||
99 | 103 | ||
100 | netif_start_queue(net); | 104 | netif_start_queue(net); |
101 | 105 | ||
106 | nvdev = hv_get_drvdata(device_obj); | ||
107 | rdev = nvdev->extension; | ||
108 | if (!rdev->link_state) | ||
109 | netif_carrier_on(net); | ||
110 | |||
102 | return ret; | 111 | return ret; |
103 | } | 112 | } |
104 | 113 | ||
@@ -229,23 +238,24 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, | |||
229 | struct net_device *net; | 238 | struct net_device *net; |
230 | struct net_device_context *ndev_ctx; | 239 | struct net_device_context *ndev_ctx; |
231 | struct netvsc_device *net_device; | 240 | struct netvsc_device *net_device; |
241 | struct rndis_device *rdev; | ||
232 | 242 | ||
233 | net_device = hv_get_drvdata(device_obj); | 243 | net_device = hv_get_drvdata(device_obj); |
244 | rdev = net_device->extension; | ||
245 | |||
246 | rdev->link_state = status != 1; | ||
247 | |||
234 | net = net_device->ndev; | 248 | net = net_device->ndev; |
235 | 249 | ||
236 | if (!net) { | 250 | if (!net || net->reg_state != NETREG_REGISTERED) |
237 | netdev_err(net, "got link status but net device " | ||
238 | "not initialized yet\n"); | ||
239 | return; | 251 | return; |
240 | } | ||
241 | 252 | ||
253 | ndev_ctx = netdev_priv(net); | ||
242 | if (status == 1) { | 254 | if (status == 1) { |
243 | netif_carrier_on(net); | ||
244 | ndev_ctx = netdev_priv(net); | ||
245 | schedule_delayed_work(&ndev_ctx->dwork, 0); | 255 | schedule_delayed_work(&ndev_ctx->dwork, 0); |
246 | schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); | 256 | schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); |
247 | } else { | 257 | } else { |
248 | netif_carrier_off(net); | 258 | schedule_delayed_work(&ndev_ctx->dwork, 0); |
249 | } | 259 | } |
250 | } | 260 | } |
251 | 261 | ||
@@ -388,17 +398,35 @@ static const struct net_device_ops device_ops = { | |||
388 | * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add | 398 | * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add |
389 | * another netif_notify_peers() into a delayed work, otherwise GARP packet | 399 | * another netif_notify_peers() into a delayed work, otherwise GARP packet |
390 | * will not be sent after quick migration, and cause network disconnection. | 400 | * will not be sent after quick migration, and cause network disconnection. |
401 | * Also, we update the carrier status here. | ||
391 | */ | 402 | */ |
392 | static void netvsc_send_garp(struct work_struct *w) | 403 | static void netvsc_link_change(struct work_struct *w) |
393 | { | 404 | { |
394 | struct net_device_context *ndev_ctx; | 405 | struct net_device_context *ndev_ctx; |
395 | struct net_device *net; | 406 | struct net_device *net; |
396 | struct netvsc_device *net_device; | 407 | struct netvsc_device *net_device; |
408 | struct rndis_device *rdev; | ||
409 | bool notify; | ||
410 | |||
411 | rtnl_lock(); | ||
397 | 412 | ||
398 | ndev_ctx = container_of(w, struct net_device_context, dwork.work); | 413 | ndev_ctx = container_of(w, struct net_device_context, dwork.work); |
399 | net_device = hv_get_drvdata(ndev_ctx->device_ctx); | 414 | net_device = hv_get_drvdata(ndev_ctx->device_ctx); |
415 | rdev = net_device->extension; | ||
400 | net = net_device->ndev; | 416 | net = net_device->ndev; |
401 | netdev_notify_peers(net); | 417 | |
418 | if (rdev->link_state) { | ||
419 | netif_carrier_off(net); | ||
420 | notify = false; | ||
421 | } else { | ||
422 | netif_carrier_on(net); | ||
423 | notify = true; | ||
424 | } | ||
425 | |||
426 | rtnl_unlock(); | ||
427 | |||
428 | if (notify) | ||
429 | netdev_notify_peers(net); | ||
402 | } | 430 | } |
403 | 431 | ||
404 | 432 | ||
@@ -414,13 +442,12 @@ static int netvsc_probe(struct hv_device *dev, | |||
414 | if (!net) | 442 | if (!net) |
415 | return -ENOMEM; | 443 | return -ENOMEM; |
416 | 444 | ||
417 | /* Set initial state */ | ||
418 | netif_carrier_off(net); | 445 | netif_carrier_off(net); |
419 | 446 | ||
420 | net_device_ctx = netdev_priv(net); | 447 | net_device_ctx = netdev_priv(net); |
421 | net_device_ctx->device_ctx = dev; | 448 | net_device_ctx->device_ctx = dev; |
422 | hv_set_drvdata(dev, net); | 449 | hv_set_drvdata(dev, net); |
423 | INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); | 450 | INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change); |
424 | INIT_WORK(&net_device_ctx->work, do_set_multicast); | 451 | INIT_WORK(&net_device_ctx->work, do_set_multicast); |
425 | 452 | ||
426 | net->netdev_ops = &device_ops; | 453 | net->netdev_ops = &device_ops; |
@@ -443,13 +470,13 @@ static int netvsc_probe(struct hv_device *dev, | |||
443 | } | 470 | } |
444 | memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); | 471 | memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); |
445 | 472 | ||
446 | netif_carrier_on(net); | ||
447 | |||
448 | ret = register_netdev(net); | 473 | ret = register_netdev(net); |
449 | if (ret != 0) { | 474 | if (ret != 0) { |
450 | pr_err("Unable to register netdev.\n"); | 475 | pr_err("Unable to register netdev.\n"); |
451 | rndis_filter_device_remove(dev); | 476 | rndis_filter_device_remove(dev); |
452 | free_netdev(net); | 477 | free_netdev(net); |
478 | } else { | ||
479 | schedule_delayed_work(&net_device_ctx->dwork, 0); | ||
453 | } | 480 | } |
454 | 481 | ||
455 | return ret; | 482 | 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, | |||
243 | return ret; | 243 | return ret; |
244 | } | 244 | } |
245 | 245 | ||
246 | static void rndis_set_link_state(struct rndis_device *rdev, | ||
247 | struct rndis_request *request) | ||
248 | { | ||
249 | u32 link_status; | ||
250 | struct rndis_query_complete *query_complete; | ||
251 | |||
252 | query_complete = &request->response_msg.msg.query_complete; | ||
253 | |||
254 | if (query_complete->status == RNDIS_STATUS_SUCCESS && | ||
255 | query_complete->info_buflen == sizeof(u32)) { | ||
256 | memcpy(&link_status, (void *)((unsigned long)query_complete + | ||
257 | query_complete->info_buf_offset), sizeof(u32)); | ||
258 | rdev->link_state = link_status != 0; | ||
259 | } | ||
260 | } | ||
261 | |||
246 | static void rndis_filter_receive_response(struct rndis_device *dev, | 262 | static void rndis_filter_receive_response(struct rndis_device *dev, |
247 | struct rndis_message *resp) | 263 | struct rndis_message *resp) |
248 | { | 264 | { |
@@ -272,6 +288,10 @@ static void rndis_filter_receive_response(struct rndis_device *dev, | |||
272 | sizeof(struct rndis_message) + RNDIS_EXT_LEN) { | 288 | sizeof(struct rndis_message) + RNDIS_EXT_LEN) { |
273 | memcpy(&request->response_msg, resp, | 289 | memcpy(&request->response_msg, resp, |
274 | resp->msg_len); | 290 | resp->msg_len); |
291 | if (request->request_msg.ndis_msg_type == | ||
292 | RNDIS_MSG_QUERY && request->request_msg.msg. | ||
293 | query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS) | ||
294 | rndis_set_link_state(dev, request); | ||
275 | } else { | 295 | } else { |
276 | netdev_err(ndev, | 296 | netdev_err(ndev, |
277 | "rndis response buffer overflow " | 297 | "rndis response buffer overflow " |
@@ -620,7 +640,6 @@ static int rndis_filter_query_device_link_status(struct rndis_device *dev) | |||
620 | ret = rndis_filter_query_device(dev, | 640 | ret = rndis_filter_query_device(dev, |
621 | RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, | 641 | RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, |
622 | &link_status, &size); | 642 | &link_status, &size); |
623 | dev->link_state = (link_status != 0) ? true : false; | ||
624 | 643 | ||
625 | return ret; | 644 | return ret; |
626 | } | 645 | } |