Skip to content

Commit fd07160

Browse files
vittyvkdavem330
authored andcommitted
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>
1 parent 1a21101 commit fd07160

File tree

1 file changed

+15
-0
lines changed

1 file changed

+15
-0
lines changed

drivers/net/xen-netfront.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
565565
struct netfront_queue *queue = NULL;
566566
unsigned int num_queues = dev->real_num_tx_queues;
567567
u16 queue_index;
568+
struct sk_buff *nskb;
568569

569570
/* Drop the packet if no queues are set up */
570571
if (num_queues < 1)
@@ -593,6 +594,20 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
593594

594595
page = virt_to_page(skb->data);
595596
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+
596611
len = skb_headlen(skb);
597612

598613
spin_lock_irqsave(&queue->tx_lock, flags);

0 commit comments

Comments
 (0)