[DCCP] ccid3: Consolidate handling of t_RTO
[linux-2.6-block.git] / net / dccp / ccids / ccid3.c
index fb21f2d9ffc653e93205ddce25e532445c04f9a4..f0ed67c84a556672221ab73dfed52896fa0cce43 100644 (file)
@@ -100,25 +100,24 @@ static void ccid3_hc_tx_set_state(struct sock *sk,
        hctx->ccid3hctx_state = state;
 }
 
-/* Calculate new t_ipi (inter packet interval) by t_ipi = s / X_inst */
-static inline void ccid3_calc_new_t_ipi(struct ccid3_hc_tx_sock *hctx)
+/*
+ * Recalculate scheduled nominal send time t_nom, inter-packet interval
+ * t_ipi, and delta value. Should be called after each change to X.
+ */
+static inline void ccid3_update_send_time(struct ccid3_hc_tx_sock *hctx)
 {
-       /*
-        * If no feedback spec says t_ipi is 1 second (set elsewhere and then
-        * doubles after every no feedback timer (separate function)
-        */
-       if (hctx->ccid3hctx_state != TFRC_SSTATE_NO_FBACK)
-               hctx->ccid3hctx_t_ipi = usecs_div(hctx->ccid3hctx_s,
-                                                 hctx->ccid3hctx_x);
-}
+       timeval_sub_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi);
 
-/* Calculate new delta by delta = min(t_ipi / 2, t_gran / 2) */
-static inline void ccid3_calc_new_delta(struct ccid3_hc_tx_sock *hctx)
-{
+       /* Calculate new t_ipi (inter packet interval) by t_ipi = s / X_inst */
+       hctx->ccid3hctx_t_ipi = usecs_div(hctx->ccid3hctx_s, hctx->ccid3hctx_x);
+
+       /* Update nominal send time with regard to the new t_ipi */
+       timeval_add_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi);
+
+       /* Calculate new delta by delta = min(t_ipi / 2, t_gran / 2) */
        hctx->ccid3hctx_delta = min_t(u32, hctx->ccid3hctx_t_ipi / 2,
                                           TFRC_OPSYS_HALF_TIME_GRAN);
 }
-
 /*
  * Update X by
  *    If (p > 0)
@@ -132,6 +131,7 @@ static inline void ccid3_calc_new_delta(struct ccid3_hc_tx_sock *hctx)
 static void ccid3_hc_tx_update_x(struct sock *sk)
 {
        struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
+       const __u32 old_x = hctx->ccid3hctx_x;
 
        /* To avoid large error in calcX */
        if (hctx->ccid3hctx_p >= TFRC_SMALLEST_P) {
@@ -155,21 +155,21 @@ static void ccid3_hc_tx_update_x(struct sock *sk)
                        hctx->ccid3hctx_t_ld = now;
                }
        }
+       if (hctx->ccid3hctx_x != old_x)
+               ccid3_update_send_time(hctx);
 }
 
 static void ccid3_hc_tx_no_feedback_timer(unsigned long data)
 {
        struct sock *sk = (struct sock *)data;
-       unsigned long next_tmout = 0;
        struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
+       unsigned long next_tmout = USEC_PER_SEC / 5;
 
        bh_lock_sock(sk);
        if (sock_owned_by_user(sk)) {
                /* Try again later. */
                /* XXX: set some sensible MIB */
-               sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer,
-                              jiffies + HZ / 5);
-               goto out;
+               goto restart_timer;
        }
 
        ccid3_pr_debug("%s, sk=%p, state=%s\n", dccp_role(sk), sk,
@@ -189,14 +189,10 @@ static void ccid3_hc_tx_no_feedback_timer(unsigned long data)
                               dccp_role(sk), sk,
                               ccid3_tx_state_name(hctx->ccid3hctx_state),
                               hctx->ccid3hctx_x);
