Skip to content

Commit 8195b13

Browse files
shemmingerdavem330
authored andcommitted
hv_netvsc: fix deadlock on hotplug
When a virtual device is added dynamically (via host console), then the vmbus sends an offer message for the primary channel. The processing of this message for networking causes the network device to then initialize the sub channels. The problem is that setting up the sub channels needs to wait until the subsequent subchannel offers have been processed. These offers come in on the same ring buffer and work queue as where the primary offer is being processed; leading to a deadlock. This did not happen in older kernels, because the sub channel waiting logic was broken (it wasn't really waiting). The solution is to do the sub channel setup in its own work queue context that is scheduled by the primary channel setup; and then happens later. Fixes: 732e498 ("netvsc: fix race on sub channel creation") Reported-by: Dexuan Cui <[email protected]> Signed-off-by: Stephen Hemminger <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4400081 commit 8195b13

File tree

4 files changed

+94
-45
lines changed

4 files changed

+94
-45
lines changed

drivers/net/hyperv/hyperv_net.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ int netvsc_recv_callback(struct net_device *net,
204204
const struct ndis_pkt_8021q_info *vlan);
205205
void netvsc_channel_cb(void *context);
206206
int netvsc_poll(struct napi_struct *napi, int budget);
207+
208+
void rndis_set_subchannel(struct work_struct *w);
207209
bool rndis_filter_opened(const struct netvsc_device *nvdev);
208210
int rndis_filter_open(struct netvsc_device *nvdev);
209211
int rndis_filter_close(struct netvsc_device *nvdev);
@@ -782,6 +784,7 @@ struct netvsc_device {
782784
u32 num_chn;
783785

784786
atomic_t open_chn;
787+
struct work_struct subchan_work;
785788
wait_queue_head_t subchan_open;
786789

787790
struct rndis_device *extension;

drivers/net/hyperv/netvsc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ static struct netvsc_device *alloc_net_device(void)
8181

8282
init_completion(&net_device->channel_init_wait);
8383
init_waitqueue_head(&net_device->subchan_open);
84+
INIT_WORK(&net_device->subchan_work, rndis_set_subchannel);
8485

8586
return net_device;
8687
}
@@ -557,6 +558,8 @@ void netvsc_device_remove(struct hv_device *device)
557558
= rtnl_dereference(net_device_ctx->nvdev);
558559
int i;
559560

561+
cancel_work_sync(&net_device->subchan_work);
562+
560563
netvsc_disconnect_vsp(device);
561564

562565
RCU_INIT_POINTER(net_device_ctx->nvdev, NULL);

drivers/net/hyperv/netvsc_drv.c

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -853,10 +853,7 @@ static int netvsc_set_channels(struct net_device *net,
853853
rndis_filter_device_remove(dev, nvdev);
854854

855855
nvdev = rndis_filter_device_add(dev, &device_info);
856-
if (!IS_ERR(nvdev)) {
857-
netif_set_real_num_tx_queues(net, nvdev->num_chn);
858-
netif_set_real_num_rx_queues(net, nvdev->num_chn);
859-
} else {
856+
if (IS_ERR(nvdev)) {
860857
ret = PTR_ERR(nvdev);
861858
device_info.num_chn = orig;
862859
nvdev = rndis_filter_device_add(dev, &device_info);
@@ -1954,9 +1951,6 @@ static int netvsc_probe(struct hv_device *dev,
19541951
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
19551952
net->vlan_features = net->features;
19561953

1957-
netif_set_real_num_tx_queues(net, nvdev->num_chn);
1958-
netif_set_real_num_rx_queues(net, nvdev->num_chn);
1959-
19601954
netdev_lockdep_set_classes(net);
19611955

19621956
/* MTU range: 68 - 1500 or 65521 */
@@ -2012,9 +2006,10 @@ static int netvsc_remove(struct hv_device *dev)
20122006
if (vf_netdev)
20132007
netvsc_unregister_vf(vf_netdev);
20142008

2009+
unregister_netdevice(net);
2010+
20152011
rndis_filter_device_remove(dev,
20162012
rtnl_dereference(ndev_ctx->nvdev));
2017-
unregister_netdevice(net);
20182013
rtnl_unlock();
20192014

20202015
hv_set_drvdata(dev, NULL);

drivers/net/hyperv/rndis_filter.c

Lines changed: 85 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,21 +1039,95 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc)
10391039

10401040
/* Set the channel before opening.*/
10411041
nvchan->channel = new_sc;
1042-
netif_napi_add(ndev, &nvchan->napi,
1043-
netvsc_poll, NAPI_POLL_WEIGHT);
10441042

10451043
ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
10461044
nvscdev->ring_size * PAGE_SIZE, NULL, 0,
10471045
netvsc_channel_cb, nvchan);
10481046
if (ret == 0)
10491047
napi_enable(&nvchan->napi);
10501048
else
1051-
netif_napi_del(&nvchan->napi);
1049+
netdev_notice(ndev, "sub channel open failed: %d\n", ret);
10521050

10531051
atomic_inc(&nvscdev->open_chn);
10541052
wake_up(&nvscdev->subchan_open);
10551053
}
10561054

1055+
/* Open sub-channels after completing the handling of the device probe.
1056+
* This breaks overlap of processing the host message for the
1057+
* new primary channel with the initialization of sub-channels.
1058+
*/
1059+
void rndis_set_subchannel(struct work_struct *w)
1060+
{
1061+
struct netvsc_device *nvdev
1062+
= container_of(w, struct netvsc_device, subchan_work);
1063+
struct nvsp_message *init_packet = &nvdev->channel_init_pkt;
1064+
struct net_device_context *ndev_ctx;
1065+
struct rndis_device *rdev;
1066+
struct net_device *ndev;
1067+
struct hv_device *hv_dev;
1068+
int i, ret;
1069+
1070+
if (!rtnl_trylock()) {
1071+
schedule_work(w);
1072+
return;
1073+
}
1074+
1075+
rdev = nvdev->extension;
1076+
if (!rdev)
1077+
goto unlock; /* device was removed */
1078+
1079+
ndev = rdev->ndev;
1080+
ndev_ctx = netdev_priv(ndev);
1081+
hv_dev = ndev_ctx->device_ctx;
1082+
1083+
memset(init_packet, 0, sizeof(struct nvsp_message));
1084+
init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
1085+
init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
1086+
init_packet->msg.v5_msg.subchn_req.num_subchannels =
1087+
nvdev->num_chn - 1;
1088+
ret = vmbus_sendpacket(hv_dev->channel, init_packet,
1089+
sizeof(struct nvsp_message),
1090+
(unsigned long)init_packet,
1091+
VM_PKT_DATA_INBAND,
1092+
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
1093+
if (ret) {
1094+
netdev_err(ndev, "sub channel allocate send failed: %d\n", ret);
1095+
goto failed;
1096+
}
1097+
1098+
wait_for_completion(&nvdev->channel_init_wait);
1099+
if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
1100+
netdev_err(ndev, "sub channel request failed\n");
1101+
goto failed;
1102+
}
1103+
1104+
nvdev->num_chn = 1 +
1105+
init_packet->msg.v5_msg.subchn_comp.num_subchannels;
1106+
1107+
/* wait for all sub channels to open */
1108+
wait_event(nvdev->subchan_open,
1109+
atomic_read(&nvdev->open_chn) == nvdev->num_chn);
1110+
1111+
/* ignore failues from setting rss parameters, still have channels */
1112+
rndis_filter_set_rss_param(rdev, netvsc_hash_key);
1113+
1114+
netif_set_real_num_tx_queues(ndev, nvdev->num_chn);
1115+
netif_set_real_num_rx_queues(ndev, nvdev->num_chn);
1116+
1117+
rtnl_unlock();
1118+
return;
1119+
1120+
failed:
1121+
/* fallback to only primary channel */
1122+
for (i = 1; i < nvdev->num_chn; i++)
1123+
netif_napi_del(&nvdev->chan_table[i].napi);
1124+
1125+
nvdev->max_chn = 1;
1126+
nvdev->num_chn = 1;
1127+
unlock:
1128+
rtnl_unlock();
1129+
}
1130+
10571131
struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
10581132
struct netvsc_device_info *device_info)
10591133
{
@@ -1063,7 +1137,6 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
10631137
struct rndis_device *rndis_device;
10641138
struct ndis_offload hwcaps;
10651139
struct ndis_offload_params offloads;
1066-
struct nvsp_message *init_packet;
10671140
struct ndis_recv_scale_cap rsscap;
10681141
u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
10691142
unsigned int gso_max_size = GSO_MAX_SIZE;
@@ -1215,9 +1288,7 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
12151288
net_device->num_chn);
12161289

12171290
atomic_set(&net_device->open_chn, 1);
1218-
1219-
if (net_device->num_chn == 1)
1220-
return net_device;
1291+
vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
12211292

12221293
for (i = 1; i < net_device->num_chn; i++) {
12231294
ret = netvsc_alloc_recv_comp_ring(net_device, i);
@@ -1228,38 +1299,15 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
12281299
}
12291300
}
12301301

