Skip to content

Commit 02400fc

Browse files
shemmingerdavem330
authored andcommitted
hv_netvsc: use RCU to fix concurrent rx and queue changes
The receive processing may continue to happen while the internal network device state is in RCU grace period. The internal RNDIS structure is associated with the internal netvsc_device structure; both have the same RCU lifetime. Defer freeing all associated parts until after grace period. Fixes: 0cf7378 ("hv_netvsc: netvsc_teardown_gpadl() split") Signed-off-by: Stephen Hemminger <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8348e04 commit 02400fc

File tree

2 files changed

+21
-35
lines changed

2 files changed

+21
-35
lines changed

drivers/net/hyperv/netvsc.c

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ static void free_netvsc_device(struct rcu_head *head)
9090
= container_of(head, struct netvsc_device, rcu);
9191
int i;
9292

93+
kfree(nvdev->extension);
94+
vfree(nvdev->recv_buf);
95+
vfree(nvdev->send_buf);
96+
kfree(nvdev->send_section_map);
97+
9398
for (i = 0; i < VRSS_CHANNEL_MAX; i++)
9499
vfree(nvdev->chan_table[i].mrc.slots);
95100

@@ -211,12 +216,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device,
211216
net_device->recv_buf_gpadl_handle = 0;
212217
}
213218

214-
if (net_device->recv_buf) {
215-
/* Free up the receive buffer */
216-
vfree(net_device->recv_buf);
217-
net_device->recv_buf = NULL;
218-
}
219-
220219
if (net_device->send_buf_gpadl_handle) {
221220
ret = vmbus_teardown_gpadl(device->channel,
222221
net_device->send_buf_gpadl_handle);
@@ -231,12 +230,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device,
231230
}
232231
net_device->send_buf_gpadl_handle = 0;
233232
}
234-
if (net_device->send_buf) {
235-
/* Free up the send buffer */
236-
vfree(net_device->send_buf);
237-
net_device->send_buf = NULL;
238-
}
239-
kfree(net_device->send_section_map);
240233
}
241234

242235
int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx)

drivers/net/hyperv/rndis_filter.c

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -264,13 +264,23 @@ static void rndis_set_link_state(struct rndis_device *rdev,
264264
}
265265
}
266266

267-
static void rndis_filter_receive_response(struct rndis_device *dev,
268-
struct rndis_message *resp)
267+
static void rndis_filter_receive_response(struct net_device *ndev,
268+
struct netvsc_device *nvdev,
269+
const struct rndis_message *resp)
269270
{
271+
struct rndis_device *dev = nvdev->extension;
270272
struct rndis_request *request = NULL;
271273
bool found = false;
272274
unsigned long flags;
273-
struct net_device *ndev = dev->ndev;
275+
276+
/* This should never happen, it means control message
277+
* response received after device removed.
278+
*/
279+
if (dev->state == RNDIS_DEV_UNINITIALIZED) {
280+
netdev_err(ndev,
281+
"got rndis message uninitialized\n");
282+
return;
283+
}
274284

275285
spin_lock_irqsave(&dev->request_lock, flags);
276286
list_for_each_entry(request, &dev->req_list, list_ent) {
@@ -352,7 +362,6 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
352362

353363
static int rndis_filter_receive_data(struct net_device *ndev,
354364
struct netvsc_device *nvdev,
355-
struct rndis_device *dev,
356365
struct rndis_message *msg,
357366
struct vmbus_channel *channel,
358367
void *data, u32 data_buflen)
@@ -372,7 +381,7 @@ static int rndis_filter_receive_data(struct net_device *ndev,
372381
* should be the data packet size plus the trailer padding size
373382
*/
374383
if (unlikely(data_buflen < rndis_pkt->data_len)) {
375-
netdev_err(dev->ndev, "rndis message buffer "
384+
netdev_err(ndev, "rndis message buffer "
376385
"overflow detected (got %u, min %u)"
377386
"...dropping this message!\n",
378387
data_buflen, rndis_pkt->data_len);
@@ -400,35 +409,20 @@ int rndis_filter_receive(struct net_device *ndev,
400409
void *data, u32 buflen)
401410
{
402411
struct net_device_context *net_device_ctx = netdev_priv(ndev);
403-
struct rndis_device *rndis_dev = net_dev->extension;
404412
struct rndis_message *rndis_msg = data;
405413

406-
/* Make sure the rndis device state is initialized */
407-
if (unlikely(!rndis_dev)) {
408-
netif_dbg(net_device_ctx, rx_err, ndev,
409-
"got rndis message but no rndis device!\n");
410-
return NVSP_STAT_FAIL;
411-
}
412-
413-
if (unlikely(rndis_dev->state == RNDIS_DEV_UNINITIALIZED)) {
414-
netif_dbg(net_device_ctx, rx_err, ndev,
415-
"got rndis message uninitialized\n");
416-
return NVSP_STAT_FAIL;
417-
}
418-
419414
if (netif_msg_rx_status(net_device_ctx))
420415
dump_rndis_message(ndev, rndis_msg);
421416

422417
switch (rndis_msg->ndis_msg_type) {
423418
case RNDIS_MSG_PACKET:
424-
return rndis_filter_receive_data(ndev, net_dev,
425-
rndis_dev, rndis_msg,
419+
return rndis_filter_receive_data(ndev, net_dev, rndis_msg,
426420
channel, data, buflen);
427421
case RNDIS_MSG_INIT_C:
428422
case RNDIS_MSG_QUERY_C:
429423
case RNDIS_MSG_SET_C:
430424
/* completion msgs */
431-
rndis_filter_receive_response(rndis_dev, rndis_msg);
425+
rndis_filter_receive_response(ndev, net_dev, rndis_msg);
432426
break;
433427

434428
case RNDIS_MSG_INDICATE:
@@ -1357,7 +1351,6 @@ void rndis_filter_device_remove(struct hv_device *dev,
13571351
net_dev->extension = NULL;
13581352

13591353
netvsc_device_remove(dev);
1360-
kfree(rndis_dev);
13611354
}
13621355

13631356
int rndis_filter_open(struct netvsc_device *nvdev)

0 commit comments

Comments
 (0)