diff options
Diffstat (limited to 'net/ipv4/inet_fragment.c')
-rw-r--r-- | net/ipv4/inet_fragment.c | 41 |
1 files changed, 29 insertions, 12 deletions
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index d666756be5f1..10d31733297d 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c | |||
@@ -331,7 +331,7 @@ struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key) | |||
331 | prev = rhashtable_lookup(&fqdir->rhashtable, key, fqdir->f->rhash_params); | 331 | prev = rhashtable_lookup(&fqdir->rhashtable, key, fqdir->f->rhash_params); |
332 | if (!prev) | 332 | if (!prev) |
333 | fq = inet_frag_create(fqdir, key, &prev); | 333 | fq = inet_frag_create(fqdir, key, &prev); |
334 | if (prev && !IS_ERR(prev)) { | 334 | if (!IS_ERR_OR_NULL(prev)) { |
335 | fq = prev; | 335 | fq = prev; |
336 | if (!refcount_inc_not_zero(&fq->refcnt)) | 336 | if (!refcount_inc_not_zero(&fq->refcnt)) |
337 | fq = NULL; | 337 | fq = NULL; |
@@ -475,11 +475,12 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, | |||
475 | EXPORT_SYMBOL(inet_frag_reasm_prepare); | 475 | EXPORT_SYMBOL(inet_frag_reasm_prepare); |
476 | 476 | ||
477 | void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head, | 477 | void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head, |
478 | void *reasm_data) | 478 | void *reasm_data, bool try_coalesce) |
479 | { | 479 | { |
480 | struct sk_buff **nextp = (struct sk_buff **)reasm_data; | 480 | struct sk_buff **nextp = (struct sk_buff **)reasm_data; |
481 | struct rb_node *rbn; | 481 | struct rb_node *rbn; |
482 | struct sk_buff *fp; | 482 | struct sk_buff *fp; |
483 | int sum_truesize; | ||
483 | 484 | ||
484 | skb_push(head, head->data - skb_network_header(head)); | 485 | skb_push(head, head->data - skb_network_header(head)); |
485 | 486 | ||
@@ -487,25 +488,41 @@ void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head, | |||
487 | fp = FRAG_CB(head)->next_frag; | 488 | fp = FRAG_CB(head)->next_frag; |
488 | rbn = rb_next(&head->rbnode); | 489 | rbn = rb_next(&head->rbnode); |
489 | rb_erase(&head->rbnode, &q->rb_fragments); | 490 | rb_erase(&head->rbnode, &q->rb_fragments); |
491 | |||
492 | sum_truesize = head->truesize; | ||
490 | while (rbn || fp) { | 493 | while (rbn || fp) { |
491 | /* fp points to the next sk_buff in the current run; | 494 | /* fp points to the next sk_buff in the current run; |
492 | * rbn points to the next run. | 495 | * rbn points to the next run. |
493 | */ | 496 | */ |
494 | /* Go through the current run. */ | 497 | /* Go through the current run. */ |
495 | while (fp) { | 498 | while (fp) { |
496 | *nextp = fp; | 499 | struct sk_buff *next_frag = FRAG_CB(fp)->next_frag; |
497 | nextp = &fp->next; | 500 | bool stolen; |
498 | fp->prev = NULL; | 501 | int delta; |
499 | memset(&fp->rbnode, 0, sizeof(fp->rbnode)); | 502 | |
500 | fp->sk = NULL; | 503 | sum_truesize += fp->truesize; |
501 | head->data_len += fp->len; | ||
502 | head->len += fp->len; | ||
503 | if (head->ip_summed != fp->ip_summed) | 504 | if (head->ip_summed != fp->ip_summed) |
504 | head->ip_summed = CHECKSUM_NONE; | 505 | head->ip_summed = CHECKSUM_NONE; |
505 | else if (head->ip_summed == CHECKSUM_COMPLETE) | 506 | else if (head->ip_summed == CHECKSUM_COMPLETE) |
506 | head->csum = csum_add(head->csum, fp->csum); | 507 | head->csum = csum_add(head->csum, fp->csum); |
507 | head->truesize += fp->truesize; | 508 | |
508 | fp = FRAG_CB(fp)->next_frag; | 509 | if (try_coalesce && skb_try_coalesce(head, fp, &stolen, |
510 | &delta)) { | ||
511 | kfree_skb_partial(fp, stolen); | ||
512 | } else { | ||
513 | fp->prev = NULL; | ||
514 | memset(&fp->rbnode, 0, sizeof(fp->rbnode)); | ||
515 | fp->sk = NULL; | ||
516 | |||
517 | head->data_len += fp->len; | ||
518 | head->len += fp->len; | ||
519 | head->truesize += fp->truesize; | ||
520 | |||
521 | *nextp = fp; | ||
522 | nextp = &fp->next; | ||
523 | } | ||
524 | |||
525 | fp = next_frag; | ||
509 | } | 526 | } |
510 | /* Move to the next run. */ | 527 | /* Move to the next run. */ |
511 | if (rbn) { | 528 | if (rbn) { |
@@ -516,7 +533,7 @@ void inet_frag_reasm_finish(struct inet_frag_queue *q, struct sk_buff *head, | |||
516 | rbn = rbnext; | 533 | rbn = rbnext; |
517 | } | 534 | } |
518 | } | 535 | } |
519 | sub_frag_mem_limit(q->fqdir, head->truesize); | 536 | sub_frag_mem_limit(q->fqdir, sum_truesize); |
520 | 537 | ||
521 | *nextp = NULL; | 538 | *nextp = NULL; |
522 | skb_mark_not_on_list(head); | 539 | skb_mark_not_on_list(head); |