rxrpc: Need to produce an ACK for service op if op takes a long time
[cascardo/linux.git] / net / rxrpc / call_event.c
index 6e2ea8f..97a17ad 100644 (file)
 /*
  * Set the timer
  */
-static void rxrpc_set_timer(struct rxrpc_call *call)
+void __rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why,
+                      ktime_t now)
 {
-       unsigned long t, now = jiffies;
-
-       _enter("{%ld,%ld,%ld:%ld}",
-              call->ack_at - now, call->resend_at - now, call->expire_at - now,
-              call->timer.expires - now);
-
-       read_lock_bh(&call->state_lock);
+       unsigned long t_j, now_j = jiffies;
+       ktime_t t;
+       bool queue = false;
 
        if (call->state < RXRPC_CALL_COMPLETE) {
-               t = call->ack_at;
-               if (time_before(call->resend_at, t))
+               t = call->expire_at;
+               if (!ktime_after(t, now)) {
+                       trace_rxrpc_timer(call, why, now, now_j);
+                       queue = true;
+                       goto out;
+               }
+
+               if (!ktime_after(call->resend_at, now)) {
+                       call->resend_at = call->expire_at;
+                       if (!test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events))
+                               queue = true;
+               } else if (ktime_before(call->resend_at, t)) {
                        t = call->resend_at;
-               if (time_before(call->expire_at, t))
-                       t = call->expire_at;
-               if (!timer_pending(&call->timer) ||
-                   time_before(t, call->timer.expires)) {
-                       _debug("set timer %ld", t - now);
-                       mod_timer(&call->timer, t);
+               }
+
+               if (!ktime_after(call->ack_at, now)) {
+                       call->ack_at = call->expire_at;
+                       if (!test_and_set_bit(RXRPC_CALL_EV_ACK, &call->events))
+                               queue = true;
+               } else if (ktime_before(call->ack_at, t)) {
+                       t = call->ack_at;
+               }
+
+               if (!ktime_after(call->ping_at, now)) {
+                       call->ping_at = call->expire_at;
+                       if (!test_and_set_bit(RXRPC_CALL_EV_PING, &call->events))
+                               queue = true;
+               } else if (ktime_before(call->ping_at, t)) {
+                       t = call->ping_at;
+               }
+
+               t_j = nsecs_to_jiffies(ktime_to_ns(ktime_sub(t, now)));
+               t_j += jiffies;
+
+               /* We have to make sure that the calculated jiffies value falls
+                * at or after the nsec value, or we may loop ceaselessly
+                * because the timer times out, but we haven't reached the nsec
+                * timeout yet.
+                */
+               t_j++;
+
+               if (call->timer.expires != t_j || !timer_pending(&call->timer)) {
+                       mod_timer(&call->timer, t_j);
+                       trace_rxrpc_timer(call, why, now, now_j);
                }
        }
+
+out:
+       if (queue)
+               rxrpc_queue_call(call);
+}
+
+/*
+ * Set the timer
+ */
+void rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why,
+                    ktime_t now)
+{
+       read_lock_bh(&call->state_lock);
+       __rxrpc_set_timer(call, why, now);
        read_unlock_bh(&call->state_lock);
 }
 
+/*
+ * Propose a PING ACK be sent.
+ */
+static void rxrpc_propose_ping(struct rxrpc_call *call,
+                              bool immediate, bool background)
+{
+       if (immediate) {
+               if (background &&
+                   !test_and_set_bit(RXRPC_CALL_EV_PING, &call->events))
+                       rxrpc_queue_call(call);
+       } else {
+               ktime_t now = ktime_get_real();
+               ktime_t ping_at = ktime_add_ms(now, rxrpc_idle_ack_delay);
+
+               if (ktime_before(ping_at, call->ping_at)) {
+                       call->ping_at = ping_at;
+                       rxrpc_set_timer(call, rxrpc_timer_set_for_ping, now);
+               }
+       }
+}
+
 /*
  * propose an ACK be sent
  */
 static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                                u16 skew, u32 serial, bool immediate,