-               next_tmout = max_t(u32, 2 * usecs_div(hctx->ccid3hctx_s,
-                                                     hctx->ccid3hctx_x),
-                                       TFRC_INITIAL_TIMEOUT);
-               /*
-                * FIXME - not sure above calculation is correct. See section
-                * 5 of CCID3 11 should adjust tx_t_ipi and double that to
-                * achieve it really
-                */
+               /* The value of R is still undefined and so we can not recompute
+                * the timout value. Keep initial value as per [RFC 4342, 5]. */
+               next_tmout = TFRC_INITIAL_TIMEOUT;
+               ccid3_update_send_time(hctx);
                break;
        case TFRC_SSTATE_FBACK:
                /*
@@ -232,11 +228,10 @@ static void ccid3_hc_tx_no_feedback_timer(unsigned long data)
                }
                /*
                 * Schedule no feedback timer to expire in
-                * max(4 * R, 2 * s / X)
+                * max(4 * t_RTO, 2 * s/X)  =  max(4 * t_RTO, 2 * t_ipi)
+                * XXX This is non-standard, RFC 3448, 4.3 uses 4 * R
                 */
-               next_tmout = max_t(u32, hctx->ccid3hctx_t_rto, 
-                                       2 * usecs_div(hctx->ccid3hctx_s,
-                                                     hctx->ccid3hctx_x));
+               next_tmout = max(hctx->ccid3hctx_t_rto, 2*hctx->ccid3hctx_t_ipi);
                break;
        case TFRC_SSTATE_NO_SENT:
                DCCP_BUG("Illegal %s state NO_SENT, sk=%p", dccp_role(sk), sk);
@@ -245,14 +240,22 @@ static void ccid3_hc_tx_no_feedback_timer(unsigned long data)
                goto out;
        }
 
-       sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer, 
-                     jiffies + max_t(u32, 1, usecs_to_jiffies(next_tmout)));
        hctx->ccid3hctx_idle = 1;
+
+restart_timer:
+       sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer,
+                          jiffies + usecs_to_jiffies(next_tmout));
 out:
        bh_unlock_sock(sk);
        sock_put(sk);
 }
 
