rxrpc: Need to produce an ACK for service op if op takes a long time
[cascardo/linux.git] / net / rxrpc / call_event.c
index 0e84780..97a17ad 100644 (file)
 /*
  * Set the timer
  */
-void rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why)
+void __rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why,
+                      ktime_t now)
 {
-       unsigned long t, now = jiffies;
-
-       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->expire_at;
-               if (time_before_eq(t, now))
+               if (!ktime_after(t, now)) {
+                       trace_rxrpc_timer(call, why, now, now_j);
+                       queue = true;
                        goto out;
+               }
 
-               if (time_after(call->resend_at, now) &&
-                   time_before(call->resend_at, t))
+               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_after(call->ack_at, now) &&
-                   time_before(call->ack_at, 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 (call->timer.expires != t || !timer_pending(&call->timer)) {
-                       mod_timer(&call->timer, t);
-                       trace_rxrpc_timer(call, why, now);
+               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
  */
@@ -62,9 +125,18 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                                enum rxrpc_propose_ack_trace why)
 {
        enum rxrpc_propose_ack_outcome outcome = rxrpc_propose_ack_use;
-       unsigned long now, ack_at, expiry = rxrpc_soft_ack_delay;
+       unsigned int expiry = rxrpc_soft_ack_delay;
+       ktime_t now, ack_at;
        s8 prior = rxrpc_ack_priority[ack_reason];
 
+       /* 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.
         */
@@ -100,7 +172,6 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                        expiry = rxrpc_soft_ack_delay;
                break;
 
-       case RXRPC_ACK_PING:
        case RXRPC_ACK_IDLE:
                if (rxrpc_idle_ack_delay < expiry)
                        expiry = rxrpc_idle_ack_delay;
@@ -111,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) {
@@ -120,11 +190,11 @@ 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_timer_set_for_ack);
+                       rxrpc_set_timer(call, rxrpc_timer_set_for_ack, now);
                }
        }
 
@@ -157,12 +227,12 @@ static void rxrpc_congestion_timeout(struct rxrpc_call *call)
 /*
  * 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, ack_ts;
+       ktime_t max_age, oldest, ack_ts;
        int ix;
        u8 annotation, anno_type, retrans = 0, unacked = 0;
 
@@ -212,14 +282,7 @@ static void rxrpc_resend(struct rxrpc_call *call)
                                       ktime_to_ns(ktime_sub(skb->tstamp, max_age)));
        }
 
-       resend_at = ktime_add_ms(oldest, rxrpc_resend_timeout);
-       call->resend_at = jiffies +
-               nsecs_to_jiffies(ktime_to_ns(ktime_sub(resend_at, now))) +
-               1; /* We have to make sure that the calculated jiffies value
-                   * falls at or after the nsec value, or we shall loop
-                   * ceaselessly because the timer times out, but we haven't
-                   * reached the nsec timeout yet.
-                   */
+       call->resend_at = ktime_add_ms(oldest, rxrpc_resend_timeout);
 
        if (unacked)
                rxrpc_congestion_timeout(call);
@@ -229,14 +292,14 @@ static void rxrpc_resend(struct rxrpc_call *call)
         * retransmitting data.
         */
        if (!retrans) {
-               rxrpc_set_timer(call, rxrpc_timer_set_for_resend);
+               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_call_packet(call, RXRPC_PACKET_TYPE_ACK);
+               rxrpc_send_ack_packet(call, true);
                goto out;
        }
 
@@ -256,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;
                }
@@ -301,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);
 
@@ -311,38 +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_timer_set_for_resend);
+       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) {