]>
Commit | Line | Data |
---|---|---|
684f36d1 GKH |
1 | From foo@baz Tue May 22 20:10:42 CEST 2018 |
2 | From: Stephen Hemminger <stephen@networkplumber.org> | |
3 | Date: Mon, 14 May 2018 15:32:16 -0700 | |
4 | Subject: hv_netvsc: use RCU to fix concurrent rx and queue changes | |
5 | ||
6 | From: Stephen Hemminger <stephen@networkplumber.org> | |
7 | ||
8 | [ Commit 02400fcee2542ee334a2394e0d9f6efd969fe782 upstream. ] | |
9 | ||
10 | The receive processing may continue to happen while the | |
11 | internal network device state is in RCU grace period. | |
12 | The internal RNDIS structure is associated with the | |
13 | internal netvsc_device structure; both have the same | |
14 | RCU lifetime. | |
15 | ||
16 | Defer freeing all associated parts until after grace | |
17 | period. | |
18 | ||
19 | Fixes: 0cf737808ae7 ("hv_netvsc: netvsc_teardown_gpadl() split") | |
20 | Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com> | |
21 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
22 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
23 | --- | |
24 | drivers/net/hyperv/netvsc.c | 17 ++++------------ | |
25 | drivers/net/hyperv/rndis_filter.c | 39 ++++++++++++++++---------------------- | |
26 | 2 files changed, 22 insertions(+), 34 deletions(-) | |
27 | ||
28 | --- a/drivers/net/hyperv/netvsc.c | |
29 | +++ b/drivers/net/hyperv/netvsc.c | |
30 | @@ -89,6 +89,11 @@ static void free_netvsc_device(struct rc | |
31 | = container_of(head, struct netvsc_device, rcu); | |
32 | int i; | |
33 | ||
34 | + kfree(nvdev->extension); | |
35 | + vfree(nvdev->recv_buf); | |
36 | + vfree(nvdev->send_buf); | |
37 | + kfree(nvdev->send_section_map); | |
38 | + | |
39 | for (i = 0; i < VRSS_CHANNEL_MAX; i++) | |
40 | vfree(nvdev->chan_table[i].mrc.slots); | |
41 | ||
42 | @@ -210,12 +215,6 @@ static void netvsc_teardown_gpadl(struct | |
43 | net_device->recv_buf_gpadl_handle = 0; | |
44 | } | |
45 | ||
46 | - if (net_device->recv_buf) { | |
47 | - /* Free up the receive buffer */ | |
48 | - vfree(net_device->recv_buf); | |
49 | - net_device->recv_buf = NULL; | |
50 | - } | |
51 | - | |
52 | if (net_device->send_buf_gpadl_handle) { | |
53 | ret = vmbus_teardown_gpadl(device->channel, | |
54 | net_device->send_buf_gpadl_handle); | |
55 | @@ -230,12 +229,6 @@ static void netvsc_teardown_gpadl(struct | |
56 | } | |
57 | net_device->send_buf_gpadl_handle = 0; | |
58 | } | |
59 | - if (net_device->send_buf) { | |
60 | - /* Free up the send buffer */ | |
61 | - vfree(net_device->send_buf); | |
62 | - net_device->send_buf = NULL; | |
63 | - } | |
64 | - kfree(net_device->send_section_map); | |
65 | } | |
66 | ||
67 | int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx) | |
68 | --- a/drivers/net/hyperv/rndis_filter.c | |
69 | +++ b/drivers/net/hyperv/rndis_filter.c | |
70 | @@ -266,13 +266,23 @@ static void rndis_set_link_state(struct | |
71 | } | |
72 | } | |
73 | ||
74 | -static void rndis_filter_receive_response(struct rndis_device *dev, | |
75 | - struct rndis_message *resp) | |
76 | +static void rndis_filter_receive_response(struct net_device *ndev, | |
77 | + struct netvsc_device *nvdev, | |
78 | + const struct rndis_message *resp) | |
79 | { | |
80 | + struct rndis_device *dev = nvdev->extension; | |
81 | struct rndis_request *request = NULL; | |
82 | bool found = false; | |
83 | unsigned long flags; | |
84 | - struct net_device *ndev = dev->ndev; | |
85 | + | |
86 | + /* This should never happen, it means control message | |
87 | + * response received after device removed. | |
88 | + */ | |
89 | + if (dev->state == RNDIS_DEV_UNINITIALIZED) { | |
90 | + netdev_err(ndev, | |
91 | + "got rndis message uninitialized\n"); | |
92 | + return; | |
93 | + } | |
94 | ||
95 | spin_lock_irqsave(&dev->request_lock, flags); | |
96 | list_for_each_entry(request, &dev->req_list, list_ent) { | |
97 | @@ -353,7 +363,7 @@ static inline void *rndis_get_ppi(struct | |
98 | } | |
99 | ||
100 | static int rndis_filter_receive_data(struct net_device *ndev, | |
101 | - struct rndis_device *dev, | |
102 | + struct netvsc_device *nvdev, | |
103 | struct rndis_message *msg, | |
104 | struct vmbus_channel *channel, | |
105 | void *data, u32 data_buflen) | |
106 | @@ -373,7 +383,7 @@ static int rndis_filter_receive_data(str | |
107 | * should be the data packet size plus the trailer padding size | |
108 | */ | |
109 | if (unlikely(data_buflen < rndis_pkt->data_len)) { | |
110 | - netdev_err(dev->ndev, "rndis message buffer " | |
111 | + netdev_err(ndev, "rndis message buffer " | |
112 | "overflow detected (got %u, min %u)" | |
113 | "...dropping this message!\n", | |
114 | data_buflen, rndis_pkt->data_len); | |
115 | @@ -401,34 +411,20 @@ int rndis_filter_receive(struct net_devi | |
116 | void *data, u32 buflen) | |
117 | { | |
118 | struct net_device_context *net_device_ctx = netdev_priv(ndev); | |
119 | - struct rndis_device *rndis_dev = net_dev->extension; | |
120 | struct rndis_message *rndis_msg = data; | |
121 | ||
122 | - /* Make sure the rndis device state is initialized */ | |
123 | - if (unlikely(!rndis_dev)) { | |
124 | - netif_err(net_device_ctx, rx_err, ndev, | |
125 | - "got rndis message but no rndis device!\n"); | |
126 | - return NVSP_STAT_FAIL; | |
127 | - } | |
128 | - | |
129 | - if (unlikely(rndis_dev->state == RNDIS_DEV_UNINITIALIZED)) { | |
130 | - netif_err(net_device_ctx, rx_err, ndev, | |
131 | - "got rndis message uninitialized\n"); | |
132 | - return NVSP_STAT_FAIL; | |
133 | - } | |
134 | - | |
135 | if (netif_msg_rx_status(net_device_ctx)) | |
136 | dump_rndis_message(dev, rndis_msg); | |
137 | ||
138 | switch (rndis_msg->ndis_msg_type) { | |
139 | case RNDIS_MSG_PACKET: | |
140 | - return rndis_filter_receive_data(ndev, rndis_dev, rndis_msg, | |
141 | + return rndis_filter_receive_data(ndev, net_dev, rndis_msg, | |
142 | channel, data, buflen); | |
143 | case RNDIS_MSG_INIT_C: | |
144 | case RNDIS_MSG_QUERY_C: | |
145 | case RNDIS_MSG_SET_C: | |
146 | /* completion msgs */ | |
147 | - rndis_filter_receive_response(rndis_dev, rndis_msg); | |
148 | + rndis_filter_receive_response(ndev, net_dev, rndis_msg); | |
149 | break; | |
150 | ||
151 | case RNDIS_MSG_INDICATE: | |
152 | @@ -1349,7 +1345,6 @@ void rndis_filter_device_remove(struct h | |
153 | net_dev->extension = NULL; | |
154 | ||
155 | netvsc_device_remove(dev); | |
156 | - kfree(rndis_dev); | |
157 | } | |
158 | ||
159 | int rndis_filter_open(struct netvsc_device *nvdev) |