aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorIlpo Järvinen <ilpo.jarvinen@helsinki.fi>2008-11-25 00:20:15 -0500
committerDavid S. Miller <davem@davemloft.net>2008-11-25 00:20:15 -0500
commit832d11c5cd076abc0aa1eaf7be96c81d1a59ce41 (patch)
tree95b22ad16d1ff414cab39578ed8c927c2ce08723 /include
parentf58b22fd3c16444edc393a217a74208f1894b601 (diff)
tcp: Try to restore large SKBs while SACK processing
During SACK processing, most of the benefits of TSO are eaten by the SACK blocks that one-by-one fragment SKBs to MSS sized chunks. Then we're in problems when cleanup work for them has to be done when a large cumulative ACK comes. Try to return back to pre-split state already while more and more SACK info gets discovered by combining newly discovered SACK areas with the previous skb if that's SACKed as well. This approach has a number of benefits: 1) The processing overhead is spread more equally over the RTT 2) Write queue has less skbs to process (affect everything which has to walk in the queue past the sacked areas) 3) Write queue is consistent whole the time, so no other parts of TCP has to be aware of this (this was not the case with some other approach that was, well, quite intrusive all around). 4) Clean_rtx_queue can release most of the pages using single put_page instead of previous PAGE_SIZE/mss+1 calls In case a hole is fully filled by the new SACK block, we attempt to combine the next skb too which allows construction of skbs that are even larger than what tso split them to and it handles hole per on every nth patterns that often occur during slow start overshoot pretty nicely. Though this to be really useful also a retransmission would have to get lost since cumulative ACKs advance one hole at a time in the most typical case. TODO: handle upwards only merging. That should be rather easy when segment is fully sacked but I'm leaving that as future work item (it won't make very large difference anyway since this current approach already covers quite a lot of normal cases). I was earlier thinking of some sophisticated way of tracking timestamps of the first and the last segment but later on realized that it won't be that necessary at all to store the timestamp of the last segment. The cases that can occur are basically either: 1) ambiguous => no sensible measurement can be taken anyway 2) non-ambiguous is due to reordering => having the timestamp of the last segment there is just skewing things more off than does some good since the ack got triggered by one of the holes (besides some substle issues that would make determining right hole/skb even harder problem). Anyway, it has nothing to do with this change then. I choose to route some abnormal looking cases with goto noop, some could be handled differently (eg., by stopping the walking at that skb but again). In general, they either shouldn't happen at all or are rare enough to make no difference in practice. In theory this change (as whole) could cause some macroscale regression (global) because of cache misses that are taken over the round-trip time but it gets very likely better because of much less (local) cache misses per other write queue walkers and the big recovery clearing cumulative ack. Worth to note that these benefits would be very easy to get also without TSO/GSO being on as long as the data is in pages so that we can merge them. Currently I won't let that happen because DSACK splitting at fragment that would mess up pcounts due to sk_can_gso in tcp_set_skb_tso_segs. Once DSACKs fragments gets avoided, we have some conditions that can be made less strict. TODO: I will probably have to convert the excessive pointer passing to struct sacktag_state... :-) My testing revealed that considerable amount of skbs couldn't be shifted because they were cloned (most likely still awaiting tx reclaim)... [The rest is considering future work instead since I got repeatably EFAULT to tcpdump's recvfrom when I added pskb_expand_head to deal with clones, so I separated that into another, later patch] ...To counter that, I gave up on the fifth advantage: 5) When growing previous SACK block, less allocs for new skbs are done, basically a new alloc is needed only when new hole is detected and when the previous skb runs out of frags space ...which now only happens of if reclaim is fast enough to dispose the clone before the SACK block comes in (the window is RTT long), otherwise we'll have to alloc some. With clones being handled I got these numbers (will be somewhat worse without that), taken with fine-grained mibs: TCPSackShifted 398 TCPSackMerged 877 TCPSackShiftFallback 320 TCPSACKCOLLAPSEFALLBACKGSO 0 TCPSACKCOLLAPSEFALLBACKSKBBITS 0 TCPSACKCOLLAPSEFALLBACKSKBDATA 0 TCPSACKCOLLAPSEFALLBACKBELOW 0 TCPSACKCOLLAPSEFALLBACKFIRST 1 TCPSACKCOLLAPSEFALLBACKPREVBITS 318 TCPSACKCOLLAPSEFALLBACKMSS 1 TCPSACKCOLLAPSEFALLBACKNOHEAD 0 TCPSACKCOLLAPSEFALLBACKSHIFT 0 TCPSACKCOLLAPSENOOPSEQ 0 TCPSACKCOLLAPSENOOPSMALLPCOUNT 0 TCPSACKCOLLAPSENOOPSMALLLEN 0 TCPSACKCOLLAPSEHOLE 12 Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'include')
-rw-r--r--include/linux/skbuff.h33
-rw-r--r--include/net/tcp.h5
2 files changed, 38 insertions, 0 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index a01b6f84e3bc..acf17af45af9 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -493,6 +493,19 @@ static inline bool skb_queue_is_last(const struct sk_buff_head *list,
493} 493}
494 494
495/** 495/**
496 * skb_queue_is_first - check if skb is the first entry in the queue
497 * @list: queue head
498 * @skb: buffer
499 *
500 * Returns true if @skb is the first buffer on the list.
501 */
502static inline bool skb_queue_is_first(const struct sk_buff_head *list,
503 const struct sk_buff *skb)
504{
505 return (skb->prev == (struct sk_buff *) list);
506}
507
508/**
496 * skb_queue_next - return the next packet in the queue 509 * skb_queue_next - return the next packet in the queue
497 * @list: queue head 510 * @list: queue head
498 * @skb: current buffer 511 * @skb: current buffer
@@ -511,6 +524,24 @@ static inline struct sk_buff *skb_queue_next(const struct sk_buff_head *list,
511} 524}
512 525
513/** 526/**
527 * skb_queue_prev - return the prev packet in the queue
528 * @list: queue head
529 * @skb: current buffer
530 *
531 * Return the prev packet in @list before @skb. It is only valid to
532 * call this if skb_queue_is_first() evaluates to false.
533 */
534static inline struct sk_buff *skb_queue_prev(const struct sk_buff_head *list,
535 const struct sk_buff *skb)
536{
537 /* This BUG_ON may seem severe, but if we just return then we
538 * are going to dereference garbage.
539 */
540 BUG_ON(skb_queue_is_first(list, skb));
541 return skb->prev;
542}
543
544/**
514 * skb_get - reference buffer 545 * skb_get - reference buffer
515 * @skb: buffer to reference 546 * @skb: buffer to reference
516 * 547 *
@@ -1652,6 +1683,8 @@ extern int skb_splice_bits(struct sk_buff *skb,
1652extern void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); 1683extern void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
1653extern void skb_split(struct sk_buff *skb, 1684extern void skb_split(struct sk_buff *skb,
1654 struct sk_buff *skb1, const u32 len); 1685 struct sk_buff *skb1, const u32 len);
1686extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb,
1687 int shiftlen);
1655 1688
1656extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); 1689extern struct sk_buff *skb_segment(struct sk_buff *skb, int features);
1657 1690
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 90b4c3b4c336..265392470b26 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1192,6 +1192,11 @@ static inline struct sk_buff *tcp_write_queue_next(struct sock *sk, struct sk_bu
1192 return skb_queue_next(&sk->sk_write_queue, skb); 1192 return skb_queue_next(&sk->sk_write_queue, skb);
1193} 1193}
1194 1194
1195static inline struct sk_buff *tcp_write_queue_prev(struct sock *sk, struct sk_buff *skb)
1196{
1197 return skb_queue_prev(&sk->sk_write_queue, skb);
1198}
1199
1195#define tcp_for_write_queue(skb, sk) \ 1200#define tcp_for_write_queue(skb, sk) \
1196 skb_queue_walk(&(sk)->sk_write_queue, skb) 1201 skb_queue_walk(&(sk)->sk_write_queue, skb)
1197 1202