Skip to content

Commit 1bdcec8

Browse files
vittyvkdavem330
authored andcommitted
hv_netvsc: use start_remove flag to protect netvsc_link_change()
netvsc_link_change() can race with netvsc_change_mtu() or netvsc_set_channels() as these functions destroy struct netvsc_device and rndis filter. Use start_remove flag for syncronization. As netvsc_change_mtu()/netvsc_set_channels() are called with rtnl lock held we need to take it before checking start_remove value in netvsc_link_change(). Reported-by: Haiyang Zhang <[email protected]> Signed-off-by: Vitaly Kuznetsov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f580aec commit 1bdcec8

File tree

1 file changed

+17
-4
lines changed

1 file changed

+17
-4
lines changed

drivers/net/hyperv/netvsc_drv.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,8 @@ static int netvsc_set_channels(struct net_device *net,
838838
out:
839839
netvsc_open(net);
840840
net_device_ctx->start_remove = false;
841+
/* We may have missed link change notifications */
842+
schedule_delayed_work(&net_device_ctx->dwork, 0);
841843

842844
return ret;
843845

@@ -946,6 +948,9 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
946948
netvsc_open(ndev);
947949
ndevctx->start_remove = false;
948950

951+
/* We may have missed link change notifications */
952+
schedule_delayed_work(&ndevctx->dwork, 0);
953+
949954
return ret;
950955
}
951956

@@ -1066,6 +1071,11 @@ static void netvsc_link_change(struct work_struct *w)
10661071
unsigned long flags, next_reconfig, delay;
10671072

10681073
ndev_ctx = container_of(w, struct net_device_context, dwork.work);
1074+
1075+
rtnl_lock();
1076+
if (ndev_ctx->start_remove)
1077+
goto out_unlock;
1078+
10691079
net_device = hv_get_drvdata(ndev_ctx->device_ctx);
10701080
rdev = net_device->extension;
10711081
net = net_device->ndev;
@@ -1079,7 +1089,7 @@ static void netvsc_link_change(struct work_struct *w)
10791089
delay = next_reconfig - jiffies;
10801090
delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT;
10811091
schedule_delayed_work(&ndev_ctx->dwork, delay);
1082-
return;
1092+
goto out_unlock;
10831093
}
10841094
ndev_ctx->last_reconfig = jiffies;
10851095

@@ -1093,9 +1103,7 @@ static void netvsc_link_change(struct work_struct *w)
10931103
spin_unlock_irqrestore(&ndev_ctx->lock, flags);
10941104

10951105
if (!event)
1096-
return;
1097-
1098-
rtnl_lock();
1106+
goto out_unlock;
10991107

11001108
switch (event->event) {
11011109
/* Only the following events are possible due to the check in
@@ -1144,6 +1152,11 @@ static void netvsc_link_change(struct work_struct *w)
11441152
*/
11451153
if (reschedule)
11461154
schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT);
1155+
1156+
return;
1157+
1158+
out_unlock:
1159+
rtnl_unlock();
11471160
}
11481161

11491162
static void netvsc_free_netdev(struct net_device *netdev)

0 commit comments

Comments
 (0)