aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2008-12-16 02:43:36 -0500
committerDavid S. Miller <davem@davemloft.net>2008-12-16 02:43:36 -0500
commitbf296b125b21b8d558ceb6ec30bb4eba2730cd6b (patch)
tree5a06714c4001ccf1b217b888121f30d57fceea6d /net
parent71d93b39e52e92aea35f1058d957cf12250d0b75 (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')
-rw-r--r--net/ipv4/af_inet.c2
-rw-r--r--net/ipv4/tcp.c100
-rw-r--r--net/ipv4/tcp_ipv4.c35
3 files changed, 137 insertions, 0 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index a85595307fa7..664ff0ee1c83 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1410,6 +1410,8 @@ static struct net_protocol tcp_protocol = {
1410 .err_handler = tcp_v4_err, 1410 .err_handler = tcp_v4_err,
1411 .gso_send_check = tcp_v4_gso_send_check, 1411 .gso_send_check = tcp_v4_gso_send_check,
1412 .gso_segment = tcp_tso_segment, 1412 .gso_segment = tcp_tso_segment,
1413 .gro_receive = tcp4_gro_receive,
1414 .gro_complete = tcp4_gro_complete,
1413 .no_policy = 1, 1415 .no_policy = 1,
1414 .netns_ok = 1, 1416 .netns_ok = 1,
1415}; 1417};
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}
2466EXPORT_SYMBOL(tcp_tso_segment); 2466EXPORT_SYMBOL(tcp_tso_segment);
2467 2467
2468struct 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
2512found:
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
2537out_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
2545out:
2546 NAPI_GRO_CB(skb)->flush |= flush;
2547
2548 return pp;
2549}
2550
2551int 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
2469static unsigned long tcp_md5sig_users; 2569static unsigned long tcp_md5sig_users;
2470static struct tcp_md5sig_pool **tcp_md5sig_pool; 2570static struct tcp_md5sig_pool **tcp_md5sig_pool;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 26b9030747cc..10172487921b 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2346,6 +2346,41 @@ void tcp4_proc_exit(void)
2346} 2346}
2347#endif /* CONFIG_PROC_FS */ 2347#endif /* CONFIG_PROC_FS */
2348 2348
2349struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb)
2350{
2351 struct iphdr *iph = ip_hdr(skb);
2352
2353 switch (skb->ip_summed) {
2354 case CHECKSUM_COMPLETE:
2355 if (!tcp_v4_check(skb->len, iph->saddr, iph->daddr,
2356 skb->csum)) {
2357 skb->ip_summed = CHECKSUM_UNNECESSARY;
2358 break;
2359 }
2360
2361 /* fall through */
2362 case CHECKSUM_NONE:
2363 NAPI_GRO_CB(skb)->flush = 1;
2364 return NULL;
2365 }
2366
2367 return tcp_gro_receive(head, skb);
2368}
2369EXPORT_SYMBOL(tcp4_gro_receive);
2370
2371int tcp4_gro_complete(struct sk_buff *skb)
2372{
2373 struct iphdr *iph = ip_hdr(skb);
2374 struct tcphdr *th = tcp_hdr(skb);
2375
2376 th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb),
2377 iph->saddr, iph->daddr, 0);
2378 skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
2379
2380 return tcp_gro_complete(skb);
2381}
2382EXPORT_SYMBOL(tcp4_gro_complete);
2383
2349struct proto tcp_prot = { 2384struct proto tcp_prot = {
2350 .name = "TCP", 2385 .name = "TCP",
2351 .owner = THIS_MODULE, 2386 .owner = THIS_MODULE,