diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2008-12-16 02:43:36 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-12-16 02:43:36 -0500 |
commit | bf296b125b21b8d558ceb6ec30bb4eba2730cd6b (patch) | |
tree | 5a06714c4001ccf1b217b888121f30d57fceea6d /net/ipv4/tcp.c | |
parent | 71d93b39e52e92aea35f1058d957cf12250d0b75 (diff) |
tcp: Add GRO support
This patch adds the TCP-specific portion of GRO. The criterion for
merging is extremely strict (the TCP header must match exactly apart
from the checksum) so as to allow refragmentation. Otherwise this
is pretty much identical to LRO, except that we support the merging
of ECN packets.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r-- | net/ipv4/tcp.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 019243408623..1f3d52946b3b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -2465,6 +2465,106 @@ out: | |||
2465 | } | 2465 | } |
2466 | EXPORT_SYMBOL(tcp_tso_segment); | 2466 | EXPORT_SYMBOL(tcp_tso_segment); |
2467 | 2467 | ||
2468 | struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb) | ||
2469 | { | ||
2470 | struct sk_buff **pp = NULL; | ||
2471 | struct sk_buff *p; | ||
2472 | struct tcphdr *th; | ||
2473 | struct tcphdr *th2; | ||
2474 | unsigned int thlen; | ||
2475 | unsigned int flags; | ||
2476 | unsigned int total; | ||
2477 | unsigned int mss = 1; | ||
2478 | int flush = 1; | ||
2479 | |||
2480 | if (!pskb_may_pull(skb, sizeof(*th))) | ||
2481 | goto out; | ||
2482 | |||
2483 | th = tcp_hdr(skb); | ||
2484 | thlen = th->doff * 4; | ||
2485 | if (thlen < sizeof(*th)) | ||
2486 | goto out; | ||
2487 | |||
2488 | if (!pskb_may_pull(skb, thlen)) | ||
2489 | goto out; | ||
2490 | |||
2491 | th = tcp_hdr(skb); | ||
2492 | __skb_pull(skb, thlen); | ||
2493 | |||
2494 | flags = tcp_flag_word(th); | ||
2495 | |||
2496 | for (; (p = *head); head = &p->next) { | ||
2497 | if (!NAPI_GRO_CB(p)->same_flow) | ||
2498 | continue; | ||
2499 | |||
2500 | th2 = tcp_hdr(p); | ||
2501 | |||
2502 | if (th->source != th2->source || th->dest != th2->dest) { | ||
2503 | NAPI_GRO_CB(p)->same_flow = 0; | ||
2504 | continue; | ||
2505 | } | ||
2506 | |||
2507 | goto found; | ||
2508 | } | ||
2509 | |||
2510 | goto out_check_final; | ||
2511 | |||
2512 | found: | ||
2513 | flush = NAPI_GRO_CB(p)->flush; | ||
2514 | flush |= flags & TCP_FLAG_CWR; | ||
2515 | flush |= (flags ^ tcp_flag_word(th2)) & | ||
2516 | ~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH); | ||
2517 | flush |= th->ack_seq != th2->ack_seq || th->window != th2->window; | ||
2518 | flush |= memcmp(th + 1, th2 + 1, thlen - sizeof(*th)); | ||
2519 | |||
2520 | total = p->len; | ||
2521 | mss = total; | ||
2522 | if (skb_shinfo(p)->frag_list) | ||
2523 | mss = skb_shinfo(p)->frag_list->len; | ||
2524 | |||
2525 | flush |= skb->len > mss || skb->len <= 0; | ||
2526 | flush |= ntohl(th2->seq) + total != ntohl(th->seq); | ||
2527 | |||
2528 | if (flush || skb_gro_receive(head, skb)) { | ||
2529 | mss = 1; | ||
2530 | goto out_check_final; | ||
2531 | } | ||
2532 | |||
2533 | p = *head; | ||
2534 | th2 = tcp_hdr(p); | ||
2535 | tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH); | ||
2536 | |||
2537 | out_check_final: | ||
2538 | flush = skb->len < mss; | ||
2539 | flush |= flags & (TCP_FLAG_URG | TCP_FLAG_PSH | TCP_FLAG_RST | | ||
2540 | TCP_FLAG_SYN | TCP_FLAG_FIN); | ||
2541 | |||
2542 | if (p && (!NAPI_GRO_CB(skb)->same_flow || flush)) | ||
2543 | pp = head; | ||
2544 | |||
2545 | out: | ||
2546 | NAPI_GRO_CB(skb)->flush |= flush; | ||
2547 | |||
2548 | return pp; | ||
2549 | } | ||
2550 | |||
2551 | int tcp_gro_complete(struct sk_buff *skb) | ||
2552 | { | ||
2553 | struct tcphdr *th = tcp_hdr(skb); | ||
2554 | |||
2555 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
2556 | skb->csum_offset = offsetof(struct tcphdr, check); | ||
2557 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
2558 | |||
2559 | skb_shinfo(skb)->gso_size = skb_shinfo(skb)->frag_list->len; | ||
2560 | skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; | ||
2561 | |||
2562 | if (th->cwr) | ||
2563 | skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; | ||
2564 | |||
2565 | return 0; | ||
2566 | } | ||
2567 | |||
2468 | #ifdef CONFIG_TCP_MD5SIG | 2568 | #ifdef CONFIG_TCP_MD5SIG |
2469 | static unsigned long tcp_md5sig_users; | 2569 | static unsigned long tcp_md5sig_users; |
2470 | static struct tcp_md5sig_pool **tcp_md5sig_pool; | 2570 | static struct tcp_md5sig_pool **tcp_md5sig_pool; |