+/*
+ * returns
+ *   > 0: delay (in msecs) that should pass before actually sending
+ *   = 0: can send immediately
+ *   < 0: error condition; do not send packet
+ */
 static int ccid3_hc_tx_send_packet(struct sock *sk,
                                   struct sk_buff *skb, int len)
 {
@@ -261,17 +264,16 @@ static int ccid3_hc_tx_send_packet(struct sock *sk,
        struct dccp_tx_hist_entry *new_packet;
        struct timeval now;
        long delay;
-       int rc = -ENOTCONN;
 
        BUG_ON(hctx == NULL);
 
-       /* Check if pure ACK or Terminating*/
        /*
-        * XXX: We only call this function for DATA and DATAACK, on, these
-        * packets can have zero length, but why the comment about "pure ACK"?
+        * This function is called only for Data and DataAck packets. Sending
+        * zero-sized Data(Ack)s is theoretically possible, but for congestion
+        * control this case is pathological - ignore it.
         */
        if (unlikely(len == 0))
-               goto out;
+               return -EBADMSG;
 
        /* See if last packet allocated was not sent */
        new_packet = dccp_tx_hist_head(&hctx->ccid3hctx_hist);
@@ -279,11 +281,10 @@ static int ccid3_hc_tx_send_packet(struct sock *sk,
                new_packet = dccp_tx_hist_entry_new(ccid3_tx_hist,
                                                    SLAB_ATOMIC);
 
-               rc = -ENOBUFS;
                if (unlikely(new_packet == NULL)) {
                        DCCP_WARN("%s, sk=%p, not enough mem to add to history,"
                                  "send refused\n", dccp_role(sk), sk);
-                       goto out;
+                       return -ENOBUFS;
                }
 
                dccp_tx_hist_add_entry(&hctx->ccid3hctx_hist, new_packet);
@@ -298,40 +299,41 @@ static int ccid3_hc_tx_send_packet(struct sock *sk,
                hctx->ccid3hctx_last_win_count   = 0;
                hctx->ccid3hctx_t_last_win_count = now;
                ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK);
-               hctx->ccid3hctx_t_ipi = TFRC_INITIAL_IPI;
 
-               /* Set nominal send time for initial packet */
+               /* First timeout, according to [RFC 3448, 4.2], is 1 second */
+               hctx->ccid3hctx_t_ipi = USEC_PER_SEC;
+               /* Initial delta: minimum of 0.5 sec and t_gran/2 */
+               hctx->ccid3hctx_delta = TFRC_OPSYS_HALF_TIME_GRAN;
+
+               /* Set t_0 for initial packet */
                hctx->ccid3hctx_t_nom = now;
-               timeval_add_usecs(&hctx->ccid3hctx_t_nom,
-                                 hctx->ccid3hctx_t_ipi);
-               ccid3_calc_new_delta(hctx);
-               rc = 0;
                break;
        case TFRC_SSTATE_NO_FBACK:
        case TFRC_SSTATE_FBACK:
-               delay = (timeval_delta(&now, &hctx->ccid3hctx_t_nom) -
-                        hctx->ccid3hctx_delta);
-               delay /= -1000;
-               /* divide by -1000 is to convert to ms and get sign right */
-               rc = delay > 0 ? delay : 0;
+               delay = timeval_delta(&hctx->ccid3hctx_t_nom, &now);
+               /*
+                *      Scheduling of packet transmissions [RFC 3448, 4.6]
+                *
+                * if (t_now > t_nom - delta)
+                *       // send the packet now
+                * else
+                *       // send the packet in (t_nom - t_now) milliseconds.
+                */
+               if (delay >= hctx->ccid3hctx_delta)
+                       return delay / 1000L;
                break;
        case TFRC_SSTATE_TERM:
                DCCP_BUG("Illegal %s state TERM, sk=%p", dccp_role(sk), sk);
-               rc = -EINVAL;
-               break;
+               return -EINVAL;
        }
 
-       /* Can we send? if so add options and add to packet history */
-       if (rc == 0) {
-               dp->dccps_hc_tx_insert_options = 1;
-               new_packet->dccphtx_ccval =
-                       DCCP_SKB_CB(skb)->dccpd_ccval =
-                               hctx->ccid3hctx_last_win_count;
-               timeval_add_usecs(&hctx->ccid3hctx_t_nom,
-                                 hctx->ccid3hctx_t_ipi);
-       }
-out:
-       return rc;
+       /* prepare to send now (add options etc.) */
+       dp->dccps_hc_tx_insert_options = 1;
+       new_packet->dccphtx_ccval = DCCP_SKB_CB(skb)->dccpd_ccval =
+                                   hctx->ccid3hctx_last_win_count;
+       timeval_add_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi);
+
+       return 0;
 }
 
 static void ccid3_hc_tx_packet_sent(struct sock *sk, int more, int len)
@@ -385,29 +387,6 @@ static void ccid3_hc_tx_packet_sent(struct sock *sk, int more, int len)
        } else
                ccid3_pr_debug("%s, sk=%p, seqno=%llu NOT inserted!\n",
                               dccp_role(sk), sk, dp->dccps_gss);
-
-       switch (hctx->ccid3hctx_state) {
-       case TFRC_SSTATE_NO_SENT:
-               /* if first wasn't pure ack */
-               if (len != 0)
-                       DCCP_CRIT("%s, First packet sent is noted "
-                                 "as a data packet",  dccp_role(sk));
-               return;
-       case TFRC_SSTATE_NO_FBACK:
-       case TFRC_SSTATE_FBACK:
-               if (len > 0) {
-                       timeval_sub_usecs(&hctx->ccid3hctx_t_nom,
-                                 hctx->ccid3hctx_t_ipi);
-                       ccid3_calc_new_t_ipi(hctx);
-                       ccid3_calc_new_delta(hctx);
-                       timeval_add_usecs(&hctx->ccid3hctx_t_nom,
-                                         hctx->ccid3hctx_t_ipi);
-               }
-               break;
-       case TFRC_SSTATE_TERM:
-               DCCP_BUG("Illegal %s state TERM, sk=%p", dccp_role(sk), sk);
-               break;
-       }
 }
 
 static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
