aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Druzhinin <igor.druzhinin@citrix.com>2017-03-10 16:36:22 -0500
committerDavid S. Miller <davem@davemloft.net>2017-03-13 02:44:43 -0400
commitb17075d5c1988b83f840d272c795ac17d57ce804 (patch)
treeb760831992b881a42e1c3ad3990855ac8a989088
parente37791ec1ad785b59022ae211f63a16189bacebf (diff)
xen-netback: fix race condition on XenBus disconnect
In some cases during XenBus disconnect event handling and subsequent queue resource release there may be some TX handlers active on other processors. Use RCU in order to synchronize with them. Signed-off-by: Igor Druzhinin <igor.druzhinin@citrix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/xen-netback/interface.c26
-rw-r--r--drivers/net/xen-netback/netback.c2
-rw-r--r--drivers/net/xen-netback/xenbus.c20
3 files changed, 28 insertions, 20 deletions
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index 829b26cd4549..8397f6c92451 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -165,13 +165,17 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
165{ 165{
166 struct xenvif *vif = netdev_priv(dev); 166 struct xenvif *vif = netdev_priv(dev);
167 struct xenvif_queue *queue = NULL; 167 struct xenvif_queue *queue = NULL;
168 unsigned int num_queues = vif->num_queues; 168 unsigned int num_queues;
169 u16 index; 169 u16 index;
170 struct xenvif_rx_cb *cb; 170 struct xenvif_rx_cb *cb;
171 171
172 BUG_ON(skb->dev != dev); 172 BUG_ON(skb->dev != dev);
173 173
174 /* Drop the packet if queues are not set up */ 174 /* Drop the packet if queues are not set up.
175 * This handler should be called inside an RCU read section
176 * so we don't need to enter it here explicitly.
177 */
178 num_queues = READ_ONCE(vif->num_queues);
175 if (num_queues < 1) 179 if (num_queues < 1)
176 goto drop; 180 goto drop;
177 181
@@ -222,18 +226,18 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
222{ 226{
223 struct xenvif *vif = netdev_priv(dev); 227 struct xenvif *vif = netdev_priv(dev);
224 struct xenvif_queue *queue = NULL; 228 struct xenvif_queue *queue = NULL;
229 unsigned int num_queues;
225 u64 rx_bytes = 0; 230 u64 rx_bytes = 0;
226 u64 rx_packets = 0; 231 u64 rx_packets = 0;
227 u64 tx_bytes = 0; 232 u64 tx_bytes = 0;
228 u64 tx_packets = 0; 233 u64 tx_packets = 0;
229 unsigned int index; 234 unsigned int index;
230 235
231 spin_lock(&vif->lock); 236 rcu_read_lock();
232 if (vif->queues == NULL) 237 num_queues = READ_ONCE(vif->num_queues);
233 goto out;
234 238
235 /* Aggregate tx and rx stats from each queue */ 239 /* Aggregate tx and rx stats from each queue */
236 for (index = 0; index < vif->num_queues; ++index) { 240 for (index = 0; index < num_queues; ++index) {
237 queue = &vif->queues[index]; 241 queue = &vif->queues[index];
238 rx_bytes += queue->stats.rx_bytes; 242 rx_bytes += queue->stats.rx_bytes;
239 rx_packets += queue->stats.rx_packets; 243 rx_packets += queue->stats.rx_packets;
@@ -241,8 +245,7 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
241 tx_packets += queue->stats.tx_packets; 245 tx_packets += queue->stats.tx_packets;
242 } 246 }
243 247
244out: 248 rcu_read_unlock();
245 spin_unlock(&vif->lock);
246 249
247 vif->dev->stats.rx_bytes = rx_bytes; 250 vif->dev->stats.rx_bytes = rx_bytes;
248 vif->dev->stats.rx_packets = rx_packets; 251 vif->dev->stats.rx_packets = rx_packets;
@@ -378,10 +381,13 @@ static void xenvif_get_ethtool_stats(struct net_device *dev,
378 struct ethtool_stats *stats, u64 * data) 381 struct ethtool_stats *stats, u64 * data)
379{ 382{
380 struct xenvif *vif = netdev_priv(dev); 383 struct xenvif *vif = netdev_priv(dev);
381 unsigned int num_queues = vif->num_queues; 384 unsigned int num_queues;
382 int i; 385 int i;
383 unsigned int queue_index; 386 unsigned int queue_index;
384 387
388 rcu_read_lock();
389 num_queues = READ_ONCE(vif->num_queues);
390
385 for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) { 391 for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) {
386 unsigned long accum = 0; 392 unsigned long accum = 0;
387 for (queue_index = 0; queue_index < num_queues; ++queue_index) { 393 for (queue_index = 0; queue_index < num_queues; ++queue_index) {
@@ -390,6 +396,8 @@ static void xenvif_get_ethtool_stats(struct net_device *dev,
390 } 396 }
391 data[i] = accum; 397 data[i] = accum;
392 } 398 }
399
400 rcu_read_unlock();
393} 401}
394 402
395static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data) 403static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data)
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index f9bcf4a665bc..602d408fa25e 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -214,7 +214,7 @@ static void xenvif_fatal_tx_err(struct xenvif *vif)
214 netdev_err(vif->dev, "fatal error; disabling device\n"); 214 netdev_err(vif->dev, "fatal error; disabling device\n");
215 vif->disabled = true; 215 vif->disabled = true;
216 /* Disable the vif from queue 0's kthread */ 216 /* Disable the vif from queue 0's kthread */
217 if (vif->queues) 217 if (vif->num_queues)
218 xenvif_kick_thread(&vif->queues[0]); 218 xenvif_kick_thread(&vif->queues[0]);
219} 219}
220 220
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index d2d7cd9145b1..a56d3eab35dd 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -495,26 +495,26 @@ static void backend_disconnect(struct backend_info *be)
495 struct xenvif *vif = be->vif; 495 struct xenvif *vif = be->vif;
496 496
497 if (vif) { 497 if (vif) {
498 unsigned int num_queues = vif->num_queues;
498 unsigned int queue_index; 499 unsigned int queue_index;
499 struct xenvif_queue *queues;
500 500
501 xen_unregister_watchers(vif); 501 xen_unregister_watchers(vif);
502#ifdef CONFIG_DEBUG_FS 502#ifdef CONFIG_DEBUG_FS
503 xenvif_debugfs_delif(vif); 503 xenvif_debugfs_delif(vif);
504#endif /* CONFIG_DEBUG_FS */ 504#endif /* CONFIG_DEBUG_FS */
505 xenvif_disconnect_data(vif); 505 xenvif_disconnect_data(vif);
506 for (queue_index = 0;
507 queue_index < vif->num_queues;
508 ++queue_index)
509 xenvif_deinit_queue(&vif->queues[queue_index]);
510 506
511 spin_lock(&vif->lock); 507 /* At this point some of the handlers may still be active
512 queues = vif->queues; 508 * so we need to have additional synchronization here.
509 */
513 vif->num_queues = 0; 510 vif->num_queues = 0;
514 vif->queues = NULL; 511 synchronize_net();
515 spin_unlock(&vif->lock);
516 512
517 vfree(queues); 513 for (queue_index = 0; queue_index < num_queues; ++queue_index)
514 xenvif_deinit_queue(&vif->queues[queue_index]);
515
516 vfree(vif->queues);
517 vif->queues = NULL;
518 518
519 xenvif_disconnect_ctrl(vif); 519 xenvif_disconnect_ctrl(vif);
520 } 520 }