-                               bool background)
+                               bool background,
+                               enum rxrpc_propose_ack_trace why)
 {
-       unsigned long now, ack_at, expiry = rxrpc_soft_ack_delay;
+       enum rxrpc_propose_ack_outcome outcome = rxrpc_propose_ack_use;
+       unsigned int expiry = rxrpc_soft_ack_delay;
+       ktime_t now, ack_at;
        s8 prior = rxrpc_ack_priority[ack_reason];
 
-       _enter("{%d},%s,%%%x,%u",
-              call->debug_id, rxrpc_acks(ack_reason), serial, immediate);
+       /* Pings are handled specially because we don't want to accidentally
+        * lose a ping response by subsuming it into a ping.
+        */
+       if (ack_reason == RXRPC_ACK_PING) {
+               rxrpc_propose_ping(call, immediate, background);
+               goto trace;
+       }
 
        /* Update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial
         * numbers, but we don't alter the timeout.
@@ -70,15 +145,18 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
               call->ackr_reason, rxrpc_ack_priority[call->ackr_reason]);
        if (ack_reason == call->ackr_reason) {
                if (RXRPC_ACK_UPDATEABLE & (1 << ack_reason)) {
+                       outcome = rxrpc_propose_ack_update;
                        call->ackr_serial = serial;
                        call->ackr_skew = skew;
                }
                if (!immediate)
-                       return;
+                       goto trace;
        } else if (prior > rxrpc_ack_priority[call->ackr_reason]) {
                call->ackr_reason = ack_reason;
                call->ackr_serial = serial;
                call->ackr_skew = skew;
+       } else {
+               outcome = rxrpc_propose_ack_subsume;
        }
 
        switch (ack_reason) {
@@ -104,7 +182,6 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                break;
        }
 
-       now = jiffies;
        if (test_bit(RXRPC_CALL_EV_ACK, &call->events)) {
                _debug("already scheduled");
        } else if (immediate || expiry == 0) {
@@ -113,38 +190,51 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                    background)
                        rxrpc_queue_call(call);
        } else {
-               ack_at = now + expiry;
-               _debug("deferred ACK %ld < %ld", expiry, call->ack_at - now);
-               if (time_before(ack_at, call->ack_at)) {
+               now = ktime_get_real();
+               ack_at = ktime_add_ms(now, expiry);
+               if (ktime_before(ack_at, call->ack_at)) {
                        call->ack_at = ack_at;
-                       rxrpc_set_timer(call);
+                       rxrpc_set_timer(call, rxrpc_timer_set_for_ack, now);
                }
        }
+
+trace:
+       trace_rxrpc_propose_ack(call, why, ack_reason, serial, immediate,
+                               background, outcome);
 }
 
 /*
  * propose an ACK be sent, locking the call structure
  */
 void rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
-                      u16 skew, u32 serial, bool immediate, bool background)
+                      u16 skew, u32 serial, bool immediate, bool background,
+                      enum rxrpc_propose_ack_trace why)
 {
        spin_lock_bh(&call->lock);
        __rxrpc_propose_ACK(call, ack_reason, skew, serial,
-                           immediate, background);
+                           immediate, background, why);
        spin_unlock_bh(&call->lock);
 }
 
+/*
+ * Handle congestion being detected by the retransmit timeout.
+ */
+static void rxrpc_congestion_timeout(struct rxrpc_call *call)
+{
+       set_bit(RXRPC_CALL_RETRANS_TIMEOUT, &call->flags);
+}
+
 /*
  * Perform retransmission of NAK'd and unack'd packets.
  */
-static void rxrpc_resend(struct rxrpc_call *call)
+static void rxrpc_resend(struct rxrpc_call *call, ktime_t now)
 {
        struct rxrpc_skb_priv *sp;
        struct sk_buff *skb;
        rxrpc_seq_t cursor, seq, top;
-       ktime_t now = ktime_get_real(), max_age, oldest,  resend_at;
+       ktime_t max_age, oldest, ack_ts;
        int ix;
-       u8 annotation, anno_type;
+       u8 annotation, anno_type, retrans = 0, unacked = 0;
 
        _enter("{%d,%d}", call->tx_hard_ack, call->tx_top);
 
@@ -181,14 +271,37 @@ static void rxrpc_resend(struct rxrpc_call *call)
                                        oldest = skb->tstamp;
                                continue;
                        }
+                       if (!(annotation & RXRPC_TX_ANNO_RESENT))
+                               unacked++;
                }
 
                /* Okay, we need to retransmit a packet. */
                call->rxtx_annotations[ix] = RXRPC_TX_ANNO_RETRANS | annotation;
+               retrans++;
+               trace_rxrpc_retransmit(call, seq, annotation | anno_type,
+                                      ktime_to_ns(ktime_sub(skb->tstamp, max_age)));
        }
 
