David Miller's rbtree patches for 2.6.22.6
Subsystems (3)
cover David Miller's rbtree patches for 2.6.22.6
Hello,
I've been experimenting with David Miller's red-black tree patch for SACK processing. We're sending TCP traffic between two machines with 10Gbps cards over a 1Gbps bottleneck link and were getting very high CPU load with large windows. With a few tweaks, this patch seems to provide a pretty substantial improvement. David: this seems like excellent work so far.
Here are a couple of patches against 2.6.22.6. The first one is just David's patches tweaked for 2.6.22.6, with a couple of minor bugfixes to get it to compile and not crash. (I also changed __tcp_insert_write_queue_tail() to set the fack_count of the new packet to the fack_count of the tail plus the packet count of the tail, not the packet count of the new skb, because I think that's how it was intended to be. Right?
In the second patch there are a couple of significant changes. One is (as Baruch suggested) to modify the existing SACK fast path so that we don't tag packets we've already tagged when we advance by a packet.
The other issue is that the cached fack_counts seem to be wrong, because they're set when we insert into the queue, but tcp_set_tso_segs() is called later, just before we send, so all the fack_counts are zero. My solution was to set the fack_count when we advance the send_head. Also I changed tcp_reset_fack_counts() so that it exits when it hits an skb whose tcp_skb_pcount() is zero or whose fack_count is already correct. (This really helps when TSO is on, since there's lots of inserting into the middle of the queue.)
Please let me know how I can help get this tested and debugged. Reducing the SACK processing load is really going to be essential for us to start testing experimental TCP variants with large windows.
Thanks
-Tom
PATCH 1/2 David Miller's rbtree patches for 2.6.22.6
Patch 1: David Miller's red-black tree code, tweaked for 2.6.22.6, with some bugfixes
-Tom
---
include/linux/skbuff.h +3 -0
@@ -18,6 +18,7 @@#include <linux/compiler.h> #include <linux/time.h> #include <linux/cache.h> +#include <linux/rbtree.h> #include <asm/atomic.h> #include <asm/types.h>
@@ -242,6 +243,8 @@struct sk_buff *next;struct sk_buff *prev; +struct rb_node rb; +struct sock *sk;ktime_t tstamp;struct net_device *dev;
include/linux/tcp.h +2 -2
@@ -174,6 +174,7 @@#include <linux/skbuff.h> #include <linux/dmaengine.h> +#include <linux/rbtree.h> #include <net/sock.h> #include <net/inet_connection_sock.h> #include <net/inet_timewait_sock.h>
@@ -321,6 +322,7 @@u32 snd_cwnd_used;u32 snd_cwnd_stamp; +struct rb_root write_queue_rb;struct sk_buff_head out_of_order_queue;/* Out of order segments go here */ u32 rcv_wnd;/* Current receiver window */
@@ -339,9 +341,7 @@struct sk_buff *scoreboard_skb_hint;struct sk_buff *retransmit_skb_hint;struct sk_buff *forward_skb_hint; -struct sk_buff *fastpath_skb_hint; -int fastpath_cnt_hint;int lost_cnt_hint;int retransmit_cnt_hint;int forward_cnt_hint;
include/net/tcp.h +89 -4
157 lines not on this page · raw message
net/ipv4/tcp_input.c +6 -20
@@ -947,14 +947,13 @@unsigned char *ptr = (skb_transport_header (ack_skb) +TCP_SKB_CB (ack_skb)->sacked);struct tcp_sack_block_wire *sp = (struct tcp_sack_block_wire *)(ptr+2 ); -struct sk_buff *cached_skb;int num_sacks = (ptr[1 ] -TCPOLEN_SACK_BASE )>>3 ;int reord = tp->packets_out;int prior_fackets;u32 lost_retrans =0 ;int flag =0 ;int found_dup_sack =0 ; -int cached_fack_count; +int fack_count_base;int i;int first_sack_index;
@@ -1020,7 +1019,6 @@ num_sacks =1 ; else {int j; - tp->fastpath_skb_hint =NULL ;/* order SACK blocks to allow in order walk of the retrans queue */ for (i = num_sacks-1 ; i >0 ; i--) {
@@ -1045,14 +1043,7 @@/* clear flag as used for different purpose in following code */ flag =0 ; -/* Use SACK fastpath hint if valid */ - cached_skb = tp->fastpath_skb_hint; - cached_fack_count = tp->fastpath_cnt_hint; -if (!cached_skb) { - cached_skb =tcp_write_queue_head (sk); - cached_fack_count =0 ; - } - + fack_count_base =TCP_SKB_CB (tcp_write_queue_head (sk))->fack_count;for (i=0 ; i<num_sacks; i++, sp++) {struct sk_buff *skb;__u32 start_seq =ntohl (sp->start_seq);
@@ -1060,8 +1051,10 @@int fack_count;int dup_sack = (found_dup_sack && (i == first_sack_index)); - skb = cached_skb; - fack_count = cached_fack_count; + skb =tcp_write_queue_find (sk, start_seq); +if (!skb) +continue ; + fack_count =TCP_SKB_CB (skb)->fack_count - fack_count_base;/* Event "B" in the comment above. */ if (after (end_seq, tp->high_seq))
@@ -1074,13 +1067,6 @@if (skb ==tcp_send_head (sk))break ; - cached_skb = skb; - cached_fack_count = fack_count; -if (i == first_sack_index) { - tp->fastpath_skb_hint = skb; - tp->fastpath_cnt_hint = fack_count; - } -/* The retransmission queue is always in order, so * we can short-circuit the walk early. */
net/ipv4/tcp_ipv4.c +1 -0
@@ -1845,6 +1845,7 @@struct inet_connection_sock *icsk =inet_csk (sk);struct tcp_sock *tp =tcp_sk (sk); + tp->write_queue_rb =RB_ROOT ;skb_queue_head_init (&tp->out_of_order_queue);tcp_init_xmit_timers (sk);tcp_prequeue_init (tp);
net/ipv4/tcp_minisocks.c +1 -0
@@ -421,6 +421,7 @@tcp_set_ca_state (newsk, TCP_CA_Open);tcp_init_xmit_timers (newsk); + newtp->write_queue_rb =RB_ROOT ;skb_queue_head_init (&newtp->out_of_order_queue); newtp->write_seq = treq->snt_isn +1 ; newtp->pushed_seq = newtp->write_seq;
net/ipv4/tcp_output.c +2 -2
@@ -1278,11 +1278,11 @@sk_charge_skb (sk, nskb); skb =tcp_send_head (sk); +TCP_SKB_CB (nskb)->seq =TCP_SKB_CB (skb)->seq; +TCP_SKB_CB (nskb)->end_seq =TCP_SKB_CB (skb)->seq + probe_size;tcp_insert_write_queue_before (nskb, skb, sk);tcp_advance_send_head (sk, skb); -TCP_SKB_CB (nskb)->seq =TCP_SKB_CB (skb)->seq; -TCP_SKB_CB (nskb)->end_seq =TCP_SKB_CB (skb)->seq + probe_size;TCP_SKB_CB (nskb)->flags =TCPCB_FLAG_ACK ;TCP_SKB_CB (nskb)->sacked =0 ; nskb->csum =0 ;
net/ipv6/tcp_ipv6.c +1 -0
@@ -1900,6 +1900,7 @@struct inet_connection_sock *icsk =inet_csk (sk);struct tcp_sock *tp =tcp_sk (sk); + tp->write_queue_rb =RB_ROOT ;skb_queue_head_init (&tp->out_of_order_queue);tcp_init_xmit_timers (sk);tcp_prequeue_init (tp);
PATCH 1/2 David Miller's rbtree patches for 2.6.22.6
Quoted 2 lines
Patch 1: David Miller's red-black tree code, tweaked for 2.6.22.6, with some bugfixes
oops...
The original patch can cause a kernel panic if tcp_sacktag_write_queue is called when the write queue is empty. Use this one instead.
-Tom
---
include/linux/skbuff.h +3 -0
@@ -18,6 +18,7 @@#include <linux/compiler.h> #include <linux/time.h> #include <linux/cache.h> +#include <linux/rbtree.h> #include <asm/atomic.h> #include <asm/types.h>
@@ -242,6 +243,8 @@struct sk_buff *next;struct sk_buff *prev; +struct rb_node rb; +struct sock *sk;ktime_t tstamp;struct net_device *dev;
include/linux/tcp.h +2 -2
@@ -174,6 +174,7 @@#include <linux/skbuff.h> #include <linux/dmaengine.h> +#include <linux/rbtree.h> #include <net/sock.h> #include <net/inet_connection_sock.h> #include <net/inet_timewait_sock.h>
@@ -321,6 +322,7 @@u32 snd_cwnd_used;u32 snd_cwnd_stamp; +struct rb_root write_queue_rb;struct sk_buff_head out_of_order_queue;/* Out of order segments go here */ u32 rcv_wnd;/* Current receiver window */
@@ -339,9 +341,7 @@struct sk_buff *scoreboard_skb_hint;struct sk_buff *retransmit_skb_hint;struct sk_buff *forward_skb_hint; -struct sk_buff *fastpath_skb_hint; -int fastpath_cnt_hint;int lost_cnt_hint;int retransmit_cnt_hint;int forward_cnt_hint;
include/net/tcp.h +89 -4
157 lines not on this page · raw message
net/ipv4/tcp_input.c +7 -19
@@ -947,14 +947,13 @@unsigned char *ptr = (skb_transport_header (ack_skb) +TCP_SKB_CB (ack_skb)->sacked);struct tcp_sack_block_wire *sp = (struct tcp_sack_block_wire *)(ptr+2 ); -struct sk_buff *cached_skb;int num_sacks = (ptr[1 ] -TCPOLEN_SACK_BASE )>>3 ;int reord = tp->packets_out;int prior_fackets;u32 lost_retrans =0 ;int flag =0 ;int found_dup_sack =0 ; -int cached_fack_count; +int fack_count_base;int i;int first_sack_index;
@@ -1020,7 +1019,6 @@ num_sacks =1 ; else {int j; - tp->fastpath_skb_hint =NULL ;/* order SACK blocks to allow in order walk of the retrans queue */ for (i = num_sacks-1 ; i >0 ; i--) {
@@ -1045,13 +1043,8 @@/* clear flag as used for different purpose in following code */ flag =0 ; -/* Use SACK fastpath hint if valid */ - cached_skb = tp->fastpath_skb_hint; - cached_fack_count = tp->fastpath_cnt_hint; -if (!cached_skb) { - cached_skb =tcp_write_queue_head (sk); - cached_fack_count =0 ; - } +if (likely (tcp_write_queue_head (sk))) + fack_count_base =TCP_SKB_CB (tcp_write_queue_head (sk))->fack_count;for (i=0 ; i<num_sacks; i++, sp++) {struct sk_buff *skb;
@@ -1060,8 +1053,10 @@int fack_count;int dup_sack = (found_dup_sack && (i == first_sack_index)); - skb = cached_skb; - fack_count = cached_fack_count; + skb =tcp_write_queue_find (sk, start_seq); +if (!skb) +continue ; + fack_count =TCP_SKB_CB (skb)->fack_count - fack_count_base;/* Event "B" in the comment above. */ if (after (end_seq, tp->high_seq))
@@ -1074,13 +1069,6 @@if (skb ==tcp_send_head (sk))break ; - cached_skb = skb; - cached_fack_count = fack_count; -if (i == first_sack_index) { - tp->fastpath_skb_hint = skb; - tp->fastpath_cnt_hint = fack_count; - } -/* The retransmission queue is always in order, so * we can short-circuit the walk early. */
net/ipv4/tcp_ipv4.c +1 -0
@@ -1845,6 +1845,7 @@struct inet_connection_sock *icsk =inet_csk (sk);struct tcp_sock *tp =tcp_sk (sk); + tp->write_queue_rb =RB_ROOT ;skb_queue_head_init (&tp->out_of_order_queue);tcp_init_xmit_timers (sk);tcp_prequeue_init (tp);
net/ipv4/tcp_minisocks.c +1 -0
@@ -421,6 +421,7 @@tcp_set_ca_state (newsk, TCP_CA_Open);tcp_init_xmit_timers (newsk); + newtp->write_queue_rb =RB_ROOT ;skb_queue_head_init (&newtp->out_of_order_queue); newtp->write_seq = treq->snt_isn +1 ; newtp->pushed_seq = newtp->write_seq;
net/ipv4/tcp_output.c +2 -2
@@ -1278,11 +1278,11 @@sk_charge_skb (sk, nskb); skb =tcp_send_head (sk); +TCP_SKB_CB (nskb)->seq =TCP_SKB_CB (skb)->seq; +TCP_SKB_CB (nskb)->end_seq =TCP_SKB_CB (skb)->seq + probe_size;tcp_insert_write_queue_before (nskb, skb, sk);tcp_advance_send_head (sk, skb); -TCP_SKB_CB (nskb)->seq =TCP_SKB_CB (skb)->seq; -TCP_SKB_CB (nskb)->end_seq =TCP_SKB_CB (skb)->seq + probe_size;TCP_SKB_CB (nskb)->flags =TCPCB_FLAG_ACK ;TCP_SKB_CB (nskb)->sacked =0 ; nskb->csum =0 ;
net/ipv6/tcp_ipv6.c +1 -0
@@ -1900,6 +1900,7 @@struct inet_connection_sock *icsk =inet_csk (sk);struct tcp_sock *tp =tcp_sk (sk); + tp->write_queue_rb =RB_ROOT ;skb_queue_head_init (&tp->out_of_order_queue);tcp_init_xmit_timers (sk);tcp_prequeue_init (tp);
PATCH 2/2 David Miller's rbtree patches for 2.6.22.6
In reply to Tom QuetchenbachPatch 2: fixes to fack_counts and enhancement of SACK fast path
-Tom
---
include/net/tcp.h +14 -6
@@ -1213,6 +1213,11 @@ sk->sk_send_head =tcp_write_queue_next (sk, skb);if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue) sk->sk_send_head =NULL ; +else +/* update fack_count of send_head. Since we've sent skb already, +* its packet count must be set by now. */ +TCP_SKB_CB (sk->sk_send_head)->fack_count = +TCP_SKB_CB (skb)->fack_count +tcp_skb_pcount (skb);/* Don't override Nagle indefinately with F-RTO */ if (tp->frto_counter ==2 ) tp->frto_counter =3 ;
@@ -1310,19 +1315,22 @@/* An insert into the middle of the write queue causes the fack * counts in subsequent packets to become invalid, fix them up. */ -static inline void tcp_reset_fack_counts (struct sock *sk,struct sk_buff *first) +static inline void tcp_reset_fack_counts (struct sock *sk,struct sk_buff *skb) { -struct sk_buff *prev = first->prev; +struct sk_buff *prev = skb->prev;unsigned int fc =0 ;if (prev != (struct sk_buff *) &sk->sk_write_queue) fc =TCP_SKB_CB (prev)->fack_count +tcp_skb_pcount (prev); -while (first != (struct sk_buff *)&sk->sk_write_queue) { -TCP_SKB_CB (first)->fack_count = fc; +while (skb != (struct sk_buff *)&sk->sk_write_queue) { +if (TCP_SKB_CB (skb)->fack_count == fc || !tcp_skb_pcount (skb)) +break ; - fc +=tcp_skb_pcount (first); - first = first->next; +TCP_SKB_CB (skb)->fack_count = fc; + + fc +=tcp_skb_pcount (skb); + skb = skb->next; } }
net/ipv4/tcp_input.c +11 -0
@@ -956,6 +956,7 @@int fack_count_base;int i;int first_sack_index; +u32 prev_end_seq =0 ;if (!tp->sacked_out) tp->fackets_out =0 ;
@@ -1000,6 +1001,7 @@if (i ==0 ) {if (tp->recv_sack_cache[i].start_seq != start_seq) flag =0 ; + prev_end_seq =ntohl (tp->recv_sack_cache[i].end_seq); }else {if ((tp->recv_sack_cache[i].start_seq != start_seq) || (tp->recv_sack_cache[i].end_seq != end_seq))
@@ -1016,9 +1018,16 @@ first_sack_index =0 ;if (flag) +/* all that has changed is end of first SACK block. So all we +* need to do is tag those skbs that were'nt tagged last time. */ num_sacks =1 ;else {int j; + +/* more than just end of first SACK block has changed; invalidate +* prev_end_seq */ + + prev_end_seq =0 ;/* order SACK blocks to allow in order walk of the retrans queue */ for (i = num_sacks-1 ; i >0 ; i--) {
@@ -1051,6 +1060,8 @@int fack_count;int dup_sack = (found_dup_sack && (i == first_sack_index)); +if (prev_end_seq) start_seq = prev_end_seq; + skb =tcp_write_queue_find (sk, start_seq);if (!skb)continue ;
Quoted 1 line · Tom Quetchenbach
Patch 2: fixes to
fack_countsand enhancement of SACK fast path
...Usually these are not combined in patches but a separate patch per change.
include/net/tcp.h
Quoted 4 lines
diff -ur
linux-2.6.22.6-rbtree-davem-fixed/include/net/tcp.hlinux-2.6.22.6-rbtree-tomq/include/net/tcp.hinclude/net/tcp.h +0
-0@@ -1213,6 +1213,11 @@
...Btw, please always use -p to diff as well as it helps review :-).
Quoted 20 lines
include/net/tcp.h +5
-0sk->sk_send_head =tcp_write_queue_next (sk, skb);if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue) sk->sk_send_head =NULL ; +else +/* update fack_count of send_head. Since we've sent skb already, +* its packet count must be set by now. */ +TCP_SKB_CB (sk->sk_send_head)->fack_count = +TCP_SKB_CB (skb)->fack_count +tcp_skb_pcount (skb);/* Don't override Nagle indefinately with F-RTO */ if (tp->frto_counter ==2 )include/net/tcp.h +1
-2tp->frto_counter =3 ;@@ -1310,19 +1315,22 @@/* An insert into the middle of the write queue causes the fack * counts in subsequent packets to become invalid, fix them up. */ -static inline void tcp_reset_fack_counts (struct sock *sk,struct sk_buff *first) +static inline void tcp_reset_fack_counts (struct sock *sk,struct sk_buff *skb) { -struct sk_buff *prev = first->prev;
include/net/tcp.h line 1327
Quoted 10 lines
include/net/tcp.h +3
-2+struct sk_buff *prev = skb->prev;unsigned int fc =0 ;if (prev != (struct sk_buff *) &sk->sk_write_queue) fc =TCP_SKB_CB (prev)->fack_count +tcp_skb_pcount (prev); -while (first != (struct sk_buff *)&sk->sk_write_queue) { -TCP_SKB_CB (first)->fack_count = fc; +while (skb != (struct sk_buff *)&sk->sk_write_queue) { +if (TCP_SKB_CB (skb)->fack_count == fc || !tcp_skb_pcount (skb))
What is this !tcp_skb_pcount(skb) for???? I think what we really want here
is this: || (skb == tcp_send_head(sk))
Quoted 31 lines
include/net/tcp.h +5
-2+break ; - fc +=tcp_skb_pcount (first); - first = first->next; +TCP_SKB_CB (skb)->fack_count = fc; + + fc +=tcp_skb_pcount (skb); + skb = skb->next; } }diff -ur
linux-2.6.22.6-rbtree-davem-fixed/net/ipv4/tcp_input.clinux-2.6.22.6-rbtree-tomq/net/ipv4/tcp_input.cnet/ipv4/tcp_input.c +2
-0@@ -956,6 +956,7 @@int fack_count_base;int i;int first_sack_index; +u32 prev_end_seq =0 ;if (!tp->sacked_out) tp->fackets_out =0 ;@@ -1000,6 +1001,7 @@if (i ==0 ) {if (tp->recv_sack_cache[i].start_seq != start_seq) flag =0 ; + prev_end_seq =ntohl (tp->recv_sack_cache[i].end_seq); }else {if ((tp->recv_sack_cache[i].start_seq != start_seq) || (tp->recv_sack_cache[i].end_seq != end_seq))
net/ipv4/tcp_input.c line 1022
Quoted 5 lines
net/ipv4/tcp_input.c +2
-0@@ -1016,9 +1018,16 @@ first_sack_index =0 ;if (flag) +/* all that has changed is end of first SACK block. So all we +* need to do is tag those skbs that were'nt tagged last time. */
IMHO, a bit verbose comment. Besides, we already tell above that what SACK fastpath is all about...
net/ipv4/tcp_input.c line 1030
Quoted 8 lines
net/ipv4/tcp_input.c +5
-0num_sacks =1 ; else {int j; + +/* more than just end of first SACK block has changed; invalidate +* prev_end_seq */ + + prev_end_seq =0 ;
Don't tell what's obvious from code as well.
net/ipv4/tcp_input.c line 1064
Quoted 8 lines
net/ipv4/tcp_input.c +2
-0/* order SACK blocks to allow in order walk of the retrans queue */ for (i = num_sacks-1 ; i >0 ; i--) {@@ -1051,6 +1060,8 @@int fack_count;int dup_sack = (found_dup_sack && (i == first_sack_index)); +if (prev_end_seq) start_seq = prev_end_seq; +
...We never add statements on the same line after if statements (see Documentation/CodingStyle)
Signature · 1 line
i.
Quoted 2 lines · Tom Quetchenbach
Patch 1: David Miller's red-black tree code, tweaked for 2.6.22.6, with some bugfixes
It would help if you would leave the original changes as is (rb-tree and fack_count separated) and add your work on top of that...
Quoted 12 lines
diff -ur
linux-2.6.22.6/include/net/tcp.hlinux-2.6.22.6-rbtree-davem-fixed/include/net/tcp.hinclude/net/tcp.h +1
-0@@ -540,6 +540,7 @@__u32 seq;/* Starting sequence number */ __u32 end_seq;/* SEQ + FIN + SYN + datalen */ __u32 when;/* used to compute rtt's */ +unsigned int fack_count;/* speed up SACK processing */ __u8 flags;/* TCP header flags. */ /* NOTE: These must match up to the flags byte in a
include/net/tcp.h line 1048
Quoted 6 lines
include/net/tcp.h +2
-1@@ -1043,12 +1044,12 @@ }/*from STCP */ -static inline void clear_all_retrans_hints (struct tcp_sock *tp){ +static inline void clear_all_retrans_hints (struct tcp_sock *tp) +{
Unrelated change, please don't do that. Besides, it's already fixed in net-2.6.24.
Quoted 10 lines
include/net/tcp.h +0
-1tp->lost_skb_hint =NULL ; tp->scoreboard_skb_hint =NULL ; tp->retransmit_skb_hint =NULL ; tp->forward_skb_hint =NULL ; - tp->fastpath_skb_hint =NULL ; }/* MD5 Signature */ @@ -1227,9 +1229,61 @@ sk->sk_send_head =NULL ;
include/net/tcp.h line 1239
Quoted 10 lines
include/net/tcp.h +8
-0} +static inline struct sk_buff *tcp_write_queue_find (struct sock *sk,__u32 seq) +{ +struct rb_node *rb_node =tcp_sk (sk)->write_queue_rb.rb_node; +struct sk_buff *skb =NULL ; + +while (rb_node) { + struct sk_buff *tmp = rb_entry(rb_node,struct sk_buff,rb); +if (TCP_SKB_CB (tmp)->end_seq > seq) {
This is old and buggy version of the rb-tree code. Get the latest rb-tree patch from tcp-2.6 tree.
include/net/tcp.h line 1241
Quoted 2 lines
include/net/tcp.h +2
-0+ skb = tmp; +if (TCP_SKB_CB (tmp)->seq <= seq)
...fixed in tcp-2.6.
Quoted 11 lines
include/net/tcp.h +11
-0+break ; + rb_node = rb_node->rb_left; + }else + rb_node = rb_node->rb_right; + + } +return skb; +} + +static inline void tcp_rb_insert (struct sk_buff *skb,struct rb_root *root) +{
include/net/tcp.h line 1262
Quoted 10 lines
include/net/tcp.h +10
-0+struct rb_node **rb_link, *rb_parent; +__u32 seq =TCP_SKB_CB (skb)->seq; + + rb_link = &root->rb_node; + rb_parent =NULL ; +while (*rb_link !=NULL ) { + struct sk_buff *tmp = rb_entry(*rb_link,struct sk_buff,rb); + rb_parent = *rb_link; +if (TCP_SKB_CB (tmp)->end_seq > seq) { +BUG_ON (TCP_SKB_CB (tmp)->seq <= seq);
...these are broken as well.
Quoted 72 lines
include/net/tcp.h +21
-0+ rb_link = &rb_parent->rb_left; + }else { + rb_link = &rb_parent->rb_right; + } + } +rb_link_node (&skb->rb, rb_parent, rb_link); +rb_insert_color (&skb->rb, root); +} + +static inline void tcp_rb_unlink (struct sk_buff *skb,struct rb_root *root) +{ +rb_erase (&skb->rb, root); +} +static inline void __tcp_add_write_queue_tail (struct sock *sk,struct sk_buff *skb) { +struct sk_buff *tail =tcp_write_queue_tail (sk); +unsigned int fc =0 ; + +if (tail) + fc =TCP_SKB_CB (tail)->fack_count +tcp_skb_pcount (tail); +TCP_SKB_CB (skb)->fack_count = fc;__skb_queue_tail (&sk->sk_write_queue, skb); +tcp_rb_insert (skb, &tcp_sk (sk)->write_queue_rb); }static inline void tcp_add_write_queue_tail (struct sock *sk,struct sk_buff *skb)diff -ur
linux-2.6.22.6/net/ipv4/tcp_input.clinux-2.6.22.6-rbtree-davem-fixed/net/ipv4/tcp_input.cnet/ipv4/tcp_input.c +2
-11@@ -947,14 +947,13 @@unsigned char *ptr = (skb_transport_header (ack_skb) +TCP_SKB_CB (ack_skb)->sacked);struct tcp_sack_block_wire *sp = (struct tcp_sack_block_wire *)(ptr+2 ); -struct sk_buff *cached_skb;int num_sacks = (ptr[1 ] -TCPOLEN_SACK_BASE )>>3 ;int reord = tp->packets_out;int prior_fackets;u32 lost_retrans =0 ;int flag =0 ;int found_dup_sack =0 ; -int cached_fack_count; +int fack_count_base;int i;int first_sack_index;@@ -1020,7 +1019,6 @@ num_sacks =1 ; else {int j; - tp->fastpath_skb_hint =NULL ;/* order SACK blocks to allow in order walk of the retrans queue */ for (i = num_sacks-1 ; i >0 ; i--) {@@ -1045,14 +1043,7 @@/* clear flag as used for different purpose in following code */ flag =0 ; -/* Use SACK fastpath hint if valid */ - cached_skb = tp->fastpath_skb_hint; - cached_fack_count = tp->fastpath_cnt_hint; -if (!cached_skb) { - cached_skb =tcp_write_queue_head (sk); - cached_fack_count =0 ; - } - + fack_count_base =TCP_SKB_CB (tcp_write_queue_head (sk))->fack_count;for (i=0 ; i<num_sacks; i++, sp++) {struct sk_buff *skb;__u32 start_seq =ntohl (sp->start_seq);
net/ipv4/tcp_input.c line 1056
Quoted 8 lines
net/ipv4/tcp_input.c +3
-2@@ -1060,8 +1051,10 @@int fack_count;int dup_sack = (found_dup_sack && (i == first_sack_index)); - skb = cached_skb; - fack_count = cached_fack_count; + skb =tcp_write_queue_find (sk, start_seq); +if (!skb) +continue ;
In net-2.6.24 we validate SACK blocks early. ...This is not a working solution anyway since tcp_write_queue_find(end_seq) might be valid don't you think (though with validator it should only happen when dup_sack is set)? For non-DSACK cases, a better alternative is like this:
if (WARN_ON(skb == NULL))
continue;
Quoted 4 lines
net/ipv4/tcp_input.c +1
-0+ fack_count =TCP_SKB_CB (skb)->fack_count - fack_count_base;/* Event "B" in the comment above. */ if (after (end_seq, tp->high_seq))
Signature · 1 line
i.
Quoted 3 lines · Tom Quetchenbach
Here are a couple of patches against 2.6.22.6. The first one is just David's patches tweaked for 2.6.22.6, with a couple of minor bugfixes to get it to compile and not crash.
Why did you combine original patches to a single larger one, I think Dave made them separate on purpose.
Quoted 5 lines
(I also changed
__tcp_insert_write_queue_tail()to set thefack_countof the new packet to thefack_countof the tail plus the packet count of the tail, not the packet count of the new skb, because I think that's how it was intended to be. Right?
I think I noticed similar "off-by-pcount" error when I looked that long time ago, so I guess you're correct. We're only interested in delta of it anyway and add the current skb's pcount to it (which is not fixed until tcp_fragment in sacktag is past).
Quoted 3 lines
In the second patch there are a couple of significant changes. One is (as Baruch suggested) to modify the existing SACK fast path so that we don't tag packets we've already tagged when we advance by a packet.
This solution would still spend extensive amount of time in processing loop, whenever recv_sack_cache fast-path is not taken, that is, e.g. when cumulative ACK after retransmissions arrive or new hole becomes visible (which are not very exceptional events after all :-)). In the cumulative ACK case especially, this processing is very likely _fully_ wasted walking.
So there is still room for large improvements. I've made an improved version of the current sacktag walk couple of days ago (it's in a state where it didn't crash but likely very buggy still), I'll post it here soon... Idea is embed recv_sack_cache checking fully into the walking loop. By doing that an previously known work is not duplicated. The patch is currently against non-rbtree stuff but incorporating rb-tree things on top of it should be very trivial and synergy benefits with rbtree should be considerable because non-rbtree has to do "fast walk" skipping for skbs that are under highest_sack which is prone to cache misses.
Quoted 4 lines
The other issue is that the cached
fack_countsseem to be wrong, because they're set when we insert into the queue, buttcp_set_tso_segs()is called later, just before we send, so all thefack_countsare zero. My solution was to set thefack_countwhen we advance thesend_head.
I think it's better solution anyway, since we might have to do reset_fack_counts() in between and there's no need to update past sk_send_head.
Quoted 3 lines
Also I
changedtcp_reset_fack_counts()so that it exits when it hits an skb whosetcp_skb_pcount()is zero
Do you mind to explain what's the purpose of that?
Quoted 3 lines
or whose
fack_countis already correct.
(This really helps when TSO is on, since there's lots of inserting into the middle of the queue.)
Good point.
Quoted 1 line
Please let me know how I can help get this tested and debugged.
Most network development happens against latest net-2.6(.x) trees. In addition, there's experimental tcp-2.6 tree but it's currently a bit outdated already (and DaveM is very busy with the phenomenal merge they're doing for 2.6.24 :-) so it's not too likely to be updated very soon).
...Anyway, thanks for your interest in these things.
Signature · 1 line
i.