提交 b17075d5 编写于 作者: I Igor Druzhinin 提交者: David S. Miller

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: NIgor Druzhinin <igor.druzhinin@citrix.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 e37791ec
...@@ -165,13 +165,17 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -165,13 +165,17 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct xenvif *vif = netdev_priv(dev); struct xenvif *vif = netdev_priv(dev);
struct xenvif_queue *queue = NULL; struct xenvif_queue *queue = NULL;
unsigned int num_queues = vif->num_queues; unsigned int num_queues;
u16 index; u16 index;
struct xenvif_rx_cb *cb; struct xenvif_rx_cb *cb;
BUG_ON(skb->dev != dev); BUG_ON(skb->dev != dev);
/* Drop the packet if queues are not set up */ /* Drop the packet if queues are not set up.
* This handler should be called inside an RCU read section
* so we don't need to enter it here explicitly.
*/
num_queues = READ_ONCE(vif->num_queues);
if (num_queues < 1) if (num_queues < 1)
goto drop; goto drop;
...@@ -222,18 +226,18 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev) ...@@ -222,18 +226,18 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
{ {
struct xenvif *vif = netdev_priv(dev); struct xenvif *vif = netdev_priv(dev);
struct xenvif_queue *queue = NULL; struct xenvif_queue *queue = NULL;
unsigned int num_queues;
u64 rx_bytes = 0; u64 rx_bytes = 0;
u64 rx_packets = 0; u64 rx_packets = 0;
u64 tx_bytes = 0; u64 tx_bytes = 0;
u64 tx_packets = 0; u64 tx_packets = 0;
unsigned int index; unsigned int index;
spin_lock(&vif->lock); rcu_read_lock();
if (vif->queues == NULL) num_queues = READ_ONCE(vif->num_queues);
goto out;
/* Aggregate tx and rx stats from each queue */ /* Aggregate tx and rx stats from each queue */
for (index = 0; index < vif->num_queues; ++index) { for (index = 0; index < num_queues; ++index) {
queue = &vif->queues[index]; queue = &vif->queues[index];
rx_bytes += queue->stats.rx_bytes; rx_bytes += queue->stats.rx_bytes;
rx_packets += queue->stats.rx_packets; rx_packets += queue->stats.rx_packets;
...@@ -241,8 +245,7 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev) ...@@ -241,8 +245,7 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
tx_packets += queue->stats.tx_packets; tx_packets += queue->stats.tx_packets;
} }
out: rcu_read_unlock();
spin_unlock(&vif->lock);
vif->dev->stats.rx_bytes = rx_bytes; vif->dev->stats.rx_bytes = rx_bytes;
vif->dev->stats.rx_packets = rx_packets; vif->dev->stats.rx_packets = rx_packets;
...@@ -378,10 +381,13 @@ static void xenvif_get_ethtool_stats(struct net_device *dev, ...@@ -378,10 +381,13 @@ static void xenvif_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 * data) struct ethtool_stats *stats, u64 * data)
{ {
struct xenvif *vif = netdev_priv(dev); struct xenvif *vif = netdev_priv(dev);
unsigned int num_queues = vif->num_queues; unsigned int num_queues;
int i; int i;
unsigned int queue_index; unsigned int queue_index;
rcu_read_lock();
num_queues = READ_ONCE(vif->num_queues);
for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) { for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) {
unsigned long accum = 0; unsigned long accum = 0;
for (queue_index = 0; queue_index < num_queues; ++queue_index) { 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,6 +396,8 @@ static void xenvif_get_ethtool_stats(struct net_device *dev,
} }
data[i] = accum; data[i] = accum;
} }
rcu_read_unlock();
} }
static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data) static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data)
......
...@@ -214,7 +214,7 @@ static void xenvif_fatal_tx_err(struct xenvif *vif) ...@@ -214,7 +214,7 @@ static void xenvif_fatal_tx_err(struct xenvif *vif)
netdev_err(vif->dev, "fatal error; disabling device\n"); netdev_err(vif->dev, "fatal error; disabling device\n");
vif->disabled = true; vif->disabled = true;
/* Disable the vif from queue 0's kthread */ /* Disable the vif from queue 0's kthread */
if (vif->queues) if (vif->num_queues)
xenvif_kick_thread(&vif->queues[0]); xenvif_kick_thread(&vif->queues[0]);
} }
......
...@@ -495,26 +495,26 @@ static void backend_disconnect(struct backend_info *be) ...@@ -495,26 +495,26 @@ static void backend_disconnect(struct backend_info *be)
struct xenvif *vif = be->vif; struct xenvif *vif = be->vif;
if (vif) { if (vif) {
unsigned int num_queues = vif->num_queues;
unsigned int queue_index; unsigned int queue_index;
struct xenvif_queue *queues;
xen_unregister_watchers(vif); xen_unregister_watchers(vif);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
xenvif_debugfs_delif(vif); xenvif_debugfs_delif(vif);
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */
xenvif_disconnect_data(vif); xenvif_disconnect_data(vif);
for (queue_index = 0;
queue_index < vif->num_queues;
++queue_index)
xenvif_deinit_queue(&vif->queues[queue_index]);
spin_lock(&vif->lock); /* At this point some of the handlers may still be active
queues = vif->queues; * so we need to have additional synchronization here.
*/
vif->num_queues = 0; vif->num_queues = 0;
vif->queues = NULL; synchronize_net();
spin_unlock(&vif->lock);
vfree(queues); for (queue_index = 0; queue_index < num_queues; ++queue_index)
xenvif_deinit_queue(&vif->queues[queue_index]);
vfree(vif->queues);
vif->queues = NULL;
xenvif_disconnect_ctrl(vif); xenvif_disconnect_ctrl(vif);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册