]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/dccp/ccids/ccid2.c
dccp ccid-3: Always perform receiver RTT sampling
[linux-2.6-omap-h63xx.git] / net / dccp / ccids / ccid2.c
index b5b52ebb2693d308d46deee5e6c5f6a5e27d7199..fa713227c66f10bb6be9518d0accbecea2a28257 100644 (file)
@@ -25,7 +25,7 @@
 /*
  * This implementation should follow RFC 4341
  */
-
+#include "../feat.h"
 #include "../ccid.h"
 #include "../dccp.h"
 #include "ccid2.h"
 #ifdef CONFIG_IP_DCCP_CCID2_DEBUG
 static int ccid2_debug;
 #define ccid2_pr_debug(format, a...)   DCCP_PR_DEBUG(ccid2_debug, format, ##a)
-
-static void ccid2_hc_tx_check_sanity(const struct ccid2_hc_tx_sock *hctx)
-{
-       int len = 0;
-       int pipe = 0;
-       struct ccid2_seq *seqp = hctx->ccid2hctx_seqh;
-
-       /* there is data in the chain */
-       if (seqp != hctx->ccid2hctx_seqt) {
-               seqp = seqp->ccid2s_prev;
-               len++;
-               if (!seqp->ccid2s_acked)
-                       pipe++;
-
-               while (seqp != hctx->ccid2hctx_seqt) {
-                       struct ccid2_seq *prev = seqp->ccid2s_prev;
-
-                       len++;
-                       if (!prev->ccid2s_acked)
-                               pipe++;
-
-                       /* packets are sent sequentially */
-                       BUG_ON(dccp_delta_seqno(seqp->ccid2s_seq,
-                                               prev->ccid2s_seq ) >= 0);
-                       BUG_ON(time_before(seqp->ccid2s_sent,
-                                          prev->ccid2s_sent));
-
-                       seqp = prev;
-               }
-       }
-
-       BUG_ON(pipe != hctx->ccid2hctx_pipe);
-       ccid2_pr_debug("len of chain=%d\n", len);
-
-       do {
-               seqp = seqp->ccid2s_prev;
-               len++;
-       } while (seqp != hctx->ccid2hctx_seqh);
-
-       ccid2_pr_debug("total len=%d\n", len);
-       BUG_ON(len != hctx->ccid2hctx_seqbufc * CCID2_SEQBUF_LEN);
-}
 #else
 #define ccid2_pr_debug(format, a...)
-#define ccid2_hc_tx_check_sanity(hctx)
 #endif
 
 static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hctx)
@@ -87,8 +44,7 @@ static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hctx)
        int i;
 
        /* check if we have space to preserve the pointer to the buffer */
-       if (hctx->ccid2hctx_seqbufc >= (sizeof(hctx->ccid2hctx_seqbuf) /
-                                       sizeof(struct ccid2_seq*)))
+       if (hctx->seqbufc >= sizeof(hctx->seqbuf) / sizeof(struct ccid2_seq *))
                return -ENOMEM;
 
        /* allocate buffer and initialize linked list */
@@ -104,38 +60,35 @@ static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hctx)
        seqp->ccid2s_prev = &seqp[CCID2_SEQBUF_LEN - 1];
 
        /* This is the first allocation.  Initiate the head and tail.  */
-       if (hctx->ccid2hctx_seqbufc == 0)
-               hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqt = seqp;
+       if (hctx->seqbufc == 0)
+               hctx->seqh = hctx->seqt = seqp;
        else {
                /* link the existing list with the one we just created */
-               hctx->ccid2hctx_seqh->ccid2s_next = seqp;
-               seqp->ccid2s_prev = hctx->ccid2hctx_seqh;
+               hctx->seqh->ccid2s_next = seqp;
+               seqp->ccid2s_prev = hctx->seqh;
 
-               hctx->ccid2hctx_seqt->ccid2s_prev = &seqp[CCID2_SEQBUF_LEN - 1];
-               seqp[CCID2_SEQBUF_LEN - 1].ccid2s_next = hctx->ccid2hctx_seqt;
+               hctx->seqt->ccid2s_prev = &seqp[CCID2_SEQBUF_LEN - 1];
+               seqp[CCID2_SEQBUF_LEN - 1].ccid2s_next = hctx->seqt;
        }
 
        /* store the original pointer to the buffer so we can free it */
-       hctx->ccid2hctx_seqbuf[hctx->ccid2hctx_seqbufc] = seqp;
-       hctx->ccid2hctx_seqbufc++;
+       hctx->seqbuf[hctx->seqbufc] = seqp;
+       hctx->seqbufc++;
 
        return 0;
 }
 
 static int ccid2_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb)
 {
-       struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
-
-       if (hctx->ccid2hctx_pipe < hctx->ccid2hctx_cwnd)
-               return 0;
-
-       return 1; /* XXX CCID should dequeue when ready instead of polling */
+       if (ccid2_cwnd_network_limited(ccid2_hc_tx_sk(sk)))
+               return CCID_PACKET_WILL_DEQUEUE_LATER;
+       return CCID_PACKET_SEND_AT_ONCE;
 }
 
 static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val)
 {
        struct dccp_sock *dp = dccp_sk(sk);
-       u32 max_ratio = DIV_ROUND_UP(ccid2_hc_tx_sk(sk)->ccid2hctx_cwnd, 2);
+       u32 max_ratio = DIV_ROUND_UP(ccid2_hc_tx_sk(sk)->cwnd, 2);
 
        /*
         * Ensure that Ack Ratio does not exceed ceil(cwnd/2), which is (2) from
@@ -147,8 +100,8 @@ static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val)
                DCCP_WARN("Limiting Ack Ratio (%u) to %u\n", val, max_ratio);
                val = max_ratio;
        }
-       if (val > 0xFFFF)               /* RFC 4340, 11.3 */
-               val = 0xFFFF;
+       if (val > DCCPF_ACK_RATIO_MAX)
+               val = DCCPF_ACK_RATIO_MAX;
 
        if (val == dp->dccps_l_ack_ratio)
                return;
@@ -157,99 +110,77 @@ static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val)
        dp->dccps_l_ack_ratio = val;
 }
 
-static void ccid2_change_srtt(struct ccid2_hc_tx_sock *hctx, long val)
-{
-       ccid2_pr_debug("change SRTT to %ld\n", val);
-       hctx->ccid2hctx_srtt = val;
-}
-
-static void ccid2_start_rto_timer(struct sock *sk);
-
 static void ccid2_hc_tx_rto_expire(unsigned long data)
 {
        struct sock *sk = (struct sock *)data;
        struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
-       long s;
+       const bool sender_was_blocked = ccid2_cwnd_network_limited(hctx);
 
        bh_lock_sock(sk);
        if (sock_owned_by_user(sk)) {
-               sk_reset_timer(sk, &hctx->ccid2hctx_rtotimer,
-                              jiffies + HZ / 5);
+               sk_reset_timer(sk, &hctx->rtotimer, jiffies + HZ / 5);
                goto out;
        }
 
        ccid2_pr_debug("RTO_EXPIRE\n");
 
-       ccid2_hc_tx_check_sanity(hctx);
-
        /* back-off timer */
-       hctx->ccid2hctx_rto <<= 1;
-
-       s = hctx->ccid2hctx_rto / HZ;
-       if (s > 60)
-               hctx->ccid2hctx_rto = 60 * HZ;
-
-       ccid2_start_rto_timer(sk);
+       hctx->rto <<= 1;
+       if (hctx->rto > DCCP_RTO_MAX)
+               hctx->rto = DCCP_RTO_MAX;
 
        /* adjust pipe, cwnd etc */
-       hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_cwnd / 2;
-       if (hctx->ccid2hctx_ssthresh < 2)
-               hctx->ccid2hctx_ssthresh = 2;
-       hctx->ccid2hctx_cwnd     = 1;
-       hctx->ccid2hctx_pipe     = 0;
+       hctx->ssthresh = hctx->cwnd / 2;
+       if (hctx->ssthresh < 2)
+               hctx->ssthresh = 2;
+       hctx->cwnd = 1;
+       hctx->pipe = 0;
 
        /* clear state about stuff we sent */
-       hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqh;
-       hctx->ccid2hctx_packets_acked = 0;
+       hctx->seqt = hctx->seqh;
+       hctx->packets_acked = 0;
 
        /* clear ack ratio state. */
-       hctx->ccid2hctx_rpseq    = 0;
-       hctx->ccid2hctx_rpdupack = -1;
+       hctx->rpseq    = 0;
+       hctx->rpdupack = -1;
        ccid2_change_l_ack_ratio(sk, 1);
-       ccid2_hc_tx_check_sanity(hctx);
+
+       /* if we were blocked before, we may now send cwnd=1 packet */
+       if (sender_was_blocked)
+               tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet);
+       /* restart backed-off timer */
+       sk_reset_timer(sk, &hctx->rtotimer, jiffies + hctx->rto);
 out:
        bh_unlock_sock(sk);
        sock_put(sk);
 }
 
