tcp: Use SKB queue and list helpers instead of doing it by-hand.

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2009-05-28 21:35:47 -07:00
parent de1033428b
commit 915219441d
2 changed files with 90 additions and 45 deletions

View File

@ -439,12 +439,14 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
!tp->urg_data || !tp->urg_data ||
before(tp->urg_seq, tp->copied_seq) || before(tp->urg_seq, tp->copied_seq) ||
!before(tp->urg_seq, tp->rcv_nxt)) { !before(tp->urg_seq, tp->rcv_nxt)) {
struct sk_buff *skb;
answ = tp->rcv_nxt - tp->copied_seq; answ = tp->rcv_nxt - tp->copied_seq;
/* Subtract 1, if FIN is in queue. */ /* Subtract 1, if FIN is in queue. */
if (answ && !skb_queue_empty(&sk->sk_receive_queue)) skb = skb_peek_tail(&sk->sk_receive_queue);
answ -= if (answ && skb)
tcp_hdr((struct sk_buff *)sk->sk_receive_queue.prev)->fin; answ -= tcp_hdr(skb)->fin;
} else } else
answ = tp->urg_seq - tp->copied_seq; answ = tp->urg_seq - tp->copied_seq;
release_sock(sk); release_sock(sk);
@ -1382,11 +1384,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
/* Next get a buffer. */ /* Next get a buffer. */
skb = skb_peek(&sk->sk_receive_queue); skb_queue_walk(&sk->sk_receive_queue, skb) {
do {
if (!skb)
break;
/* Now that we have two receive queues this /* Now that we have two receive queues this
* shouldn't happen. * shouldn't happen.
*/ */
@ -1403,8 +1401,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (tcp_hdr(skb)->fin) if (tcp_hdr(skb)->fin)
goto found_fin_ok; goto found_fin_ok;
WARN_ON(!(flags & MSG_PEEK)); WARN_ON(!(flags & MSG_PEEK));
skb = skb->next; }
} while (skb != (struct sk_buff *)&sk->sk_receive_queue);
/* Well, if we have backlog, try to process it now yet. */ /* Well, if we have backlog, try to process it now yet. */

View File

