Skip to content

Commit b17075d

Browse files
Igor Druzhinindavem330
authored andcommitted
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>
1 parent e37791e commit b17075d

File tree

3 files changed

+28
-20
lines changed

3 files changed

+28
-20
lines changed

drivers/net/xen-netback/interface.c

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,17 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
165165
{
166166
struct xenvif *vif = netdev_priv(dev);
167167
struct xenvif_queue *queue = NULL;
168-
unsigned int num_queues = vif->num_queues;
168+
unsigned int num_queues;
169169
u16 index;
170170
struct xenvif_rx_cb *cb;
171171

172172
BUG_ON(skb->dev != dev);
173173

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);
175179
if (num_queues < 1)
176180
goto drop;
177181

@@ -222,27 +226,26 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
222226
{
223227
struct xenvif *vif = netdev_priv(dev);
224228
struct xenvif_queue *queue = NULL;
229+
unsigned int num_queues;
225230
u64 rx_bytes = 0;
226231
u64 rx_packets = 0;
227232
u64 tx_bytes = 0;
228233
u64 tx_packets = 0;
229234
unsigned int index;
230235

231-
spin_lock(&vif->lock);
232-
if (vif->queues == NULL)
233-
goto out;
236+
rcu_read_lock();
237+
num_queues = READ_ONCE(vif->num_queues);
234238

235239
/* 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) {
237241
queue = &vif->queues[index];
238242
rx_bytes += queue->stats.rx_bytes;
239243
rx_packets += queue->stats.rx_packets;
240244
tx_bytes += queue->stats.tx_bytes;
241245
tx_packets += queue->stats.tx_packets;
242246
}
243247

244-
out:
245-
spin_unlock(&vif->lock);
248+
rcu_read_unlock();
246249

247250
vif->dev->stats.rx_bytes = rx_bytes;
248251
vif->dev->stats.rx_packets = rx_packets;
@@ -378,10 +381,13 @@ static void xenvif_get_ethtool_stats(struct net_device *dev,
378381
struct ethtool_stats *stats, u64 * data)
379382
{
380383
struct xenvif *vif = netdev_priv(dev);
381-
unsigned int num_queues = vif->num_queues;
384+
unsigned int num_queues;
382385
int i;
383386
unsigned int queue_index;
384387

388+
rcu_read_lock();
389+
num_queues = READ_ONCE(vif->num_queues);
390+
385391
for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) {
386392
unsigned long accum = 0;
387393
for (queue_index = 0; queue_index < num_queues; ++queue_index) {
@@ -390,6 +396,8 @@ static void xenvif_get_ethtool_stats(struct net_device *dev,
390396
}
391397
data[i] = accum;
392398
}
399+
400+
rcu_read_unlock();
393401
}
394402

395403
static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data)

drivers/net/xen-netback/netback.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ static void xenvif_fatal_tx_err(struct xenvif *vif)
214214
netdev_err(vif->dev, "fatal error; disabling device\n");
215215
vif->disabled = true;
216216
/* Disable the vif from queue 0's kthread */
217-
if (vif->queues)
217+
if (vif->num_queues)
218218
xenvif_kick_thread(&vif->queues[0]);
219219
}
220220

drivers/net/xen-netback/xenbus.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -495,26 +495,26 @@ static void backend_disconnect(struct backend_info *be)
495495
struct xenvif *vif = be->vif;
496496

497497
if (vif) {
498+
unsigned int num_queues = vif->num_queues;
498499
unsigned int queue_index;
499-
struct xenvif_queue *queues;
500500

501501
xen_unregister_watchers(vif);
502502
#ifdef CONFIG_DEBUG_FS
503503
xenvif_debugfs_delif(vif);
504504
#endif /* CONFIG_DEBUG_FS */
505505
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]);
510506

511-
spin_lock(&vif->lock);
512-
queues = vif->queues;
507+
/* At this point some of the handlers may still be active
508+
* so we need to have additional synchronization here.
509+
*/
513510
vif->num_queues = 0;
514-
vif->queues = NULL;
515-
spin_unlock(&vif->lock);
511+
synchronize_net();
516512

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;
518518

519519
xenvif_disconnect_ctrl(vif);
520520
}

0 commit comments

Comments
 (0)