@@ -437,9 +416,6 @@ static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
        pinv = opt_recv->ccid3or_loss_event_rate;
 
        switch (hctx->ccid3hctx_state) {
-       case TFRC_SSTATE_NO_SENT:
-               /* FIXME: what to do here? */
-               return;
        case TFRC_SSTATE_NO_FBACK:
        case TFRC_SSTATE_FBACK:
                /* Calculate new round trip sample by
@@ -483,10 +459,6 @@ static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
                               "r_sample=%us\n", dccp_role(sk), sk,
                               hctx->ccid3hctx_rtt, r_sample);
 
-               /* Update timeout interval */
-               hctx->ccid3hctx_t_rto = max_t(u32, 4 * hctx->ccid3hctx_rtt,
-                                             USEC_PER_SEC);
-
                /* Update receive rate */
                hctx->ccid3hctx_x_recv = x_recv;/* X_recv in bytes per sec */
 
@@ -506,33 +478,30 @@ static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
                /* unschedule no feedback timer */
                sk_stop_timer(sk, &hctx->ccid3hctx_no_feedback_timer);
 
-               /* Update sending rate */
+               /* Update sending rate (and likely t_ipi, t_nom, and delta) */
                ccid3_hc_tx_update_x(sk);
 
-               /* Update next send time */
-               timeval_sub_usecs(&hctx->ccid3hctx_t_nom,
-                                 hctx->ccid3hctx_t_ipi);
-               ccid3_calc_new_t_ipi(hctx);
-               timeval_add_usecs(&hctx->ccid3hctx_t_nom,
-                                 hctx->ccid3hctx_t_ipi);
-               ccid3_calc_new_delta(hctx);
-
                /* remove all packets older than the one acked from history */
                dccp_tx_hist_purge_older(ccid3_tx_hist,
                                         &hctx->ccid3hctx_hist, packet);
                /*
                 * As we have calculated new ipi, delta, t_nom it is possible that
-                * we now can send a packet, so wake up dccp_wait_for_ccids.
+                * we now can send a packet, so wake up dccp_wait_for_ccid
                 */
                sk->sk_write_space(sk);
 
+
+               /* Update timeout interval. We use the alternative variant of
+                * [RFC 3448, 3.1] which sets the upper bound of t_rto to one
+                * second, as it is suggested for TCP (see RFC 2988, 2.4). */
+               hctx->ccid3hctx_t_rto = max_t(u32, 4 * hctx->ccid3hctx_rtt,
+                                                  USEC_PER_SEC            );
                /*
                 * Schedule no feedback timer to expire in
-                * max(4 * R, 2 * s / X)
+                * max(4 * t_RTO, 2 * s/X)  =  max(4 * t_RTO, 2 * t_ipi)
+                * XXX This is non-standard, RFC 3448, 4.3 uses 4 * R
                 */
-               next_tmout = max(hctx->ccid3hctx_t_rto,
-                                2 * usecs_div(hctx->ccid3hctx_s,
-                                              hctx->ccid3hctx_x));
+               next_tmout = max(hctx->ccid3hctx_t_rto, 2*hctx->ccid3hctx_t_ipi);
                        
                ccid3_pr_debug("%s, sk=%p, Scheduled no feedback timer to "
                               "expire in %lu jiffies (%luus)\n",
@@ -540,13 +509,15 @@ static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
                               usecs_to_jiffies(next_tmout), next_tmout); 
 
                sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer, 
-                              jiffies + max_t(u32, 1, usecs_to_jiffies(next_tmout)));
+                                  jiffies + usecs_to_jiffies(next_tmout));
 
                /* set idle flag */
                hctx->ccid3hctx_idle = 1;   
                break;
-       case TFRC_SSTATE_TERM:
-               DCCP_BUG("Illegal %s state TERM, sk=%p", dccp_role(sk), sk);
+       case TFRC_SSTATE_NO_SENT:
+               DCCP_WARN("Illegal ACK received - no packet has been sent\n");
+               /* fall through */
+       case TFRC_SSTATE_TERM:          /* ignore feedback when closing */
                break;
        }
 }
@@ -636,7 +607,6 @@ static int ccid3_hc_tx_init(struct ccid *ccid, struct sock *sk)
 
        /* Set transmission rate to 1 packet per second */
        hctx->ccid3hctx_x     = hctx->ccid3hctx_s;
-       hctx->ccid3hctx_t_rto = USEC_PER_SEC;
        hctx->ccid3hctx_state = TFRC_SSTATE_NO_SENT;
        INIT_LIST_HEAD(&hctx->ccid3hctx_hist);