aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/xen-netfront.c
diff options
context:
space:
mode:
authorVitaly Kuznetsov <vkuznets@redhat.com>2016-09-19 06:53:40 -0400
committerDavid S. Miller <davem@davemloft.net>2016-09-20 04:40:41 -0400
commitfd07160bb7180cdd0afeb089d8cdfd66002f17e6 (patch)
treeee49e66f29836da66dfc5ccfa8548a2fc74fc338 /drivers/net/xen-netfront.c
parent1a21101d21d7ef056dfda1d7b843289e05ecd034 (diff)
xen-netfront: avoid packet loss when ethernet header crosses page boundary
Small packet loss is reported on complex multi host network configurations including tunnels, NAT, ... My investigation led me to the following check in netback which drops packets: if (unlikely(txreq.size < ETH_HLEN)) { netdev_err(queue->vif->dev, "Bad packet size: %d\n", txreq.size); xenvif_tx_err(queue, &txreq, extra_count, idx); break; } But this check itself is legitimate. SKBs consist of a linear part (which has to have the ethernet header) and (optionally) a number of frags. Netfront transmits the head of the linear part up to the page boundary as the first request and all the rest becomes frags so when we're reconstructing the SKB in netback we can't distinguish between original frags and the 'tail' of the linear part. The first SKB needs to be at least ETH_HLEN size. So in case we have an SKB with its linear part starting too close to the page boundary the packet is lost. I see two ways to fix the issue: - Change the 'wire' protocol between netfront and netback to start keeping the original SKB structure. We'll have to add a flag indicating the fact that the particular request is a part of the original linear part and not a frag. We'll need to know the length of the linear part to pre-allocate memory. - Avoid transmitting SKBs with linear parts starting too close to the page boundary. That seems preferable short-term and shouldn't bring significant performance degradation as such packets are rare. That's what this patch is trying to achieve with skb_copy(). Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Acked-by: David Vrabel <david.vrabel@citrix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/xen-netfront.c')
-rw-r--r--drivers/net/xen-netfront.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 96ccd4e943db..e17879dd5d5a 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -565,6 +565,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
565 struct netfront_queue *queue = NULL; 565 struct netfront_queue *queue = NULL;
566 unsigned int num_queues = dev->real_num_tx_queues; 566 unsigned int num_queues = dev->real_num_tx_queues;
567 u16 queue_index; 567 u16 queue_index;
568 struct sk_buff *nskb;
568 569
569 /* Drop the packet if no queues are set up */ 570 /* Drop the packet if no queues are set up */
570 if (num_queues < 1) 571 if (num_queues < 1)
@@ -593,6 +594,20 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
593 594
594 page = virt_to_page(skb->data); 595 page = virt_to_page(skb->data);
595 offset = offset_in_page(skb->data); 596 offset = offset_in_page(skb->data);
597
598 /* The first req should be at least ETH_HLEN size or the packet will be
599 * dropped by netback.
600 */
601 if (unlikely(PAGE_SIZE - offset < ETH_HLEN)) {
602 nskb = skb_copy(skb, GFP_ATOMIC);
603 if (!nskb)
604 goto drop;
605 dev_kfree_skb_any(skb);
606 skb = nskb;
607 page = virt_to_page(skb->data);
608 offset = offset_in_page(skb->data);
609 }
610
596 len = skb_headlen(skb); 611 len = skb_headlen(skb);
597 612
598 spin_lock_irqsave(&queue->tx_lock, flags); 613 spin_lock_irqsave(&queue->tx_lock, flags);