-       resend_at = ktime_sub(ktime_add_ns(oldest, rxrpc_resend_timeout), now);
-       call->resend_at = jiffies + nsecs_to_jiffies(ktime_to_ns(resend_at));
+       call->resend_at = ktime_add_ms(oldest, rxrpc_resend_timeout);
+
+       if (unacked)
+               rxrpc_congestion_timeout(call);
+
+       /* If there was nothing that needed retransmission then it's likely
+        * that an ACK got lost somewhere.  Send a ping to find out instead of
+        * retransmitting data.
+        */
+       if (!retrans) {
+               rxrpc_set_timer(call, rxrpc_timer_set_for_resend, now);
+               spin_unlock_bh(&call->lock);
+               ack_ts = ktime_sub(now, call->acks_latest_ts);
+               if (ktime_to_ns(ack_ts) < call->peer->rtt)
+                       goto out;
+               rxrpc_propose_ACK(call, RXRPC_ACK_PING, 0, 0, true, false,
+                                 rxrpc_propose_ack_ping_for_lost_ack);
+               rxrpc_send_ack_packet(call, true);
+               goto out;
+       }
 
        /* Now go through the Tx window and perform the retransmissions.  We
         * have to drop the lock for each send.  If an ACK comes in whilst the
@@ -206,7 +319,7 @@ static void rxrpc_resend(struct rxrpc_call *call)
                rxrpc_get_skb(skb, rxrpc_skb_tx_got);
                spin_unlock_bh(&call->lock);
 
-               if (rxrpc_send_data_packet(call, skb) < 0) {
+               if (rxrpc_send_data_packet(call, skb, true) < 0) {
                        rxrpc_free_skb(skb, rxrpc_skb_tx_freed);
                        return;
                }
@@ -240,6 +353,7 @@ static void rxrpc_resend(struct rxrpc_call *call)
 
 out_unlock:
        spin_unlock_bh(&call->lock);
+out:
        _leave("");
 }
 
@@ -250,7 +364,7 @@ void rxrpc_process_call(struct work_struct *work)
 {
        struct rxrpc_call *call =
                container_of(work, struct rxrpc_call, processor);
-       unsigned long now;
+       ktime_t now;
 
        rxrpc_see_call(call);
 
@@ -260,37 +374,41 @@ void rxrpc_process_call(struct work_struct *work)
 
 recheck_state:
        if (test_and_clear_bit(RXRPC_CALL_EV_ABORT, &call->events)) {
-               rxrpc_send_call_packet(call, RXRPC_PACKET_TYPE_ABORT);
+               rxrpc_send_abort_packet(call);
                goto recheck_state;
        }
 
        if (call->state == RXRPC_CALL_COMPLETE) {
                del_timer_sync(&call->timer);
+               rxrpc_notify_socket(call);
                goto out_put;
        }
 
-       now = jiffies;
-       if (time_after_eq(now, call->expire_at)) {
+       now = ktime_get_real();
+       if (ktime_before(call->expire_at, now)) {
                rxrpc_abort_call("EXP", call, 0, RX_CALL_TIMEOUT, ETIME);
                set_bit(RXRPC_CALL_EV_ABORT, &call->events);
+               goto recheck_state;
        }
 
-       if (test_and_clear_bit(RXRPC_CALL_EV_ACK, &call->events) ||
-           time_after_eq(now, call->ack_at)) {
-               call->ack_at = call->expire_at;
+       if (test_and_clear_bit(RXRPC_CALL_EV_ACK, &call->events)) {
                if (call->ackr_reason) {
-                       rxrpc_send_call_packet(call, RXRPC_PACKET_TYPE_ACK);
+                       rxrpc_send_ack_packet(call, false);
                        goto recheck_state;
                }
        }
 
-       if (test_and_clear_bit(RXRPC_CALL_EV_RESEND, &call->events) ||
-           time_after_eq(now, call->resend_at)) {
-               rxrpc_resend(call);
+       if (test_and_clear_bit(RXRPC_CALL_EV_PING, &call->events)) {
+               rxrpc_send_ack_packet(call, true);
+               goto recheck_state;
+       }
+
+       if (test_and_clear_bit(RXRPC_CALL_EV_RESEND, &call->events)) {
+               rxrpc_resend(call, now);
                goto recheck_state;
        }
 
-       rxrpc_set_timer(call);
+       rxrpc_set_timer(call, rxrpc_timer_set_for_resend, now);
 
        /* other events may have been raised since we started checking */
        if (call->events && call->state < RXRPC_CALL_COMPLETE) {