1231-
vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
1302+
for (i = 1; i < net_device->num_chn; i++)
1303+
netif_napi_add(net, &net_device->chan_table[i].napi,
1304+
netvsc_poll, NAPI_POLL_WEIGHT);
12321305

1233-
init_packet = &net_device->channel_init_pkt;
1234-
memset(init_packet, 0, sizeof(struct nvsp_message));
1235-
init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
1236-
init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
1237-
init_packet->msg.v5_msg.subchn_req.num_subchannels =
1238-
net_device->num_chn - 1;
1239-
ret = vmbus_sendpacket(dev->channel, init_packet,
1240-
sizeof(struct nvsp_message),
1241-
(unsigned long)init_packet,
1242-
VM_PKT_DATA_INBAND,
1243-
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
1244-
if (ret)
1245-
goto out;
1246-
1247-
wait_for_completion(&net_device->channel_init_wait);
1248-
if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
1249-
ret = -ENODEV;
1250-
goto out;
1251-
}
1306+
if (net_device->num_chn > 1)
1307+
schedule_work(&net_device->subchan_work);
12521308

1253-
net_device->num_chn = 1 +
1254-
init_packet->msg.v5_msg.subchn_comp.num_subchannels;
1255-
1256-
/* wait for all sub channels to open */
1257-
wait_event(net_device->subchan_open,
1258-
atomic_read(&net_device->open_chn) == net_device->num_chn);
1259-
1260-
/* ignore failues from setting rss parameters, still have channels */
1261-
rndis_filter_set_rss_param(rndis_device, netvsc_hash_key);
12621309
out:
1310+
/* if unavailable, just proceed with one queue */
12631311
if (ret) {
12641312
net_device->max_chn = 1;
12651313
net_device->num_chn = 1;
@@ -1280,10 +1328,10 @@ void rndis_filter_device_remove(struct hv_device *dev,
12801328
/* Halt and release the rndis device */
12811329
rndis_filter_halt_device(rndis_dev);
12821330

1283-
kfree(rndis_dev);
12841331
net_dev->extension = NULL;
12851332

12861333
netvsc_device_remove(dev);
1334+
kfree(rndis_dev);
12871335
}
12881336

12891337
int rndis_filter_open(struct netvsc_device *nvdev)

0 commit comments

Comments
 (0)