|
@@ -257,11 +257,11 @@ nodata:
|
|
|
}
|
|
|
|
|
|
|
|
|
-static void skb_drop_fraglist(struct sk_buff *skb)
|
|
|
+static void skb_drop_list(struct sk_buff **listp)
|
|
|
{
|
|
|
- struct sk_buff *list = skb_shinfo(skb)->frag_list;
|
|
|
+ struct sk_buff *list = *listp;
|
|
|
|
|
|
- skb_shinfo(skb)->frag_list = NULL;
|
|
|
+ *listp = NULL;
|
|
|
|
|
|
do {
|
|
|
struct sk_buff *this = list;
|
|
@@ -270,6 +270,11 @@ static void skb_drop_fraglist(struct sk_buff *skb)
|
|
|
} while (list);
|
|
|
}
|
|
|
|
|
|
+static inline void skb_drop_fraglist(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ skb_drop_list(&skb_shinfo(skb)->frag_list);
|
|
|
+}
|
|
|
+
|
|
|
static void skb_clone_fraglist(struct sk_buff *skb)
|
|
|
{
|
|
|
struct sk_buff *list;
|
|
@@ -830,41 +835,75 @@ free_skb:
|
|
|
|
|
|
int ___pskb_trim(struct sk_buff *skb, unsigned int len)
|
|
|
{
|
|
|
+ struct sk_buff **fragp;
|
|
|
+ struct sk_buff *frag;
|
|
|
int offset = skb_headlen(skb);
|
|
|
int nfrags = skb_shinfo(skb)->nr_frags;
|
|
|
int i;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (skb_cloned(skb) &&
|
|
|
+ unlikely((err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))))
|
|
|
+ return err;
|
|
|
|
|
|
for (i = 0; i < nfrags; i++) {
|
|
|
int end = offset + skb_shinfo(skb)->frags[i].size;
|
|
|
- if (end > len) {
|
|
|
- if (skb_cloned(skb)) {
|
|
|
- if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- if (len <= offset) {
|
|
|
- put_page(skb_shinfo(skb)->frags[i].page);
|
|
|
- skb_shinfo(skb)->nr_frags--;
|
|
|
- } else {
|
|
|
- skb_shinfo(skb)->frags[i].size = len - offset;
|
|
|
- }
|
|
|
+
|
|
|
+ if (end < len) {
|
|
|
+ offset = end;
|
|
|
+ continue;
|
|
|
}
|
|
|
- offset = end;
|
|
|
+
|
|
|
+ if (len > offset)
|
|
|
+ skb_shinfo(skb)->frags[i++].size = len - offset;
|
|
|
+
|
|
|
+ skb_shinfo(skb)->nr_frags = i;
|
|
|
+
|
|
|
+ for (; i < nfrags; i++)
|
|
|
+ put_page(skb_shinfo(skb)->frags[i].page);
|
|
|
+
|
|
|
+ if (skb_shinfo(skb)->frag_list)
|
|
|
+ skb_drop_fraglist(skb);
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
- if (offset < len) {
|
|
|
+ for (fragp = &skb_shinfo(skb)->frag_list; (frag = *fragp);
|
|
|
+ fragp = &frag->next) {
|
|
|
+ int end = offset + frag->len;
|
|
|
+
|
|
|
+ if (skb_shared(frag)) {
|
|
|
+ struct sk_buff *nfrag;
|
|
|
+
|
|
|
+ nfrag = skb_clone(frag, GFP_ATOMIC);
|
|
|
+ if (unlikely(!nfrag))
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ nfrag->next = frag->next;
|
|
|
+ frag = nfrag;
|
|
|
+ *fragp = frag;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (end < len) {
|
|
|
+ offset = end;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (end > len &&
|
|
|
+ unlikely((err = pskb_trim(frag, len - offset))))
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (frag->next)
|
|
|
+ skb_drop_list(&frag->next);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (len > skb_headlen(skb)) {
|
|
|
skb->data_len -= skb->len - len;
|
|
|
skb->len = len;
|
|
|
} else {
|
|
|
- if (len <= skb_headlen(skb)) {
|
|
|
- skb->len = len;
|
|
|
- skb->data_len = 0;
|
|
|
- skb->tail = skb->data + len;
|
|
|
- if (skb_shinfo(skb)->frag_list && !skb_cloned(skb))
|
|
|
- skb_drop_fraglist(skb);
|
|
|
- } else {
|
|
|
- skb->data_len -= skb->len - len;
|
|
|
- skb->len = len;
|
|
|
- }
|
|
|
+ skb->len = len;
|
|
|
+ skb->data_len = 0;
|
|
|
+ skb->tail = skb->data + len;
|
|
|
}
|
|
|
|
|
|
return 0;
|