diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2008-07-25 05:55:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-07-25 05:55:33 -0400 |
commit | 7d7e5a60c62e88cb8782760bb6c4d3bd1577a6c6 (patch) | |
tree | c6bedd62097698466d7dc0b5166e4ed29e1b6ee5 /net | |
parent | 6fccab671f2f0a24b799f29a4ec878f62d34656c (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/xfrm/xfrm_ipcomp.c | 48 |
1 files changed, 42 insertions, 6 deletions
diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c index b51e804fbbad..800f669083fb 100644 --- a/net/xfrm/xfrm_ipcomp.c +++ b/net/xfrm/xfrm_ipcomp.c | |||
@@ -17,6 +17,7 @@ | |||
17 | 17 | ||
18 | #include <linux/crypto.h> | 18 | #include <linux/crypto.h> |
19 | #include <linux/err.h> | 19 | #include <linux/err.h> |
20 | #include <linux/gfp.h> | ||
20 | #include <linux/list.h> | 21 | #include <linux/list.h> |
21 | #include <linux/module.h> | 22 | #include <linux/module.h> |
22 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
@@ -49,6 +50,7 @@ static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb) | |||
49 | u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); | 50 | u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); |
50 | struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu); | 51 | struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu); |
51 | int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen); | 52 | int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen); |
53 | int len; | ||
52 | 54 | ||
53 | if (err) | 55 | if (err) |
54 | goto out; | 56 | goto out; |
@@ -58,13 +60,47 @@ static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb) | |||
58 | goto out; | 60 | goto out; |
59 | } | 61 | } |
60 | 62 | ||
61 | err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC); | 63 | len = dlen - plen; |
62 | if (err) | 64 | if (len > skb_tailroom(skb)) |
63 | goto out; | 65 | len = skb_tailroom(skb); |
66 | |||
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; | ||
64 | 103 | ||
65 | skb->truesize += dlen - plen; | ||
66 | __skb_put(skb, dlen - plen); | ||
67 | skb_copy_to_linear_data(skb, scratch, dlen); | ||
68 | out: | 104 | out: |
69 | put_cpu(); | 105 | put_cpu(); |
70 | return err; | 106 | return err; |