mirror of
https://github.com/torvalds/linux.git
synced 2026-06-09 07:03:37 +02:00
xen-netfront: pull on receive skb may need to happen earlier
commit093b9c71b6upstream. Due to commit3683243b("xen-netfront: use __pskb_pull_tail to ensure linear area is big enough on RX") xennet_fill_frags() may end up filling MAX_SKB_FRAGS + 1 fragments in a receive skb, and only reduce the fragment count subsequently via __pskb_pull_tail(). That's a result of xennet_get_responses() allowing a maximum of one more slot to be consumed (and intermediately transformed into a fragment) if the head slot has a size less than or equal to RX_COPY_THRESHOLD. Hence we need to adjust xennet_fill_frags() to pull earlier if we reached the maximum fragment count - due to the described behavior of xennet_get_responses() this guarantees that at least the first fragment will get completely consumed, and hence the fragment count reduced. In order to not needlessly call __pskb_pull_tail() twice, make the original call conditional upon the pull target not having been reached yet, and defer the newly added one as much as possible (an alternative would have been to always call the function right before the call to xennet_fill_frags(), but that would imply more frequent cases of needing to call it twice). Signed-off-by: Jan Beulich <jbeulich@suse.com> Acked-by: Wei Liu <wei.liu2@citrix.com> Cc: Ian Campbell <ian.campbell@citrix.com> Acked-by: Ian Campbell <ian.campbell@citrix.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
0183a8ee8c
commit
f4b5b99f18
|
|
@ -276,8 +276,7 @@ static void xennet_alloc_rx_buffers(struct net_device *dev)
|
|||
break;
|
||||
}
|
||||
|
||||
__skb_fill_page_desc(skb, 0, page, 0, 0);
|
||||
skb_shinfo(skb)->nr_frags = 1;
|
||||
skb_add_rx_frag(skb, 0, page, 0, 0, PAGE_SIZE);
|
||||
__skb_queue_tail(&np->rx_batch, skb);
|
||||
}
|
||||
|
||||
|
|
@ -822,7 +821,6 @@ static RING_IDX xennet_fill_frags(struct netfront_info *np,
|
|||
struct sk_buff_head *list)
|
||||
{
|
||||
struct skb_shared_info *shinfo = skb_shinfo(skb);
|
||||
int nr_frags = shinfo->nr_frags;
|
||||
RING_IDX cons = np->rx.rsp_cons;
|
||||
struct sk_buff *nskb;
|
||||
|
||||
|
|
@ -831,19 +829,21 @@ static RING_IDX xennet_fill_frags(struct netfront_info *np,
|
|||
RING_GET_RESPONSE(&np->rx, ++cons);
|
||||
skb_frag_t *nfrag = &skb_shinfo(nskb)->frags[0];
|
||||
|
||||
__skb_fill_page_desc(skb, nr_frags,
|
||||
skb_frag_page(nfrag),
|
||||
rx->offset, rx->status);
|
||||
if (shinfo->nr_frags == MAX_SKB_FRAGS) {
|
||||
unsigned int pull_to = NETFRONT_SKB_CB(skb)->pull_to;
|
||||
|
||||
skb->data_len += rx->status;
|
||||
BUG_ON(pull_to <= skb_headlen(skb));
|
||||
__pskb_pull_tail(skb, pull_to - skb_headlen(skb));
|
||||
}
|
||||
BUG_ON(shinfo->nr_frags >= MAX_SKB_FRAGS);
|
||||
|
||||
skb_add_rx_frag(skb, shinfo->nr_frags, skb_frag_page(nfrag),
|
||||
rx->offset, rx->status, PAGE_SIZE);
|
||||
|
||||
skb_shinfo(nskb)->nr_frags = 0;
|
||||
kfree_skb(nskb);
|
||||
|
||||
nr_frags++;
|
||||
}
|
||||
|
||||
shinfo->nr_frags = nr_frags;
|
||||
return cons;
|
||||
}
|
||||
|
||||
|
|
@ -929,7 +929,8 @@ static int handle_incoming_queue(struct net_device *dev,
|
|||
while ((skb = __skb_dequeue(rxq)) != NULL) {
|
||||
int pull_to = NETFRONT_SKB_CB(skb)->pull_to;
|
||||
|
||||
__pskb_pull_tail(skb, pull_to - skb_headlen(skb));
|
||||
if (pull_to > skb_headlen(skb))
|
||||
__pskb_pull_tail(skb, pull_to - skb_headlen(skb));
|
||||
|
||||
/* Ethernet work: Delayed to here as it peeks the header. */
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
|
|
@ -1015,16 +1016,10 @@ static int xennet_poll(struct napi_struct *napi, int budget)
|
|||
skb_shinfo(skb)->frags[0].page_offset = rx->offset;
|
||||
skb_frag_size_set(&skb_shinfo(skb)->frags[0], rx->status);
|
||||
skb->data_len = rx->status;
|
||||
skb->len += rx->status;
|
||||
|
||||
i = xennet_fill_frags(np, skb, &tmpq);
|
||||
|
||||
/*
|
||||
* Truesize is the actual allocation size, even if the
|
||||
* allocation is only partially used.
|
||||
*/
|
||||
skb->truesize += PAGE_SIZE * skb_shinfo(skb)->nr_frags;
|
||||
skb->len += skb->data_len;
|
||||
|
||||
if (rx->flags & XEN_NETRXF_csum_blank)
|
||||
skb->ip_summed = CHECKSUM_PARTIAL;
|
||||
else if (rx->flags & XEN_NETRXF_data_validated)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user