diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2012-03-18 07:06:44 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-03-19 16:53:07 -0400 |
commit | e86b291962cbf477e35d983d312428cf737bc0f8 (patch) | |
tree | 11520ec988be8644df08684004512c35881e21b5 /net/ipv4/tcp_input.c | |
parent | de1288041d01120559d53ebd98e0f92476ee56d3 (diff) |
tcp: introduce tcp_data_queue_ofo
Split tcp_data_queue() in two parts for better readability.
tcp_data_queue_ofo() is responsible for queueing incoming skb into out
of order queue.
Change code layout so that the skb_set_owner_r() is performed only if
skb is not dropped.
This is a preliminary patch before "reduce out_of_order memory use"
following patch.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: H.K. Jerry Chu <hkchu@google.com>
Cc: Tom Herbert <therbert@google.com>
Cc: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Acked-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_input.c')
-rw-r--r-- | net/ipv4/tcp_input.c | 214 |
1 files changed, 115 insertions, 99 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 68d4057cba00..fa7de12c4a52 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c | |||
@@ -4446,6 +4446,120 @@ static inline int tcp_try_rmem_schedule(struct sock *sk, unsigned int size) | |||
4446 | return 0; | 4446 | return 0; |
4447 | } | 4447 | } |
4448 | 4448 | ||
4449 | static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) | ||
4450 | { | ||
4451 | struct tcp_sock *tp = tcp_sk(sk); | ||
4452 | struct sk_buff *skb1; | ||
4453 | u32 seq, end_seq; | ||
4454 | |||
4455 | TCP_ECN_check_ce(tp, skb); | ||
4456 | |||
4457 | if (tcp_try_rmem_schedule(sk, skb->truesize)) { | ||
4458 | /* TODO: should increment a counter */ | ||
4459 | __kfree_skb(skb); | ||
4460 | return; | ||
4461 | } | ||
4462 | |||
4463 | /* Disable header prediction. */ | ||
4464 | tp->pred_flags = 0; | ||
4465 | inet_csk_schedule_ack(sk); | ||
4466 | |||
4467 | SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n", | ||
4468 | tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq); | ||
4469 | |||
4470 | skb1 = skb_peek_tail(&tp->out_of_order_queue); | ||
4471 | if (!skb1) { | ||
4472 | /* Initial out of order segment, build 1 SACK. */ | ||
4473 | if (tcp_is_sack(tp)) { | ||
4474 | tp->rx_opt.num_sacks = 1; | ||
4475 | tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq; | ||
4476 | tp->selective_acks[0].end_seq = | ||
4477 | TCP_SKB_CB(skb)->end_seq; | ||
4478 | } | ||
4479 | __skb_queue_head(&tp->out_of_order_queue, skb); | ||
4480 | goto end; | ||
4481 | } | ||
4482 | |||
4483 | seq = TCP_SKB_CB(skb)->seq; | ||
4484 | end_seq = TCP_SKB_CB(skb)->end_seq; | ||
4485 | |||
4486 | if (seq == TCP_SKB_CB(skb1)->end_seq) { | ||
4487 | __skb_queue_after(&tp->out_of_order_queue, skb1, skb); | ||
4488 | |||
4489 | if (!tp->rx_opt.num_sacks || | ||
4490 | tp->selective_acks[0].end_seq != seq) | ||
4491 | goto add_sack; | ||
4492 | |||
4493 | /* Common case: data arrive in order after hole. */ | ||
4494 | tp->selective_acks[0].end_seq = end_seq; | ||
4495 | goto end; | ||
4496 | } | ||
4497 | |||
4498 | /* Find place to insert this segment. */ | ||
4499 | while (1) { | ||
4500 | if (!after(TCP_SKB_CB(skb1)->seq, seq)) | ||
4501 | break; | ||
4502 | if (skb_queue_is_first(&tp->out_of_order_queue, skb1)) { | ||
4503 | skb1 = NULL; | ||
4504 | break; | ||
4505 | } | ||
4506 | skb1 = skb_queue_prev(&tp->out_of_order_queue, skb1); | ||
4507 | } | ||
4508 | |||
4509 | /* Do skb overlap to previous one? */ | ||
4510 | if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { | ||
4511 | if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { | ||
4512 | /* All the bits are present. Drop. */ | ||
4513 | __kfree_skb(skb); | ||
4514 | skb = NULL; | ||
4515 | tcp_dsack_set(sk, seq, end_seq); | ||
4516 | goto add_sack; | ||
4517 | } | ||
4518 | if (after(seq, TCP_SKB_CB(skb1)->seq)) { | ||
4519 | /* Partial overlap. */ | ||
4520 | tcp_dsack_set(sk, seq, | ||
4521 | TCP_SKB_CB(skb1)->end_seq); | ||
4522 | } else { | ||
4523 | if (skb_queue_is_first(&tp->out_of_order_queue, | ||
4524 | skb1)) | ||
4525 | skb1 = NULL; | ||
4526 | else | ||
4527 | skb1 = skb_queue_prev( | ||
4528 | &tp->out_of_order_queue, | ||
4529 | skb1); | ||
4530 | } | ||
4531 | } | ||
4532 | if (!skb1) | ||
4533 | __skb_queue_head(&tp->out_of_order_queue, skb); | ||
4534 | else | ||
4535 | __skb_queue_after(&tp->out_of_order_queue, skb1, skb); | ||
4536 | |||
4537 | /* And clean segments covered by new one as whole. */ | ||
4538 | while (!skb_queue_is_last(&tp->out_of_order_queue, skb)) { | ||
4539 | skb1 = skb_queue_next(&tp->out_of_order_queue, skb); | ||
4540 | |||
4541 | if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) | ||
4542 | break; | ||
4543 | if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) { | ||
4544 | tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, | ||
4545 | end_seq); | ||
4546 | break; | ||
4547 | } | ||
4548 | __skb_unlink(skb1, &tp->out_of_order_queue); | ||
4549 | tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, | ||
4550 | TCP_SKB_CB(skb1)->end_seq); | ||
4551 | __kfree_skb(skb1); | ||
4552 | } | ||
4553 | |||
4554 | add_sack: | ||
4555 | if (tcp_is_sack(tp)) | ||
4556 | tcp_sack_new_ofo_skb(sk, seq, end_seq); | ||
4557 | end: | ||
4558 | if (skb) | ||
4559 | skb_set_owner_r(skb, sk); | ||
4560 | } | ||
4561 | |||
4562 | |||
4449 | static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) | 4563 | static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) |
4450 | { | 4564 | { |
4451 | const struct tcphdr *th = tcp_hdr(skb); | 4565 | const struct tcphdr *th = tcp_hdr(skb); |
@@ -4561,105 +4675,7 @@ drop: | |||
4561 | goto queue_and_out; | 4675 | goto queue_and_out; |
4562 | } | 4676 | } |
4563 | 4677 | ||
4564 | TCP_ECN_check_ce(tp, skb); | 4678 | tcp_data_queue_ofo(sk, skb); |
4565 | |||
4566 | if (tcp_try_rmem_schedule(sk, skb->truesize)) | ||
4567 | goto drop; | ||
4568 | |||
4569 | /* Disable header prediction. */ | ||
4570 | tp->pred_flags = 0; | ||
4571 | inet_csk_schedule_ack(sk); | ||
4572 | |||
4573 | SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n", | ||
4574 | tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq); | ||
4575 | |||
4576 | skb_set_owner_r(skb, sk); | ||
4577 | |||
4578 | if (!skb_peek(&tp->out_of_order_queue)) { | ||
4579 | /* Initial out of order segment, build 1 SACK. */ | ||
4580 | if (tcp_is_sack(tp)) { | ||
4581 | tp->rx_opt.num_sacks = 1; | ||
4582 | tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq; | ||
4583 | tp->selective_acks[0].end_seq = | ||
4584 | TCP_SKB_CB(skb)->end_seq; | ||
4585 | } | ||
4586 | __skb_queue_head(&tp->out_of_order_queue, skb); | ||
4587 | } else { | ||
4588 | struct sk_buff *skb1 = skb_peek_tail(&tp->out_of_order_queue); | ||
4589 | u32 seq = TCP_SKB_CB(skb)->seq; | ||
4590 | u32 end_seq = TCP_SKB_CB(skb)->end_seq; | ||
4591 | |||
4592 | if (seq == TCP_SKB_CB(skb1)->end_seq) { | ||
4593 | __skb_queue_after(&tp->out_of_order_queue, skb1, skb); | ||
4594 | |||
4595 | if (!tp->rx_opt.num_sacks || | ||
4596 | tp->selective_acks[0].end_seq != seq) | ||
4597 | goto add_sack; | ||
4598 | |||
4599 | /* Common case: data arrive in order after hole. */ | ||
4600 | tp->selective_acks[0].end_seq = end_seq; | ||
4601 | return; | ||
4602 | } | ||
4603 | |||
4604 | /* Find place to insert this segment. */ | ||
4605 | while (1) { | ||
4606 | if (!after(TCP_SKB_CB(skb1)->seq, seq)) | ||
4607 | break; | ||
4608 | if (skb_queue_is_first(&tp->out_of_order_queue, skb1)) { | ||
4609 | skb1 = NULL; | ||
4610 | break; | ||
4611 | } | ||
4612 | skb1 = skb_queue_prev(&tp->out_of_order_queue, skb1); | ||
4613 | } | ||
4614 | |||
4615 | /* Do skb overlap to previous one? */ | ||
4616 | if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { | ||
4617 | if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { | ||
4618 | /* All the bits are present. Drop. */ | ||
4619 | __kfree_skb(skb); | ||
4620 | tcp_dsack_set(sk, seq, end_seq); | ||
4621 | goto add_sack; | ||
4622 | } | ||
4623 | if (after(seq, TCP_SKB_CB(skb1)->seq)) { | ||
4624 | /* Partial overlap. */ | ||
4625 | tcp_dsack_set(sk, seq, | ||
4626 | TCP_SKB_CB(skb1)->end_seq); | ||
4627 | } else { | ||
4628 | if (skb_queue_is_first(&tp->out_of_order_queue, | ||
4629 | skb1)) | ||
4630 | skb1 = NULL; | ||
4631 | else | ||
4632 | skb1 = skb_queue_prev( | ||
4633 | &tp->out_of_order_queue, | ||
4634 | skb1); | ||
4635 | } | ||
4636 | } | ||
4637 | if (!skb1) | ||
4638 | __skb_queue_head(&tp->out_of_order_queue, skb); | ||
4639 | else | ||
4640 | __skb_queue_after(&tp->out_of_order_queue, skb1, skb); | ||
4641 | |||
4642 | /* And clean segments covered by new one as whole. */ | ||
4643 | while (!skb_queue_is_last(&tp->out_of_order_queue, skb)) { | ||
4644 | skb1 = skb_queue_next(&tp->out_of_order_queue, skb); | ||
4645 | |||
4646 | if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) | ||
4647 | break; | ||
4648 | if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) { | ||
4649 | tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, | ||
4650 | end_seq); | ||
4651 | break; | ||
4652 | } | ||
4653 | __skb_unlink(skb1, &tp->out_of_order_queue); | ||
4654 | tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, | ||
4655 | TCP_SKB_CB(skb1)->end_seq); | ||
4656 | __kfree_skb(skb1); | ||
4657 | } | ||
4658 | |||
4659 | add_sack: | ||
4660 | if (tcp_is_sack(tp)) | ||
4661 | tcp_sack_new_ofo_skb(sk, seq, end_seq); | ||
4662 | } | ||
4663 | } | 4679 | } |
4664 | 4680 | ||
4665 | static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb, | 4681 | static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb, |