@ -4426,7 +4426,7 @@ drop:
} }
__skb_queue_head(&tp->out_of_order_queue, skb); __skb_queue_head(&tp->out_of_order_queue, skb);
} else { } else {
struct sk_buff *skb1 = tp->out_of_order_queue.prev; struct sk_buff *skb1 = skb_peek_tail(&tp->out_of_order_queue);
u32 seq = TCP_SKB_CB(skb)->seq; u32 seq = TCP_SKB_CB(skb)->seq;
u32 end_seq = TCP_SKB_CB(skb)->end_seq; u32 end_seq = TCP_SKB_CB(skb)->end_seq;
@ -4443,15 +4443,18 @@ drop:
} }
/* Find place to insert this segment. */ /* Find place to insert this segment. */
do { while (1) {
if (!after(TCP_SKB_CB(skb1)->seq, seq)) if (!after(TCP_SKB_CB(skb1)->seq, seq))
break; break;
} while ((skb1 = skb1->prev) != if (skb_queue_is_first(&tp->out_of_order_queue, skb1)) {
(struct sk_buff *)&tp->out_of_order_queue); skb1 = NULL;
break;
}
skb1 = skb_queue_prev(&tp->out_of_order_queue, skb1);
}
/* Do skb overlap to previous one? */ /* Do skb overlap to previous one? */
if (skb1 != (struct sk_buff *)&tp->out_of_order_queue && if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) {
before(seq, TCP_SKB_CB(skb1)->end_seq)) {
if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
/* All the bits are present. Drop. */ /* All the bits are present. Drop. */
__kfree_skb(skb); __kfree_skb(skb);
@ -4463,24 +4466,41 @@ drop:
tcp_dsack_set(sk, seq, tcp_dsack_set(sk, seq,
TCP_SKB_CB(skb1)->end_seq); TCP_SKB_CB(skb1)->end_seq);
} else { } else {
skb1 = skb1->prev; if (skb_queue_is_first(&tp->out_of_order_queue,
skb1))
skb1 = NULL;
else
skb1 = skb_queue_prev(
&tp->out_of_order_queue,
skb1);
} }
} }
__skb_queue_after(&tp->out_of_order_queue, skb1, skb); if (!skb1)
__skb_queue_head(&tp->out_of_order_queue, skb);
else
__skb_queue_after(&tp->out_of_order_queue, skb1, skb);
/* And clean segments covered by new one as whole. */ /* And clean segments covered by new one as whole. */
while ((skb1 = skb->next) != if (skb1 && !skb_queue_is_last(&tp->out_of_order_queue, skb1)) {
(struct sk_buff *)&tp->out_of_order_queue && struct sk_buff *n;
after(end_seq, TCP_SKB_CB(skb1)->seq)) {
if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) { skb1 = skb_queue_next(&tp->out_of_order_queue, skb1);
skb_queue_walk_from_safe(&tp->out_of_order_queue,
skb1, n) {
if (!after(end_seq, TCP_SKB_CB(skb1)->seq))
break;
if (before(end_seq,
TCP_SKB_CB(skb1)->end_seq)) {
tcp_dsack_extend(sk,
TCP_SKB_CB(skb1)->seq,
end_seq);
break;
}
__skb_unlink(skb1, &tp->out_of_order_queue);
tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
end_seq); TCP_SKB_CB(skb1)->end_seq);
break; __kfree_skb(skb1);
} }
__skb_unlink(skb1, &tp->out_of_order_queue);
tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
TCP_SKB_CB(skb1)->end_seq);
__kfree_skb(skb1);
} }
add_sack: add_sack:
@ -4492,7 +4512,10 @@ add_sack:
static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb, static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb,
struct sk_buff_head *list) struct sk_buff_head *list)
{ {
struct sk_buff *next = skb->next; struct sk_buff *next = NULL;
if (!skb_queue_is_last(list, skb))
next = skb_queue_next(list, skb);
__skb_unlink(skb, list); __skb_unlink(skb, list);
__kfree_skb(skb); __kfree_skb(skb);
@ -4503,6 +4526,9 @@ static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb,
/* Collapse contiguous sequence of skbs head..tail with /* Collapse contiguous sequence of skbs head..tail with
* sequence numbers start..end. * sequence numbers start..end.
*
* If tail is NULL, this means until the end of the list.
*
* Segments with FIN/SYN are not collapsed (only because this * Segments with FIN/SYN are not collapsed (only because this
* simplifies code) * simplifies code)
*/ */
@ -4511,15 +4537,23 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list,
struct sk_buff *head, struct sk_buff *tail, struct sk_buff *head, struct sk_buff *tail,
u32 start, u32 end) u32 start, u32 end)
{ {
struct sk_buff *skb; struct sk_buff *skb, *n;
bool end_of_skbs;
/* First, check that queue is collapsible and find /* First, check that queue is collapsible and find
* the point where collapsing can be useful. */ * the point where collapsing can be useful. */
for (skb = head; skb != tail;) { skb = head;
restart:
end_of_skbs = true;
skb_queue_walk_from_safe(list, skb, n) {
if (skb == tail)
break;
/* No new bits? It is possible on ofo queue. */ /* No new bits? It is possible on ofo queue. */
if (!before(start, TCP_SKB_CB(skb)->end_seq)) { if (!before(start, TCP_SKB_CB(skb)->end_seq)) {
skb = tcp_collapse_one(sk, skb, list); skb = tcp_collapse_one(sk, skb, list);
continue; if (!skb)
break;
goto restart;
} }
/* The first skb to collapse is: /* The first skb to collapse is:
@ -4529,16 +4563,24 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list,
*/ */
if (!tcp_hdr(skb)->syn && !tcp_hdr(skb)->fin && if (!tcp_hdr(skb)->syn && !tcp_hdr(skb)->fin &&
(tcp_win_from_space(skb->truesize) > skb->len || (tcp_win_from_space(skb->truesize) > skb->len ||
before(TCP_SKB_CB(skb)->seq, start) || before(TCP_SKB_CB(skb)->seq, start))) {
(skb->next != tail && end_of_skbs = false;
TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb->next)->seq)))
break; break;
}
if (!skb_queue_is_last(list, skb)) {
struct sk_buff *next = skb_queue_next(list, skb);
if (next != tail &&
TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(next)->seq) {
end_of_skbs = false;
break;
}
}
/* Decided to skip this, advance start seq. */ /* Decided to skip this, advance start seq. */
start = TCP_SKB_CB(skb)->end_seq; start = TCP_SKB_CB(skb)->end_seq;
skb = skb->next;
} }
if (skb == tail || tcp_hdr(skb)->syn || tcp_hdr(skb)->fin) if (end_of_skbs || tcp_hdr(skb)->syn || tcp_hdr(skb)->fin)
return; return;
while (before(start, end)) { while (before(start, end)) {
@ -4583,7 +4625,8 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list,
} }
if (!before(start, TCP_SKB_CB(skb)->end_seq)) { if (!before(start, TCP_SKB_CB(skb)->end_seq)) {
skb = tcp_collapse_one(sk, skb, list); skb = tcp_collapse_one(sk, skb, list);
if (skb == tail || if (!skb ||
skb == tail ||
tcp_hdr(skb)->syn || tcp_hdr(skb)->syn ||
tcp_hdr(skb)->fin) tcp_hdr(skb)->fin)
return; return;
@ -4610,17 +4653,21 @@ static void tcp_collapse_ofo_queue(struct sock *sk)
head = skb; head = skb;
for (;;) { for (;;) {
skb = skb->next; struct sk_buff *next = NULL;
if (!skb_queue_is_last(&tp->out_of_order_queue, skb))
next = skb_queue_next(&tp->out_of_order_queue, skb);
skb = next;
/* Segment is terminated when we see gap or when /* Segment is terminated when we see gap or when
* we are at the end of all the queue. */ * we are at the end of all the queue. */
if (skb == (struct sk_buff *)&tp->out_of_order_queue || if (!skb ||
after(TCP_SKB_CB(skb)->seq, end) || after(TCP_SKB_CB(skb)->seq, end) ||
before(TCP_SKB_CB(skb)->end_seq, start)) { before(TCP_SKB_CB(skb)->end_seq, start)) {
tcp_collapse(sk, &tp->out_of_order_queue, tcp_collapse(sk, &tp->out_of_order_queue,
head, skb, start, end); head, skb, start, end);
head = skb; head = skb;
if (skb == (struct sk_buff *)&tp->out_of_order_queue) if (!skb)
break; break;
/* Start new segment */ /* Start new segment */
start = TCP_SKB_CB(skb)->seq; start = TCP_SKB_CB(skb)->seq;
@ -4681,10 +4728,11 @@ static int tcp_prune_queue(struct sock *sk)
tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss); tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss);
tcp_collapse_ofo_queue(sk); tcp_collapse_ofo_queue(sk);
tcp_collapse(sk, &sk->sk_receive_queue, if (!skb_queue_empty(&sk->sk_receive_queue))
sk->sk_receive_queue.next, tcp_collapse(sk, &sk->sk_receive_queue,
(struct sk_buff *)&sk->sk_receive_queue, skb_peek(&sk->sk_receive_queue),
tp->copied_seq, tp->rcv_nxt); NULL,
tp->copied_seq, tp->rcv_nxt);
sk_mem_reclaim(sk); sk_mem_reclaim(sk);
if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)