Commit 02400fce authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

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: 0cf73780 ("hv_netvsc: netvsc_teardown_gpadl() split")
Signed-off-by: default avatarStephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8348e046
...@@ -90,6 +90,11 @@ static void free_netvsc_device(struct rcu_head *head) ...@@ -90,6 +90,11 @@ static void free_netvsc_device(struct rcu_head *head)
= container_of(head, struct netvsc_device, rcu); = container_of(head, struct netvsc_device, rcu);
int i; int i;
kfree(nvdev->extension);
vfree(nvdev->recv_buf);
vfree(nvdev->send_buf);
kfree(nvdev->send_section_map);
for (i = 0; i < VRSS_CHANNEL_MAX; i++) for (i = 0; i < VRSS_CHANNEL_MAX; i++)
vfree(nvdev->chan_table[i].mrc.slots); vfree(nvdev->chan_table[i].mrc.slots);
...@@ -211,12 +216,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device, ...@@ -211,12 +216,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device,
net_device->recv_buf_gpadl_handle = 0; net_device->recv_buf_gpadl_handle = 0;
} }
if (net_device->recv_buf) {
/* Free up the receive buffer */
vfree(net_device->recv_buf);
net_device->recv_buf = NULL;
}
if (net_device->send_buf_gpadl_handle) { if (net_device->send_buf_gpadl_handle) {
ret = vmbus_teardown_gpadl(device->channel, ret = vmbus_teardown_gpadl(device->channel,
net_device->send_buf_gpadl_handle); net_device->send_buf_gpadl_handle);
...@@ -231,12 +230,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device, ...@@ -231,12 +230,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device,
} }
net_device->send_buf_gpadl_handle = 0; net_device->send_buf_gpadl_handle = 0;
} }
if (net_device->send_buf) {
/* Free up the send buffer */
vfree(net_device->send_buf);
net_device->send_buf = NULL;
}
kfree(net_device->send_section_map);
} }
int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx) int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx)
......
...@@ -264,13 +264,23 @@ static void rndis_set_link_state(struct rndis_device *rdev, ...@@ -264,13 +264,23 @@ static void rndis_set_link_state(struct rndis_device *rdev,
} }
} }
static void rndis_filter_receive_response(struct rndis_device *dev, static void rndis_filter_receive_response(struct net_device *ndev,
struct rndis_message *resp) struct netvsc_device *nvdev,
const struct rndis_message *resp)
{ {
struct rndis_device *dev = nvdev->extension;
struct rndis_request *request = NULL; struct rndis_request *request = NULL;
bool found = false; bool found = false;
unsigned long flags; unsigned long flags;
struct net_device *ndev = dev->ndev;
/* This should never happen, it means control message
* response received after device removed.
*/
if (dev->state == RNDIS_DEV_UNINITIALIZED) {
netdev_err(ndev,
"got rndis message uninitialized\n");
return;
}
spin_lock_irqsave(&dev->request_lock, flags); spin_lock_irqsave(&dev->request_lock, flags);
list_for_each_entry(request, &dev->req_list, list_ent) { 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,7 +362,6 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
static int rndis_filter_receive_data(struct net_device *ndev, static int rndis_filter_receive_data(struct net_device *ndev,
struct netvsc_device *nvdev, struct netvsc_device *nvdev,
struct rndis_device *dev,
struct rndis_message *msg, struct rndis_message *msg,
struct vmbus_channel *channel, struct vmbus_channel *channel,
void *data, u32 data_buflen) void *data, u32 data_buflen)
...@@ -372,7 +381,7 @@ static int rndis_filter_receive_data(struct net_device *ndev, ...@@ -372,7 +381,7 @@ static int rndis_filter_receive_data(struct net_device *ndev,
* should be the data packet size plus the trailer padding size * should be the data packet size plus the trailer padding size
*/ */
if (unlikely(data_buflen < rndis_pkt->data_len)) { if (unlikely(data_buflen < rndis_pkt->data_len)) {
netdev_err(dev->ndev, "rndis message buffer " netdev_err(ndev, "rndis message buffer "
"overflow detected (got %u, min %u)" "overflow detected (got %u, min %u)"
"...dropping this message!\n", "...dropping this message!\n",
data_buflen, rndis_pkt->data_len); data_buflen, rndis_pkt->data_len);
...@@ -400,35 +409,20 @@ int rndis_filter_receive(struct net_device *ndev, ...@@ -400,35 +409,20 @@ int rndis_filter_receive(struct net_device *ndev,
void *data, u32 buflen) void *data, u32 buflen)
{ {
struct net_device_context *net_device_ctx = netdev_priv(ndev); struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct rndis_device *rndis_dev = net_dev->extension;
struct rndis_message *rndis_msg = data; struct rndis_message *rndis_msg = data;
/* Make sure the rndis device state is initialized */
if (unlikely(!rndis_dev)) {
netif_dbg(net_device_ctx, rx_err, ndev,
"got rndis message but no rndis device!\n");
return NVSP_STAT_FAIL;
}
if (unlikely(rndis_dev->state == RNDIS_DEV_UNINITIALIZED)) {
netif_dbg(net_device_ctx, rx_err, ndev,
"got rndis message uninitialized\n");
return NVSP_STAT_FAIL;
}
if (netif_msg_rx_status(net_device_ctx)) if (netif_msg_rx_status(net_device_ctx))
dump_rndis_message(ndev, rndis_msg); dump_rndis_message(ndev, rndis_msg);
switch (rndis_msg->ndis_msg_type) { switch (rndis_msg->ndis_msg_type) {
case RNDIS_MSG_PACKET: case RNDIS_MSG_PACKET:
return rndis_filter_receive_data(ndev, net_dev, return rndis_filter_receive_data(ndev, net_dev, rndis_msg,
rndis_dev, rndis_msg,
channel, data, buflen); channel, data, buflen);
case RNDIS_MSG_INIT_C: case RNDIS_MSG_INIT_C:
case RNDIS_MSG_QUERY_C: case RNDIS_MSG_QUERY_C:
case RNDIS_MSG_SET_C: case RNDIS_MSG_SET_C:
/* completion msgs */ /* completion msgs */
rndis_filter_receive_response(rndis_dev, rndis_msg); rndis_filter_receive_response(ndev, net_dev, rndis_msg);
break; break;
case RNDIS_MSG_INDICATE: case RNDIS_MSG_INDICATE:
...@@ -1357,7 +1351,6 @@ void rndis_filter_device_remove(struct hv_device *dev, ...@@ -1357,7 +1351,6 @@ void rndis_filter_device_remove(struct hv_device *dev,
net_dev->extension = NULL; net_dev->extension = NULL;
netvsc_device_remove(dev); netvsc_device_remove(dev);
kfree(rndis_dev);
} }
int rndis_filter_open(struct netvsc_device *nvdev) int rndis_filter_open(struct netvsc_device *nvdev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment