ipsec: ipcomp - Decompress into frags if necessary
diff mbox series

Message ID 20080725074328.GB16747@gondor.apana.org.au
State New, archived
Headers show
Series
  • ipsec: ipcomp - Decompress into frags if necessary
Related show

Commit Message

Herbert Xu July 25, 2008, 7:43 a.m. UTC
On Fri, Jul 25, 2008 at 03:41:09PM +0800, Herbert Xu wrote:
>
> Here is the first part:

And finally:

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>



Cheers,

Comments

David Miller July 25, 2008, 9:58 a.m. UTC | #1
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Fri, 25 Jul 2008 15:43:28 +0800

> On Fri, Jul 25, 2008 at 03:41:09PM +0800, Herbert Xu wrote:
> >
> > Here is the first part:
> 
> And finally:
> 
> 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>

Also applied, nice work Herbert.

About MAX_SKB_FRAGS, if we go over that limit we probably need to
log a message or something because we'll likely get that same kind
of huge sized packet that we can't handle in future retransmits.
There is no way to figure out that this is what is happening.

Actually I wonder if it's even possible.  Our limit is 64K anyways
(and that's our scratch buffer size) and MAX_SKB_FRAGS is always
large enough to accomodate at least 64K of data.

So perhaps it wants a WARN_ON :-)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Herbert Xu July 25, 2008, 10:46 a.m. UTC | #2
On Fri, Jul 25, 2008 at 02:58:01AM -0700, David Miller wrote:
>
> Actually I wonder if it's even possible.  Our limit is 64K anyways
> (and that's our scratch buffer size) and MAX_SKB_FRAGS is always
> large enough to accomodate at least 64K of data.

Right it shouldn't be possible right now.

> So perhaps it wants a WARN_ON :-)

There is one :)

Cheers,
David Miller July 25, 2008, 10:55 a.m. UTC | #3
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Fri, 25 Jul 2008 18:46:03 +0800

> On Fri, Jul 25, 2008 at 02:58:01AM -0700, David Miller wrote:
> > So perhaps it wants a WARN_ON :-)
> 
> There is one :)

Indeed, I even read that thing as I was composing my original
reply, hehe.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Patch
diff mbox series

diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
index b51e804..800f669 100644
--- a/net/xfrm/xfrm_ipcomp.c
+++ b/net/xfrm/xfrm_ipcomp.c
@@ -17,6 +17,7 @@ 
 
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <linux/gfp.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
@@ -49,6 +50,7 @@  static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
 	u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
 	struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
 	int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
+	int len;
 
 	if (err)
 		goto out;
@@ -58,13 +60,47 @@  static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
 		goto out;
 	}
 
-	err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
-	if (err)
-		goto out;
+	len = dlen - plen;
+	if (len > skb_tailroom(skb))
+		len = skb_tailroom(skb);
+
+	skb->truesize += len;
+	__skb_put(skb, len);
+
+	len += plen;
+	skb_copy_to_linear_data(skb, scratch, len);
+
+	while ((scratch += len, dlen -= len) > 0) {
+		skb_frag_t *frag;
+
+		err = -EMSGSIZE;
+		if (WARN_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS))
+			goto out;
+
+		frag = skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags;
+		frag->page = alloc_page(GFP_ATOMIC);
+
+		err = -ENOMEM;
+		if (!frag->page)
+			goto out;
+
+		len = PAGE_SIZE;
+		if (dlen < len)
+			len = dlen;
+
+		memcpy(page_address(frag->page), scratch, len);
+
+		frag->page_offset = 0;
+		frag->size = len;
+		skb->truesize += len;
+		skb->data_len += len;
+		skb->len += len;
+
+		skb_shinfo(skb)->nr_frags++;
+	}
+
+	err = 0;
 
-	skb->truesize += dlen - plen;
-	__skb_put(skb, dlen - plen);
-	skb_copy_to_linear_data(skb, scratch, dlen);
 out:
 	put_cpu();
 	return err;