summaryrefslogtreecommitdiff
path: root/drivers/net/xen-netback
diff options
context:
space:
mode:
authorDavid Vrabel <david.vrabel@citrix.com>2015-03-04 14:14:47 +0300
committerDavid S. Miller <davem@davemloft.net>2015-03-05 22:58:17 +0300
commit49d9991a18f9aae7b14abbd9c1cc87555330a769 (patch)
treef642411098ecbf508d8303abdaacaa7e39642482 /drivers/net/xen-netback
parentd63951d7442982ef81df585a9c08c2b5fd49f898 (diff)
downloadlinux-49d9991a18f9aae7b14abbd9c1cc87555330a769.tar.xz
xen-netback: unref frags when handling a from-guest skb with a frag list
Every time a VIF is destroyed up to 256 pages may be leaked if packets with more than MAX_SKB_FRAGS frags were transmitted from the guest. Even worse, if another user of ballooned pages allocated one of these ballooned pages it would not handle the unexpectedly >1 page count (e.g., gntdev would deadlock when unmapping a grant because the page count would never reach 1). When handling a from-guest skb with a frag list, unref the frags before releasing them so they are freed correctly when the VIF is destroyed. Signed-off-by: David Vrabel <david.vrabel@citrix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/xen-netback')
-rw-r--r--drivers/net/xen-netback/netback.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index c4d68d768408..f1d84fb1eba8 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -1349,7 +1349,7 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s
{
unsigned int offset = skb_headlen(skb);
skb_frag_t frags[MAX_SKB_FRAGS];
- int i;
+ int i, f;
struct ubuf_info *uarg;
struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
@@ -1389,6 +1389,11 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s
frags[i].page_offset = 0;
skb_frag_size_set(&frags[i], len);
}
+
+ /* Release all the original (foreign) frags. */
+ for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
+ skb_frag_unref(skb, f);
+
/* swap out with old one */
memcpy(skb_shinfo(skb)->frags,
frags,