[DCCP] ccid3: Consolidate handling of t_RTO
[linux-2.6-block.git] / net / dccp / ccids / ccid3.c
index 62c304200eda7f69d15e877f145cb10684e66899..f0ed67c84a556672221ab73dfed52896fa0cce43 100644 (file)
@@ -100,19 +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)
 {
+       timeval_sub_usecs(&hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi);
+
+       /* 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);
-}
 
-/* 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)
-{
+       /* 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)
@@ -126,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) {
@@ -149,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,
@@ -183,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:
                /*
@@ -226,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);
@@ -239,9 +240,11 @@ 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);
@@ -384,27 +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:
-               /* fall through */
-       case TFRC_SSTATE_NO_FBACK:
-               /* t_nom, t_ipi, delta do not change until feedback arrives */
-               return;
-       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)
@@ -434,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
@@ -480,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 */
 
@@ -503,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",
@@ -537,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;
        }
 }
@@ -633,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);