Commit 7d7e5a60c62e88cb8782760bb6c4d3bd1577a6c6

Authored by Herbert Xu
Committed by David S. Miller
1 parent 6fccab671f

ipsec: ipcomp - Decompress into frags if necessary

When decompressing extremely large packets allocating them through
kmalloc is prone to failure.  Therefore it's better to use page
frags instead.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 42 additions and 6 deletions Side-by-side Diff

net/xfrm/xfrm_ipcomp.c
... ... @@ -17,6 +17,7 @@
17 17  
18 18 #include <linux/crypto.h>
19 19 #include <linux/err.h>
  20 +#include <linux/gfp.h>
20 21 #include <linux/list.h>
21 22 #include <linux/module.h>
22 23 #include <linux/mutex.h>
... ... @@ -49,6 +50,7 @@
49 50 u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
50 51 struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
51 52 int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
  53 + int len;
52 54  
53 55 if (err)
54 56 goto out;
55 57  
... ... @@ -58,13 +60,47 @@
58 60 goto out;
59 61 }
60 62  
61   - err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
62   - if (err)
63   - goto out;
  63 + len = dlen - plen;
  64 + if (len > skb_tailroom(skb))
  65 + len = skb_tailroom(skb);
64 66  
65   - skb->truesize += dlen - plen;
66   - __skb_put(skb, dlen - plen);
67   - skb_copy_to_linear_data(skb, scratch, dlen);
  67 + skb->truesize += len;
  68 + __skb_put(skb, len);
  69 +
  70 + len += plen;
  71 + skb_copy_to_linear_data(skb, scratch, len);
  72 +
  73 + while ((scratch += len, dlen -= len) > 0) {
  74 + skb_frag_t *frag;
  75 +
  76 + err = -EMSGSIZE;
  77 + if (WARN_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS))
  78 + goto out;
  79 +
  80 + frag = skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags;
  81 + frag->page = alloc_page(GFP_ATOMIC);
  82 +
  83 + err = -ENOMEM;
  84 + if (!frag->page)
  85 + goto out;
  86 +
  87 + len = PAGE_SIZE;
  88 + if (dlen < len)
  89 + len = dlen;
  90 +
  91 + memcpy(page_address(frag->page), scratch, len);
  92 +
  93 + frag->page_offset = 0;
  94 + frag->size = len;
  95 + skb->truesize += len;
  96 + skb->data_len += len;
  97 + skb->len += len;
  98 +
  99 + skb_shinfo(skb)->nr_frags++;
  100 + }
  101 +
  102 + err = 0;
  103 +
68 104 out:
69 105 put_cpu();
70 106 return err;