hyperv: Add handler for RNDIS_STATUS_NETWORK_CHANGE event
authorHaiyang Zhang <haiyangz@microsoft.com>
Fri, 20 Jun 2014 01:34:36 +0000 (18:34 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 20 Jun 2014 04:17:37 +0000 (21:17 -0700)
The RNDIS_STATUS_NETWORK_CHANGE event is received after the Hyper-V host
sleep or hibernation. We refresh network at this time.
MS-TFS: 135162

Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc_drv.c
drivers/net/hyperv/rndis_filter.c
include/linux/rndis.h

index 6cc37c15e0bf98341131a8229bd7fbd2567994a8..24441ae832d108dc304372d03ec99c0e6281e4d8 100644 (file)
@@ -170,6 +170,7 @@ struct rndis_device {
 
        enum rndis_device_state state;
        bool link_state;
+       bool link_change;
        atomic_t new_req_id;
 
        spinlock_t request_lock;
@@ -185,7 +186,7 @@ int netvsc_device_remove(struct hv_device *device);
 int netvsc_send(struct hv_device *device,
                struct hv_netvsc_packet *packet);
 void netvsc_linkstatus_callback(struct hv_device *device_obj,
-                               unsigned int status);
+                               struct rndis_message *resp);
 int netvsc_recv_callback(struct hv_device *device_obj,
                        struct hv_netvsc_packet *packet,
                        struct ndis_tcp_ip_checksum_info *csum_info);
index 4fd71b75e666418ab7063447160d561a294a0966..9b27ca8c1d397004b4d825cebc634a043ca26687 100644 (file)
@@ -579,8 +579,9 @@ drop:
  * netvsc_linkstatus_callback - Link up/down notification
  */
 void netvsc_linkstatus_callback(struct hv_device *device_obj,
-                                      unsigned int status)
+                               struct rndis_message *resp)
 {
+       struct rndis_indicate_status *indicate = &resp->msg.indicate_status;
        struct net_device *net;
        struct net_device_context *ndev_ctx;
        struct netvsc_device *net_device;
@@ -589,7 +590,19 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
        net_device = hv_get_drvdata(device_obj);
        rdev = net_device->extension;
 
-       rdev->link_state = status != 1;
+       switch (indicate->status) {
+       case RNDIS_STATUS_MEDIA_CONNECT:
+               rdev->link_state = false;
+               break;
+       case RNDIS_STATUS_MEDIA_DISCONNECT:
+               rdev->link_state = true;
+               break;
+       case RNDIS_STATUS_NETWORK_CHANGE:
+               rdev->link_change = true;
+               break;
+       default:
+               return;
+       }
 
        net = net_device->ndev;
 
@@ -597,7 +610,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
                return;
 
        ndev_ctx = netdev_priv(net);
-       if (status == 1) {
+       if (!rdev->link_state) {
                schedule_delayed_work(&ndev_ctx->dwork, 0);
                schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20));
        } else {
@@ -767,7 +780,9 @@ static void netvsc_link_change(struct work_struct *w)
        struct net_device *net;
        struct netvsc_device *net_device;
        struct rndis_device *rdev;
-       bool notify;
+       bool notify, refresh = false;
+       char *argv[] = { "/etc/init.d/network", "restart", NULL };
+       char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
 
        rtnl_lock();
 
@@ -782,10 +797,17 @@ static void netvsc_link_change(struct work_struct *w)
        } else {
                netif_carrier_on(net);
                notify = true;
+               if (rdev->link_change) {
+                       rdev->link_change = false;
+                       refresh = true;
+               }
        }
 
        rtnl_unlock();
 
+       if (refresh)
+               call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
+
        if (notify)
                netdev_notify_peers(net);
 }
index 99c527adae5bf1ee154b2a02eb0f93a6df32e333..2b86f0b6f6d18adf8b13bdba263a426a4d89aaa6 100644 (file)
@@ -320,25 +320,6 @@ static void rndis_filter_receive_response(struct rndis_device *dev,
        }
 }
 
-static void rndis_filter_receive_indicate_status(struct rndis_device *dev,
-                                            struct rndis_message *resp)
-{
-       struct rndis_indicate_status *indicate =
-                       &resp->msg.indicate_status;
-
-       if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) {
-               netvsc_linkstatus_callback(
-                       dev->net_dev->dev, 1);
-       } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) {
-               netvsc_linkstatus_callback(
-                       dev->net_dev->dev, 0);
-       } else {
-               /*
-                * TODO:
-                */
-       }
-}
-
 /*
  * Get the Per-Packet-Info with the specified type
  * return NULL if not found.
@@ -464,7 +445,7 @@ int rndis_filter_receive(struct hv_device *dev,
 
        case RNDIS_MSG_INDICATE:
                /* notification msgs */
-               rndis_filter_receive_indicate_status(rndis_dev, rndis_msg);
+               netvsc_linkstatus_callback(dev, rndis_msg);
                break;
        default:
                netdev_err(ndev,
index 0c8dc7195cdb290debd000a0b4837ac2baeab85c..93c0a64aefa6c4f2ebe8431904c1b5c74b5ffcff 100644 (file)
@@ -65,6 +65,7 @@
 #define        RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION  0x40010012
 #define RNDIS_STATUS_WW_INDICATION             RDIA_SPECIFIC_INDICATION
 #define RNDIS_STATUS_LINK_SPEED_CHANGE         0x40010013L
+#define RNDIS_STATUS_NETWORK_CHANGE            0x40010018
 
 #define RNDIS_STATUS_NOT_RESETTABLE            0x80010001
 #define RNDIS_STATUS_SOFT_ERRORS               0x80010003