-static void ccid2_start_rto_timer(struct sock *sk)
-{
-       struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
-
-       ccid2_pr_debug("setting RTO timeout=%ld\n", hctx->ccid2hctx_rto);
-
-       BUG_ON(timer_pending(&hctx->ccid2hctx_rtotimer));
-       sk_reset_timer(sk, &hctx->ccid2hctx_rtotimer,
-                      jiffies + hctx->ccid2hctx_rto);
-}
-
-static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len)
+static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len)
 {
        struct dccp_sock *dp = dccp_sk(sk);
        struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
        struct ccid2_seq *next;
 
-       hctx->ccid2hctx_pipe++;
+       hctx->pipe++;
 
-       hctx->ccid2hctx_seqh->ccid2s_seq   = dp->dccps_gss;
-       hctx->ccid2hctx_seqh->ccid2s_acked = 0;
-       hctx->ccid2hctx_seqh->ccid2s_sent  = jiffies;
+       hctx->seqh->ccid2s_seq   = dp->dccps_gss;
+       hctx->seqh->ccid2s_acked = 0;
+       hctx->seqh->ccid2s_sent  = jiffies;
 
-       next = hctx->ccid2hctx_seqh->ccid2s_next;
+       next = hctx->seqh->ccid2s_next;
        /* check if we need to alloc more space */
-       if (next == hctx->ccid2hctx_seqt) {
+       if (next == hctx->seqt) {
                if (ccid2_hc_tx_alloc_seq(hctx)) {
                        DCCP_CRIT("packet history - out of memory!");
                        /* FIXME: find a more graceful way to bail out */
                        return;
                }
-               next = hctx->ccid2hctx_seqh->ccid2s_next;
-               BUG_ON(next == hctx->ccid2hctx_seqt);
+               next = hctx->seqh->ccid2s_next;
+               BUG_ON(next == hctx->seqt);
        }
-       hctx->ccid2hctx_seqh = next;
+       hctx->seqh = next;
 
-       ccid2_pr_debug("cwnd=%d pipe=%d\n", hctx->ccid2hctx_cwnd,
-                      hctx->ccid2hctx_pipe);
+       ccid2_pr_debug("cwnd=%d pipe=%d\n", hctx->cwnd, hctx->pipe);
 
        /*
         * FIXME: The code below is broken and the variables have been removed
@@ -272,12 +203,12 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len)
         */
 #if 0
        /* Ack Ratio.  Need to maintain a concept of how many windows we sent */
-       hctx->ccid2hctx_arsent++;
+       hctx->arsent++;
        /* We had an ack loss in this window... */
-       if (hctx->ccid2hctx_ackloss) {
-               if (hctx->ccid2hctx_arsent >= hctx->ccid2hctx_cwnd) {
-                       hctx->ccid2hctx_arsent  = 0;
-                       hctx->ccid2hctx_ackloss = 0;
+       if (hctx->ackloss) {
+               if (hctx->arsent >= hctx->cwnd) {
+                       hctx->arsent  = 0;
+                       hctx->ackloss = 0;
                }
        } else {
                /* No acks lost up to now... */
@@ -287,28 +218,28 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len)
                        int denom = dp->dccps_l_ack_ratio * dp->dccps_l_ack_ratio -
                                    dp->dccps_l_ack_ratio;
 
-                       denom = hctx->ccid2hctx_cwnd * hctx->ccid2hctx_cwnd / denom;
+                       denom = hctx->cwnd * hctx->cwnd / denom;
 
-                       if (hctx->ccid2hctx_arsent >= denom) {
+                       if (hctx->arsent >= denom) {
                                ccid2_change_l_ack_ratio(sk, dp->dccps_l_ack_ratio - 1);
-                               hctx->ccid2hctx_arsent = 0;
+                               hctx->arsent = 0;
                        }
                } else {
                        /* we can't increase ack ratio further [1] */
-                       hctx->ccid2hctx_arsent = 0; /* or maybe set it to cwnd*/
+                       hctx->arsent = 0; /* or maybe set it to cwnd*/
                }
        }
 #endif
 
        /* setup RTO timer */
-       if (!timer_pending(&hctx->ccid2hctx_rtotimer))
-               ccid2_start_rto_timer(sk);
+       if (!timer_pending(&hctx->rtotimer))
+               sk_reset_timer(sk, &hctx->rtotimer, jiffies + hctx->rto);
 
 #ifdef CONFIG_IP_DCCP_CCID2_DEBUG
        do {
-               struct ccid2_seq *seqp = hctx->ccid2hctx_seqt;
+               struct ccid2_seq *seqp = hctx->seqt;
 
-               while (seqp != hctx->ccid2hctx_seqh) {
+               while (seqp != hctx->seqh) {
                        ccid2_pr_debug("out seq=%llu acked=%d time=%lu\n",
                                       (unsigned long long)seqp->ccid2s_seq,
                                       seqp->ccid2s_acked, seqp->ccid2s_sent);
@@ -316,205 +247,158 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len)
                }
        } while (0);
        ccid2_pr_debug("=========\n");
-       ccid2_hc_tx_check_sanity(hctx);
 #endif
 }
 
-/* XXX Lame code duplication!
- * returns -1 if none was found.
- * else returns the next offset to use in the function call.
+/**
+ * ccid2_rtt_estimator - Sample RTT and compute RTO using RFC2988 algorithm
+ * This code is almost identical with TCP's tcp_rtt_estimator(), since
+ * - it has a higher sampling frequency (recommended by RFC 1323),
+ * - the RTO does not collapse into RTT due to RTTVAR going towards zero,
+ * - it is simple (cf. more complex proposals such as Eifel timer or research
+ *   which suggests that the gain should be set according to window size),
+ * - in tests it was found to work well with CCID2 [gerrit].
  */
-static int ccid2_ackvector(struct sock *sk, struct sk_buff *skb, int offset,
-                          unsigned char **vec, unsigned char *veclen)
+static void ccid2_rtt_estimator(struct sock *sk, const long mrtt)
 {
-       const struct dccp_hdr *dh = dccp_hdr(skb);
-       unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb);
-       unsigned char *opt_ptr;
-       const unsigned char *opt_end = (unsigned char *)dh +
-                                       (dh->dccph_doff * 4);
-       unsigned char opt, len;
-       unsigned char *value;
-
-       BUG_ON(offset < 0);
-       options += offset;
-       opt_ptr = options;
-       if (opt_ptr >= opt_end)
-               return -1;
-
-       while (opt_ptr != opt_end) {
-               opt   = *opt_ptr++;
-               len   = 0;
-               value = NULL;
-
-               /* Check if this isn't a single byte option */
-               if (opt > DCCPO_MAX_RESERVED) {
-                       if (opt_ptr == opt_end)
-                               goto out_invalid_option;
-
-                       len = *opt_ptr++;
-                       if (len < 3)
-                               goto out_invalid_option;
+       struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+       long m = mrtt ? : 1;
+
+       if (hctx->srtt == 0) {
+               /* First measurement m */
+               hctx->srtt = m << 3;
+               hctx->mdev = m << 1;
+
+               hctx->mdev_max = max(TCP_RTO_MIN, hctx->mdev);
+               hctx->rttvar   = hctx->mdev_max;
+               hctx->rtt_seq  = dccp_sk(sk)->dccps_gss;
+       } else {
+               /* Update scaled SRTT as SRTT += 1/8 * (m - SRTT) */
+               m -= (hctx->srtt >> 3);
+               hctx->srtt += m;
+
+               /* Similarly, update scaled mdev with regard to |m| */
+               if (m < 0) {
+                       m = -m;
+                       m -= (hctx->mdev >> 2);
                        /*
-                        * Remove the type and len fields, leaving
-                        * just the value size
+                        * This neutralises RTO increase when RTT < SRTT - mdev
+                        * (see P. Sarolahti, A. Kuznetsov,"Congestion Control
+                        * in Linux TCP", USENIX 2002, pp. 49-62).
                         */
-                       len     -= 2;
-                       value   = opt_ptr;
-                       opt_ptr += len;
+                       if (m > 0)
+                               m >>= 3;
+               } else {
+                       m -= (hctx->mdev >> 2);
+               }
+               hctx->mdev += m;
 
-                       if (opt_ptr > opt_end)
-                               goto out_invalid_option;
+               if (hctx->mdev > hctx->mdev_max) {
+                       hctx->mdev_max = hctx->mdev;
+                       if (hctx->mdev_max > hctx->rttvar)
+                               hctx->rttvar = hctx->mdev_max;
                }
 
-               switch (opt) {
-               case DCCPO_ACK_VECTOR_0:
-               case DCCPO_ACK_VECTOR_1:
-                       *vec    = value;
-                       *veclen = len;
-                       return offset + (opt_ptr - options);
+               /*
+                * Decay RTTVAR at most once per flight, exploiting that
+                *  1) pipe <= cwnd <= Sequence_Window = W  (RFC 4340, 7.5.2)
+                *  2) AWL = GSS-W+1 <= GAR <= GSS          (RFC 4340, 7.5.1)
+                * GAR is a useful bound for FlightSize = pipe, AWL is probably
+                * too low as it over-estimates pipe.
+                */
+               if (after48(dccp_sk(sk)->dccps_gar, hctx->rtt_seq)) {
+                       if (hctx->mdev_max < hctx->rttvar)
+                               hctx->rttvar -= (hctx->rttvar -
+                                                hctx->mdev_max) >> 2;
+                       hctx->rtt_seq  = dccp_sk(sk)->dccps_gss;
+                       hctx->mdev_max = TCP_RTO_MIN;
                }
        }
 
-       return -1;
-
-out_invalid_option:
-       DCCP_BUG("Invalid option - this should not happen (previous parsing)!");
-       return -1;
-}
-
-static void ccid2_hc_tx_kill_rto_timer(struct sock *sk)
-{
-       struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+       /*
+        * Set RTO from SRTT and RTTVAR
+        * Clock granularity is ignored since the minimum error for RTTVAR is
+        * clamped to 50msec (corresponding to HZ=20). This leads to a minimum
+        * RTO of 200msec. This agrees with TCP and RFC 4341, 5.: "Because DCCP
+        * does not retransmit data, DCCP does not require TCP's recommended
+        * minimum timeout of one second".
+        */
+       hctx->rto = (hctx->srtt >> 3) + hctx->rttvar;
 
-       sk_stop_timer(sk, &hctx->ccid2hctx_rtotimer);
-       ccid2_pr_debug("deleted RTO timer\n");
+       if (hctx->rto > DCCP_RTO_MAX)
+               hctx->rto = DCCP_RTO_MAX;
 }
 
-static inline void ccid2_new_ack(struct sock *sk,
-                                struct ccid2_seq *seqp,
-                                unsigned int *maxincr)
+static void ccid2_new_ack(struct sock *sk, struct ccid2_seq *seqp,
+                         unsigned int *maxincr)
 {
        struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
 
-       if (hctx->ccid2hctx_cwnd < hctx->ccid2hctx_ssthresh) {
-               if (*maxincr > 0 && ++hctx->ccid2hctx_packets_acked == 2) {
-                       hctx->ccid2hctx_cwnd += 1;
-                       *maxincr             -= 1;
-                       hctx->ccid2hctx_packets_acked = 0;
+       if (hctx->cwnd < hctx->ssthresh) {
+               if (*maxincr > 0 && ++hctx->packets_acked == 2) {
+                       hctx->cwnd += 1;
+                       *maxincr   -= 1;
+                       hctx->packets_acked = 0;
                }
-       } else if (++hctx->ccid2hctx_packets_acked >= hctx->ccid2hctx_cwnd) {
-                       hctx->ccid2hctx_cwnd += 1;
-                       hctx->ccid2hctx_packets_acked = 0;
+       } else if (++hctx->packets_acked >= hctx->cwnd) {
+                       hctx->cwnd += 1;
+                       hctx->packets_acked = 0;
        }
-
-       /* update RTO */
-       if (hctx->ccid2hctx_srtt == -1 ||
-           time_after(jiffies, hctx->ccid2hctx_lastrtt + hctx->ccid2hctx_srtt)) {
-               unsigned long r = (long)jiffies - (long)seqp->ccid2s_sent;
-               int s;
-
-               /* first measurement */
-               if (hctx->ccid2hctx_srtt == -1) {
-                       ccid2_pr_debug("R: %lu Time=%lu seq=%llu\n",
-                                      r, jiffies,
-                                      (unsigned long long)seqp->ccid2s_seq);
-                       ccid2_change_srtt(hctx, r);
-                       hctx->ccid2hctx_rttvar = r >> 1;
-               } else {
-                       /* RTTVAR */
-                       long tmp = hctx->ccid2hctx_srtt - r;
-                       long srtt;
-
-                       if (tmp < 0)
-                               tmp *= -1;
-
-                       tmp >>= 2;
-                       hctx->ccid2hctx_rttvar *= 3;
-                       hctx->ccid2hctx_rttvar >>= 2;
-                       hctx->ccid2hctx_rttvar += tmp;
-
-                       /* SRTT */
-                       srtt = hctx->ccid2hctx_srtt;
-                       srtt *= 7;
-                       srtt >>= 3;
-                       tmp = r >> 3;
-                       srtt += tmp;
-                       ccid2_change_srtt(hctx, srtt);
-               }
-               s = hctx->ccid2hctx_rttvar << 2;
-               /* clock granularity is 1 when based on jiffies */
-               if (!s)
-                       s = 1;
-               hctx->ccid2hctx_rto = hctx->ccid2hctx_srtt + s;
-
-               /* must be at least a second */
-               s = hctx->ccid2hctx_rto / HZ;
-               /* DCCP doesn't require this [but I like it cuz my code sux] */
-#if 1
-               if (s < 1)
-                       hctx->ccid2hctx_rto = HZ;
-#endif
-               /* max 60 seconds */
-               if (s > 60)
-                       hctx->ccid2hctx_rto = HZ * 60;
-
-               hctx->ccid2hctx_lastrtt = jiffies;
-
-               ccid2_pr_debug("srtt: %ld rttvar: %ld rto: %ld (HZ=%d) R=%lu\n",
-                              hctx->ccid2hctx_srtt, hctx->ccid2hctx_rttvar,
-                              hctx->ccid2hctx_rto, HZ, r);
-       }
-
-       /* we got a new ack, so re-start RTO timer */
-       ccid2_hc_tx_kill_rto_timer(sk);
-       ccid2_start_rto_timer(sk);
-}
-
-static void ccid2_hc_tx_dec_pipe(struct sock *sk)
-{
-       struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
-
-       if (hctx->ccid2hctx_pipe == 0)
-               DCCP_BUG("pipe == 0");
-       else
-               hctx->ccid2hctx_pipe--;
-
-       if (hctx->ccid2hctx_pipe == 0)
-               ccid2_hc_tx_kill_rto_timer(sk);
+       /*
+        * FIXME: RTT is sampled several times per acknowledgment (for each
+        * entry in the Ack Vector), instead of once per Ack (as in TCP SACK).
+        * This causes the RTT to be over-estimated, since the older entries
+        * in the Ack Vector have earlier sending times.
+        * The cleanest solution is to not use the ccid2s_sent field at all
+        * and instead use DCCP timestamps - need to be resolved at some time.
+        */
+       ccid2_rtt_estimator(sk, jiffies - seqp->ccid2s_sent);
 }
 
 static void ccid2_congestion_event(struct sock *sk, struct ccid2_seq *seqp)
 {
        struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
 
-       if (time_before(seqp->ccid2s_sent, hctx->ccid2hctx_last_cong)) {
+       if (time_before(seqp->ccid2s_sent, hctx->last_cong)) {
                ccid2_pr_debug("Multiple losses in an RTT---treating as one\n");
                return;
        }
 
-       hctx->ccid2hctx_last_cong = jiffies;
+       hctx->last_cong = jiffies;
 
-       hctx->ccid2hctx_cwnd     = hctx->ccid2hctx_cwnd / 2 ? : 1U;
-       hctx->ccid2hctx_ssthresh = max(hctx->ccid2hctx_cwnd, 2U);
+       hctx->cwnd     = hctx->cwnd / 2 ? : 1U;
+       hctx->ssthresh = max(hctx->cwnd, 2U);
 
        /* Avoid spurious timeouts resulting from Ack Ratio > cwnd */
-       if (dccp_sk(sk)->dccps_l_ack_ratio > hctx->ccid2hctx_cwnd)
-               ccid2_change_l_ack_ratio(sk, hctx->ccid2hctx_cwnd);
+       if (dccp_sk(sk)->dccps_l_ack_ratio > hctx->cwnd)
+               ccid2_change_l_ack_ratio(sk, hctx->cwnd);
+}
+
+static int ccid2_hc_tx_parse_options(struct sock *sk, u8 packet_type,
+                                    u8 option, u8 *optval, u8 optlen)
+{
+       struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+       switch (option) {
+       case DCCPO_ACK_VECTOR_0:
+       case DCCPO_ACK_VECTOR_1:
+               return dccp_ackvec_parsed_add(&hctx->av_chunks, optval, optlen,
+                                             option - DCCPO_ACK_VECTOR_0);
+       }
+       return 0;
 }
 
 static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
 {
        struct dccp_sock *dp = dccp_sk(sk);
        struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+       const bool sender_was_blocked = ccid2_cwnd_network_limited(hctx);
+       struct dccp_ackvec_parsed *avp;
        u64 ackno, seqno;
        struct ccid2_seq *seqp;
-       unsigned char *vector;
-       unsigned char veclen;
-       int offset = 0;
        int done = 0;
        unsigned int maxincr = 0;
 
-       ccid2_hc_tx_check_sanity(hctx);
        /* check reverse path congestion */
        seqno = DCCP_SKB_CB(skb)->dccpd_seq;
 
@@ -523,21 +407,21 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
         * -sorbo.
         */
        /* need to bootstrap */
-       if (hctx->ccid2hctx_rpdupack == -1) {
-               hctx->ccid2hctx_rpdupack = 0;
-               hctx->ccid2hctx_rpseq = seqno;
+       if (hctx->rpdupack == -1) {
+               hctx->rpdupack = 0;
+               hctx->rpseq = seqno;
        } else {
                /* check if packet is consecutive */
-               if (dccp_delta_seqno(hctx->ccid2hctx_rpseq, seqno) == 1)
-                       hctx->ccid2hctx_rpseq = seqno;
+               if (dccp_delta_seqno(hctx->rpseq, seqno) == 1)
+                       hctx->rpseq = seqno;
                /* it's a later packet */
-               else if (after48(seqno, hctx->ccid2hctx_rpseq)) {
-                       hctx->ccid2hctx_rpdupack++;
+               else if (after48(seqno, hctx->rpseq)) {
+                       hctx->rpdupack++;
 
                        /* check if we got enough dupacks */
-                       if (hctx->ccid2hctx_rpdupack >= NUMDUPACK) {
-                               hctx->ccid2hctx_rpdupack = -1; /* XXX lame */
-                               hctx->ccid2hctx_rpseq = 0;
+                       if (hctx->rpdupack >= NUMDUPACK) {
+                               hctx->rpdupack = -1; /* XXX lame */
+                               hctx->rpseq = 0;
 
                                ccid2_change_l_ack_ratio(sk, 2 * dp->dccps_l_ack_ratio);
                        }
@@ -545,27 +429,22 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
        }
 
        /* check forward path congestion */
-       /* still didn't send out new data packets */
-       if (hctx->ccid2hctx_seqh == hctx->ccid2hctx_seqt)
+       if (dccp_packet_without_ack(skb))
                return;
 
-       switch (DCCP_SKB_CB(skb)->dccpd_type) {
-       case DCCP_PKT_ACK:
-       case DCCP_PKT_DATAACK:
-               break;
-       default:
-               return;
-       }
+       /* still didn't send out new data packets */
+       if (hctx->seqh == hctx->seqt)
+               goto done;
 
        ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq;
-       if (after48(ackno, hctx->ccid2hctx_high_ack))
-               hctx->ccid2hctx_high_ack = ackno;
+       if (after48(ackno, hctx->high_ack))
+               hctx->high_ack = ackno;
 
-       seqp = hctx->ccid2hctx_seqt;
+       seqp = hctx->seqt;
        while (before48(seqp->ccid2s_seq, ackno)) {
                seqp = seqp->ccid2s_next;
-               if (seqp == hctx->ccid2hctx_seqh) {
-                       seqp = hctx->ccid2hctx_seqh->ccid2s_prev;
+               if (seqp == hctx->seqh) {
+                       seqp = hctx->seqh->ccid2s_prev;
                        break;
                }
        }
@@ -575,26 +454,26 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
         * packets per acknowledgement. Rounding up avoids that cwnd is not
         * advanced when Ack Ratio is 1 and gives a slight edge otherwise.
         */
-       if (hctx->ccid2hctx_cwnd < hctx->ccid2hctx_ssthresh)
+       if (hctx->cwnd < hctx->ssthresh)
                maxincr = DIV_ROUND_UP(dp->dccps_l_ack_ratio, 2);
 
        /* go through all ack vectors */
-       while ((offset = ccid2_ackvector(sk, skb, offset,
-                                        &vector, &veclen)) != -1) {
+       list_for_each_entry(avp, &hctx->av_chunks, node) {
                /* go through this ack vector */
-               while (veclen--) {
-                       const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK;
-                       u64 ackno_end_rl = SUB48(ackno, rl);
+               for (; avp->len--; avp->vec++) {
+                       u64 ackno_end_rl = SUB48(ackno,
+                                                dccp_ackvec_runlen(avp->vec));
 
-                       ccid2_pr_debug("ackvec start:%llu end:%llu\n",
+                       ccid2_pr_debug("ackvec %llu |%u,%u|\n",
                                       (unsigned long long)ackno,
-                                      (unsigned long long)ackno_end_rl);
+                                      dccp_ackvec_state(avp->vec) >> 6,
+                                      dccp_ackvec_runlen(avp->vec));
                        /* if the seqno we are analyzing is larger than the
                         * current ackno, then move towards the tail of our
                         * seqnos.
                         */
                        while (after48(seqp->ccid2s_seq, ackno)) {
-                               if (seqp == hctx->ccid2hctx_seqt) {
+                               if (seqp == hctx->seqt) {
                                        done = 1;
                                        break;
                                }
@@ -607,26 +486,24 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
                         * run length
                         */
                        while (between48(seqp->ccid2s_seq,ackno_end_rl,ackno)) {
-                               const u8 state = *vector &
-                                                DCCP_ACKVEC_STATE_MASK;
+                               const u8 state = dccp_ackvec_state(avp->vec);
 
                                /* new packet received or marked */
-                               if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED &&
+                               if (state != DCCPAV_NOT_RECEIVED &&
                                    !seqp->ccid2s_acked) {
-                                       if (state ==
-                                           DCCP_ACKVEC_STATE_ECN_MARKED) {
+                                       if (state == DCCPAV_ECN_MARKED)
                                                ccid2_congestion_event(sk,
                                                                       seqp);
-                                       else
+                                       else
                                                ccid2_new_ack(sk, seqp,
                                                              &maxincr);
 
                                        seqp->ccid2s_acked = 1;
                                        ccid2_pr_debug("Got ack for %llu\n",
                                                       (unsigned long long)seqp->ccid2s_seq);
-                                       ccid2_hc_tx_dec_pipe(sk);
+                                       hctx->pipe--;
                                }
-                               if (seqp == hctx->ccid2hctx_seqt) {
+                               if (seqp == hctx->seqt) {
                                        done = 1;
                                        break;
                                }
@@ -636,7 +513,6 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
                                break;
 
                        ackno = SUB48(ackno_end_rl, 1);
-                       vector++;
                }
                if (done)
                        break;
@@ -645,11 +521,11 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
        /* The state about what is acked should be correct now
         * Check for NUMDUPACK
         */
-       seqp = hctx->ccid2hctx_seqt;
-       while (before48(seqp->ccid2s_seq, hctx->ccid2hctx_high_ack)) {
+       seqp = hctx->seqt;
+       while (before48(seqp->ccid2s_seq, hctx->high_ack)) {
                seqp = seqp->ccid2s_next;
-               if (seqp == hctx->ccid2hctx_seqh) {
-                       seqp = hctx->ccid2hctx_seqh->ccid2s_prev;
+               if (seqp == hctx->seqh) {
+                       seqp = hctx->seqh->ccid2s_prev;
                        break;
                }
        }
@@ -660,7 +536,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
                        if (done == NUMDUPACK)
                                break;
                }
-               if (seqp == hctx->ccid2hctx_seqt)
+               if (seqp == hctx->seqt)
                        break;
                seqp = seqp->ccid2s_prev;
        }
@@ -681,25 +557,34 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
                                 * one ack vector.
                                 */
                                ccid2_congestion_event(sk, seqp);
-                               ccid2_hc_tx_dec_pipe(sk);
+                               hctx->pipe--;
                        }
-                       if (seqp == hctx->ccid2hctx_seqt)
+                       if (seqp == hctx->seqt)
                                break;
                        seqp = seqp->ccid2s_prev;
                }
 
-               hctx->ccid2hctx_seqt = last_acked;
+               hctx->seqt = last_acked;
        }
 
        /* trim acked packets in tail */
-       while (hctx->ccid2hctx_seqt != hctx->ccid2hctx_seqh) {
-               if (!hctx->ccid2hctx_seqt->ccid2s_acked)
+       while (hctx->seqt != hctx->seqh) {
+               if (!hctx->seqt->ccid2s_acked)
                        break;
 
-               hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqt->ccid2s_next;
+               hctx->seqt = hctx->seqt->ccid2s_next;
        }
 
-       ccid2_hc_tx_check_sanity(hctx);
+       /* restart RTO timer if not all outstanding data has been acked */
+       if (hctx->pipe == 0)
+               sk_stop_timer(sk, &hctx->rtotimer);
+       else
+               sk_reset_timer(sk, &hctx->rtotimer, jiffies + hctx->rto);
+done:
+       /* check if incoming Acks allow pending packets to be sent */
+       if (sender_was_blocked && !ccid2_cwnd_network_limited(hctx))
+               tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet);
+       dccp_ackvec_parsed_cleanup(&hctx->av_chunks);
 }
 
 static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
@@ -709,17 +594,13 @@ static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
        u32 max_ratio;
 
        /* RFC 4341, 5: initialise ssthresh to arbitrarily high (max) value */
-       hctx->ccid2hctx_ssthresh  = ~0U;
+       hctx->ssthresh = ~0U;
 
-       /*
-        * RFC 4341, 5: "The cwnd parameter is initialized to at most four
-        * packets for new connections, following the rules from [RFC3390]".
-        * We need to convert the bytes of RFC3390 into the packets of RFC 4341.
-        */
-       hctx->ccid2hctx_cwnd = min(4U, max(2U, 4380U / dp->dccps_mss_cache));
+       /* Use larger initial windows (RFC 3390, rfc2581bis) */
+       hctx->cwnd = rfc3390_bytes_to_packets(dp->dccps_mss_cache);
 
        /* Make sure that Ack Ratio is enabled and within bounds. */
-       max_ratio = DIV_ROUND_UP(hctx->ccid2hctx_cwnd, 2);
+       max_ratio = DIV_ROUND_UP(hctx->cwnd, 2);
        if (dp->dccps_l_ack_ratio == 0 || dp->dccps_l_ack_ratio > max_ratio)
                dp->dccps_l_ack_ratio = max_ratio;
 
@@ -727,15 +608,11 @@ static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
        if (ccid2_hc_tx_alloc_seq(hctx))
                return -ENOMEM;
 
-       hctx->ccid2hctx_rto      = 3 * HZ;
-       ccid2_change_srtt(hctx, -1);
-       hctx->ccid2hctx_rttvar   = -1;
-       hctx->ccid2hctx_rpdupack = -1;
-       hctx->ccid2hctx_last_cong = jiffies;
-       setup_timer(&hctx->ccid2hctx_rtotimer, ccid2_hc_tx_rto_expire,
-                       (unsigned long)sk);
-
-       ccid2_hc_tx_check_sanity(hctx);
+       hctx->rto       = DCCP_TIMEOUT_INIT;
+       hctx->rpdupack  = -1;
+       hctx->last_cong = jiffies;
+       setup_timer(&hctx->rtotimer, ccid2_hc_tx_rto_expire, (unsigned long)sk);
+       INIT_LIST_HEAD(&hctx->av_chunks);
        return 0;
 }
 
@@ -744,11 +621,11 @@ static void ccid2_hc_tx_exit(struct sock *sk)
        struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
        int i;
 
-       ccid2_hc_tx_kill_rto_timer(sk);
+       sk_stop_timer(sk, &hctx->rtotimer);
 
-       for (i = 0; i < hctx->ccid2hctx_seqbufc; i++)
-               kfree(hctx->ccid2hctx_seqbuf[i]);
-       hctx->ccid2hctx_seqbufc = 0;
+       for (i = 0; i < hctx->seqbufc; i++)
+               kfree(hctx->seqbuf[i]);
+       hctx->seqbufc = 0;
 }
 
 static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
@@ -759,31 +636,32 @@ static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
        switch (DCCP_SKB_CB(skb)->dccpd_type) {
        case DCCP_PKT_DATA:
        case DCCP_PKT_DATAACK:
-               hcrx->ccid2hcrx_data++;
-               if (hcrx->ccid2hcrx_data >= dp->dccps_r_ack_ratio) {
+               hcrx->data++;
+               if (hcrx->data >= dp->dccps_r_ack_ratio) {
                        dccp_send_ack(sk);
-                       hcrx->ccid2hcrx_data = 0;
+                       hcrx->data = 0;
                }
                break;
        }
 }
 
 static struct ccid_operations ccid2 = {
-       .ccid_id                = DCCPC_CCID2,
-       .ccid_name              = "TCP-like",
-       .ccid_owner             = THIS_MODULE,
-       .ccid_hc_tx_obj_size    = sizeof(struct ccid2_hc_tx_sock),
-       .ccid_hc_tx_init        = ccid2_hc_tx_init,
-       .ccid_hc_tx_exit        = ccid2_hc_tx_exit,
-       .ccid_hc_tx_send_packet = ccid2_hc_tx_send_packet,
-       .ccid_hc_tx_packet_sent = ccid2_hc_tx_packet_sent,
-       .ccid_hc_tx_packet_recv = ccid2_hc_tx_packet_recv,
-       .ccid_hc_rx_obj_size    = sizeof(struct ccid2_hc_rx_sock),
-       .ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv,
+       .ccid_id                  = DCCPC_CCID2,
+       .ccid_name                = "TCP-like",
+       .ccid_owner               = THIS_MODULE,
+       .ccid_hc_tx_obj_size      = sizeof(struct ccid2_hc_tx_sock),
+       .ccid_hc_tx_init          = ccid2_hc_tx_init,
+       .ccid_hc_tx_exit          = ccid2_hc_tx_exit,
+       .ccid_hc_tx_send_packet   = ccid2_hc_tx_send_packet,
+       .ccid_hc_tx_packet_sent   = ccid2_hc_tx_packet_sent,
+       .ccid_hc_tx_parse_options = ccid2_hc_tx_parse_options,
+       .ccid_hc_tx_packet_recv   = ccid2_hc_tx_packet_recv,
+       .ccid_hc_rx_obj_size      = sizeof(struct ccid2_hc_rx_sock),
+       .ccid_hc_rx_packet_recv   = ccid2_hc_rx_packet_recv,
 };
 
 #ifdef CONFIG_IP_DCCP_CCID2_DEBUG
-module_param(ccid2_debug, bool, 0444);
+module_param(ccid2_debug, bool, 0644);
 MODULE_PARM_DESC(ccid2_debug, "Enable debug messages");
 #endif