OSDN Git Service

net: permit skb_segment on head_frag frag_list skb
[android-x86/kernel.git] / net / core / skbuff.c
index 46cb222..b5c75d4 100644 (file)
@@ -3460,6 +3460,19 @@ void *skb_pull_rcsum(struct sk_buff *skb, unsigned int len)
 }
 EXPORT_SYMBOL_GPL(skb_pull_rcsum);
 
+static inline skb_frag_t skb_head_frag_to_page_desc(struct sk_buff *frag_skb)
+{
+       skb_frag_t head_frag;
+       struct page *page;
+
+       page = virt_to_head_page(frag_skb->head);
+       head_frag.page.p = page;
+       head_frag.page_offset = frag_skb->data -
+               (unsigned char *)page_address(page);
+       head_frag.size = skb_headlen(frag_skb);
+       return head_frag;
+}
+
 /**
  *     skb_segment - Perform protocol segmentation on skb.
  *     @head_skb: buffer to segment
@@ -3664,15 +3677,19 @@ normal:
 
                while (pos < offset + len) {
                        if (i >= nfrags) {
-                               BUG_ON(skb_headlen(list_skb));
-
                                i = 0;
                                nfrags = skb_shinfo(list_skb)->nr_frags;
                                frag = skb_shinfo(list_skb)->frags;
                                frag_skb = list_skb;
+                               if (!skb_headlen(list_skb)) {
+                                       BUG_ON(!nfrags);
+                               } else {
+                                       BUG_ON(!list_skb->head_frag);
 
-                               BUG_ON(!nfrags);
-
+                                       /* to make room for head_frag. */
+                                       i--;
+                                       frag--;
+                               }
                                if (skb_orphan_frags(frag_skb, GFP_ATOMIC) ||
                                    skb_zerocopy_clone(nskb, frag_skb,
                                                       GFP_ATOMIC))
@@ -3689,7 +3706,7 @@ normal:
                                goto err;
                        }
 
-                       *nskb_frag = *frag;
+                       *nskb_frag = (i < 0) ? skb_head_frag_to_page_desc(frag_skb) : *frag;
                        __skb_frag_ref(nskb_frag);
                        size = skb_frag_size(nskb_frag);