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>

authored by

Herbert Xu and committed by
David S. Miller
7d7e5a60 6fccab67

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