hv_netvsc: fix rtnl locking in callback
[cascardo/linux.git] / drivers / net / hyperv / netvsc_drv.c
index 62a4e6e..6924d01 100644 (file)
@@ -579,19 +579,32 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
        struct netvsc_reconfig *event;
        unsigned long flags;
 
-       /* Handle link change statuses only */
+       net = hv_get_drvdata(device_obj);
+
+       if (!net)
+               return;
+
+       ndev_ctx = netdev_priv(net);
+
+       /* Update the physical link speed when changing to another vSwitch */
+       if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) {
+               u32 speed;
+
+               speed = *(u32 *)((void *)indicate + indicate->
+                                status_buf_offset) / 10000;
+               ndev_ctx->speed = speed;
+               return;
+       }
+
+       /* Handle these link change statuses below */
        if (indicate->status != RNDIS_STATUS_NETWORK_CHANGE &&
            indicate->status != RNDIS_STATUS_MEDIA_CONNECT &&
            indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT)
                return;
 
-       net = hv_get_drvdata(device_obj);
-
-       if (!net || net->reg_state != NETREG_REGISTERED)
+       if (net->reg_state != NETREG_REGISTERED)
                return;
 
-       ndev_ctx = netdev_priv(net);
-
        event = kzalloc(sizeof(*event), GFP_ATOMIC);
        if (!event)
                return;
@@ -1154,9 +1167,8 @@ static void netvsc_free_netdev(struct net_device *netdev)
 static struct net_device *get_netvsc_net_device(char *mac)
 {
        struct net_device *dev, *found = NULL;
-       int rtnl_locked;
 
-       rtnl_locked = rtnl_trylock();
+       ASSERT_RTNL();
 
        for_each_netdev(&init_net, dev) {
                if (memcmp(dev->dev_addr, mac, ETH_ALEN) == 0) {
@@ -1166,8 +1178,6 @@ static struct net_device *get_netvsc_net_device(char *mac)
                        break;
                }
        }
-       if (rtnl_locked)
-               rtnl_unlock();
 
        return found;
 }
@@ -1337,6 +1347,8 @@ static int netvsc_probe(struct hv_device *dev,
 
        netif_carrier_off(net);
 
+       netvsc_init_settings(net);
+
        net_device_ctx = netdev_priv(net);
        net_device_ctx->device_ctx = dev;
        net_device_ctx->msg_enable = netif_msg_init(debug, default_msg);
@@ -1398,8 +1410,6 @@ static int netvsc_probe(struct hv_device *dev,
        netif_set_real_num_tx_queues(net, nvdev->num_chn);
        netif_set_real_num_rx_queues(net, nvdev->num_chn);
 
-       netvsc_init_settings(net);
-
        ret = register_netdev(net);
        if (ret != 0) {
                pr_err("Unable to register netdev.\n");
@@ -1482,8 +1492,13 @@ static int netvsc_netdev_event(struct notifier_block *this,
 {
        struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
 
-       /* Avoid Vlan, Bonding dev with same MAC registering as VF */
-       if (event_dev->priv_flags & (IFF_802_1Q_VLAN | IFF_BONDING))
+       /* Avoid Vlan dev with same MAC registering as VF */
+       if (event_dev->priv_flags & IFF_802_1Q_VLAN)
+               return NOTIFY_DONE;
+
+       /* Avoid Bonding master dev with same MAC registering as VF */
+       if (event_dev->priv_flags & IFF_BONDING &&
+           event_dev->flags & IFF_MASTER)
                return NOTIFY_DONE;
 
        switch (event) {