aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hemminger <stephen@networkplumber.org>2018-03-20 18:03:03 -0400
committerDavid S. Miller <davem@davemloft.net>2018-03-22 12:45:09 -0400
commit02400fcee2542ee334a2394e0d9f6efd969fe782 (patch)
tree2ad599ce6ff91b8f1b298adf73d4fdde29f756af
parent8348e0460ab1473f06c8b824699dd2eed3c1979d (diff)
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: 0cf737808ae7 ("hv_netvsc: netvsc_teardown_gpadl() split") Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/hyperv/netvsc.c17
-rw-r--r--drivers/net/hyperv/rndis_filter.c39
2 files changed, 21 insertions, 35 deletions
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index e70a44273f55..12c044baf1af 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -90,6 +90,11 @@ static void free_netvsc_device(struct rcu_head *head)
90 = container_of(head, struct netvsc_device, rcu); 90 = container_of(head, struct netvsc_device, rcu);
91 int i; 91 int i;
92 92
93 kfree(nvdev->extension);
94 vfree(nvdev->recv_buf);
95 vfree(nvdev->send_buf);
96 kfree(nvdev->send_section_map);
97
93 for (i = 0; i < VRSS_CHANNEL_MAX; i++) 98 for (i = 0; i < VRSS_CHANNEL_MAX; i++)
94 vfree(nvdev->chan_table[i].mrc.slots); 99 vfree(nvdev->chan_table[i].mrc.slots);
95 100
@@ -211,12 +216,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device,
211 net_device->recv_buf_gpadl_handle = 0; 216 net_device->recv_buf_gpadl_handle = 0;
212 } 217 }
213 218
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
220 if (net_device->send_buf_gpadl_handle) { 219 if (net_device->send_buf_gpadl_handle) {
221 ret = vmbus_teardown_gpadl(device->channel, 220 ret = vmbus_teardown_gpadl(device->channel,
222 net_device->send_buf_gpadl_handle); 221 net_device->send_buf_gpadl_handle);
@@ -231,12 +230,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device,
231 } 230 }
232 net_device->send_buf_gpadl_handle = 0; 231 net_device->send_buf_gpadl_handle = 0;
233 } 232 }
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);
240} 233}
241 234
242int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx) 235int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx)
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index 00ec80c23fe5..963314eb3226 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -264,13 +264,23 @@ static void rndis_set_link_state(struct rndis_device *rdev,
264 } 264 }
265} 265}
266 266
267static void rndis_filter_receive_response(struct rndis_device *dev, 267static void rndis_filter_receive_response(struct net_device *ndev,
268 struct rndis_message *resp) 268 struct netvsc_device *nvdev,
269 const struct rndis_message *resp)
269{ 270{
271 struct rndis_device *dev = nvdev->extension;
270 struct rndis_request *request = NULL; 272 struct rndis_request *request = NULL;
271 bool found = false; 273 bool found = false;
272 unsigned long flags; 274 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 }
274 284
275 spin_lock_irqsave(&dev->request_lock, flags); 285 spin_lock_irqsave(&dev->request_lock, flags);
276 list_for_each_entry(request, &dev->req_list, list_ent) { 286 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)
352 362
353static int rndis_filter_receive_data(struct net_device *ndev, 363static int rndis_filter_receive_data(struct net_device *ndev,
354 struct netvsc_device *nvdev, 364 struct netvsc_device *nvdev,
355 struct rndis_device *dev,
356 struct rndis_message *msg, 365 struct rndis_message *msg,
357 struct vmbus_channel *channel, 366 struct vmbus_channel *channel,
358 void *data, u32 data_buflen) 367 void *data, u32 data_buflen)
@@ -372,7 +381,7 @@ static int rndis_filter_receive_data(struct net_device *ndev,
372 * should be the data packet size plus the trailer padding size 381 * should be the data packet size plus the trailer padding size
373 */ 382 */
374 if (unlikely(data_buflen < rndis_pkt->data_len)) { 383 if (unlikely(data_buflen < rndis_pkt->data_len)) {
375 netdev_err(dev->ndev, "rndis message buffer " 384 netdev_err(ndev, "rndis message buffer "
376 "overflow detected (got %u, min %u)" 385 "overflow detected (got %u, min %u)"
377 "...dropping this message!\n", 386 "...dropping this message!\n",
378 data_buflen, rndis_pkt->data_len); 387 data_buflen, rndis_pkt->data_len);
@@ -400,35 +409,20 @@ int rndis_filter_receive(struct net_device *ndev,
400 void *data, u32 buflen) 409 void *data, u32 buflen)
401{ 410{
402 struct net_device_context *net_device_ctx = netdev_priv(ndev); 411 struct net_device_context *net_device_ctx = netdev_priv(ndev);
403 struct rndis_device *rndis_dev = net_dev->extension;
404 struct rndis_message *rndis_msg = data; 412 struct rndis_message *rndis_msg = data;
405 413
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
419 if (netif_msg_rx_status(net_device_ctx)) 414 if (netif_msg_rx_status(net_device_ctx))
420 dump_rndis_message(ndev, rndis_msg); 415 dump_rndis_message(ndev, rndis_msg);
421 416
422 switch (rndis_msg->ndis_msg_type) { 417 switch (rndis_msg->ndis_msg_type) {
423 case RNDIS_MSG_PACKET: 418 case RNDIS_MSG_PACKET:
424 return rndis_filter_receive_data(ndev, net_dev, 419 return rndis_filter_receive_data(ndev, net_dev, rndis_msg,
425 rndis_dev, rndis_msg,
426 channel, data, buflen); 420 channel, data, buflen);
427 case RNDIS_MSG_INIT_C: 421 case RNDIS_MSG_INIT_C:
428 case RNDIS_MSG_QUERY_C: 422 case RNDIS_MSG_QUERY_C:
429 case RNDIS_MSG_SET_C: 423 case RNDIS_MSG_SET_C:
430 /* completion msgs */ 424 /* completion msgs */
431 rndis_filter_receive_response(rndis_dev, rndis_msg); 425 rndis_filter_receive_response(ndev, net_dev, rndis_msg);
432 break; 426 break;
433 427
434 case RNDIS_MSG_INDICATE: 428 case RNDIS_MSG_INDICATE:
@@ -1357,7 +1351,6 @@ void rndis_filter_device_remove(struct hv_device *dev,
1357 net_dev->extension = NULL; 1351 net_dev->extension = NULL;
1358 1352
1359 netvsc_device_remove(dev); 1353 netvsc_device_remove(dev);
1360 kfree(rndis_dev);
1361} 1354}
1362 1355
1363int rndis_filter_open(struct netvsc_device *nvdev) 1356int rndis_filter_open(struct netvsc_device *nvdev)