Merge tag 'rxrpc-rewrite-20160923' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorDavid S. Miller <davem@davemloft.net>
Sat, 24 Sep 2016 12:24:19 +0000 (08:24 -0400)
committerDavid S. Miller <davem@davemloft.net>
Sat, 24 Sep 2016 12:24:19 +0000 (08:24 -0400)
David Howells says:

====================
rxrpc: Bug fixes and tracepoints

Here are a bunch of bug fixes:

 (1) Need to set the timestamp on a Tx packet before queueing it to avoid
     trouble with the retransmission function.

 (2) Don't send an ACK at the end of the service reply transmission; it's
     the responsibility of the client to send an ACK to close the call.
     The service can resend the last DATA packet or send a PING ACK.

 (3) Wake sendmsg() on abnormal call termination.

 (4) Use ktime_add_ms() not ktime_add_ns() to add millisecond offsets.

 (5) Use before_eq() & co. to compare serial numbers (which may wrap).

 (6) Start the resend timer on DATA packet transmission.

 (7) Don't accidentally cancel a retransmission upon receiving a NACK.

 (8) Fix the call timer setting function to deal with timeouts that are now
     or past.

 (9) Don't use a flag to communicate the presence of the last packet in the
     Tx buffer from sendmsg to the input routines where ACK and DATA
     reception is handled.  The problem is that there's a window between
     queueing the last packet for transmission and setting the flag in
     which ACKs or reply DATA packets can arrive, causing apparent state
     machine violation issues.

     Instead use the annotation buffer to mark the last packet and pick up
     and set the flag in the input routines.

(10) Don't call the tx_ack tracepoint and don't allocate a serial number if
     someone else nicked the ACK we were about to transmit.

There are also new tracepoints and one altered tracepoint used to track
down the above bugs:

(11) Call timer tracepoint.

(12) Data Tx tracepoint (and adjustments to ACK tracepoint).

(13) Injected Rx packet loss tracepoint.

(14) Ack proposal tracepoint.

(15) Retransmission selection tracepoint.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
include/rxrpc/packet.h
include/trace/events/rxrpc.h
net/rxrpc/ar-internal.h
net/rxrpc/call_event.c
net/rxrpc/call_object.c
net/rxrpc/conn_event.c
net/rxrpc/input.c
net/rxrpc/misc.c
net/rxrpc/output.c
net/rxrpc/recvmsg.c
net/rxrpc/sendmsg.c

index fd6eb3a..703a64b 100644 (file)
@@ -123,6 +123,7 @@ struct rxrpc_ackpacket {
 #define RXRPC_ACK_PING_RESPONSE                7       /* response to RXRPC_ACK_PING */
 #define RXRPC_ACK_DELAY                        8       /* nothing happened since received packet */
 #define RXRPC_ACK_IDLE                 9       /* ACK due to fully received ACK window */
+#define RXRPC_ACK__INVALID             10      /* Representation of invalid ACK reason */
 
        uint8_t         nAcks;          /* number of ACKs */
 #define RXRPC_MAXACKS  255
index e8f2afb..5647549 100644 (file)
@@ -251,38 +251,72 @@ TRACE_EVENT(rxrpc_rx_ack,
 
            TP_printk("c=%p %s f=%08x n=%u",
                      __entry->call,
-                     rxrpc_acks(__entry->reason),
+                     rxrpc_ack_names[__entry->reason],
                      __entry->first,
                      __entry->n_acks)
            );
 
+TRACE_EVENT(rxrpc_tx_data,
+           TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq,
+                    rxrpc_serial_t serial, u8 flags, bool lose),
+
+           TP_ARGS(call, seq, serial, flags, lose),
+
+           TP_STRUCT__entry(
+                   __field(struct rxrpc_call *,        call            )
+                   __field(rxrpc_seq_t,                seq             )
+                   __field(rxrpc_serial_t,             serial          )
+                   __field(u8,                         flags           )
+                   __field(bool,                       lose            )
+                            ),
+
+           TP_fast_assign(
+                   __entry->call = call;
+                   __entry->seq = seq;
+                   __entry->serial = serial;
+                   __entry->flags = flags;
+                   __entry->lose = lose;
+                          ),
+
+           TP_printk("c=%p DATA %08x q=%08x fl=%02x%s",
+                     __entry->call,
+                     __entry->serial,
+                     __entry->seq,
+                     __entry->flags,
+                     __entry->lose ? " *LOSE*" : "")
+           );
+
 TRACE_EVENT(rxrpc_tx_ack,
-           TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t first,
-                    rxrpc_serial_t serial, u8 reason, u8 n_acks),
+           TP_PROTO(struct rxrpc_call *call, rxrpc_serial_t serial,
+                    rxrpc_seq_t ack_first, rxrpc_serial_t ack_serial,
+                    u8 reason, u8 n_acks),
 
-           TP_ARGS(call, first, serial, reason, n_acks),
+           TP_ARGS(call, serial, ack_first, ack_serial, reason, n_acks),
 
            TP_STRUCT__entry(
                    __field(struct rxrpc_call *,        call            )
-                   __field(rxrpc_seq_t,                first           )
                    __field(rxrpc_serial_t,             serial          )
+                   __field(rxrpc_seq_t,                ack_first       )
+                   __field(rxrpc_serial_t,             ack_serial      )
                    __field(u8,                         reason          )
                    __field(u8,                         n_acks          )
                             ),
 
            TP_fast_assign(
                    __entry->call = call;
-                   __entry->first = first;
                    __entry->serial = serial;
+                   __entry->ack_first = ack_first;
+                   __entry->ack_serial = ack_serial;
                    __entry->reason = reason;
                    __entry->n_acks = n_acks;
                           ),
 
-           TP_printk("c=%p %s f=%08x r=%08x n=%u",
+           TP_printk(" c=%p ACK  %08x %s f=%08x r=%08x n=%u",
                      __entry->call,
-                     rxrpc_acks(__entry->reason),
-                     __entry->first,
                      __entry->serial,
+                     rxrpc_ack_names[__entry->reason],
+                     __entry->ack_first,
+                     __entry->ack_serial,
                      __entry->n_acks)
            );
 
@@ -414,6 +448,128 @@ TRACE_EVENT(rxrpc_rtt_rx,
                      __entry->avg)
            );
 
+TRACE_EVENT(rxrpc_timer,
+           TP_PROTO(struct rxrpc_call *call, enum rxrpc_timer_trace why,
+                    unsigned long now),
+
+           TP_ARGS(call, why, now),
+
+           TP_STRUCT__entry(
+                   __field(struct rxrpc_call *,                call            )
+                   __field(enum rxrpc_timer_trace,             why             )
+                   __field(unsigned long,                      now             )
+                   __field(unsigned long,                      expire_at       )
+                   __field(unsigned long,                      ack_at          )
+                   __field(unsigned long,                      resend_at       )
+                   __field(unsigned long,                      timer           )
+                            ),
+
+           TP_fast_assign(
+                   __entry->call       = call;
+                   __entry->why        = why;
+                   __entry->now        = now;
+                   __entry->expire_at  = call->expire_at;
+                   __entry->ack_at     = call->ack_at;
+                   __entry->resend_at  = call->resend_at;
+                   __entry->timer      = call->timer.expires;
+                          ),
+
+           TP_printk("c=%p %s now=%lx x=%ld a=%ld r=%ld t=%ld",
+                     __entry->call,
+                     rxrpc_timer_traces[__entry->why],
+                     __entry->now,
+                     __entry->expire_at - __entry->now,
+                     __entry->ack_at - __entry->now,
+                     __entry->resend_at - __entry->now,
+                     __entry->timer - __entry->now)
+           );
+
+TRACE_EVENT(rxrpc_rx_lose,
+           TP_PROTO(struct rxrpc_skb_priv *sp),
+
+           TP_ARGS(sp),
+
+           TP_STRUCT__entry(
+                   __field_struct(struct rxrpc_host_header,    hdr             )
+                            ),
+
+           TP_fast_assign(
+                   memcpy(&__entry->hdr, &sp->hdr, sizeof(__entry->hdr));
+                          ),
+
+           TP_printk("%08x:%08x:%08x:%04x %08x %08x %02x %02x %s *LOSE*",
+                     __entry->hdr.epoch, __entry->hdr.cid,
+                     __entry->hdr.callNumber, __entry->hdr.serviceId,
+                     __entry->hdr.serial, __entry->hdr.seq,
+                     __entry->hdr.type, __entry->hdr.flags,
+                     __entry->hdr.type <= 15 ? rxrpc_pkts[__entry->hdr.type] : "?UNK")
+           );
+
+TRACE_EVENT(rxrpc_propose_ack,
+           TP_PROTO(struct rxrpc_call *call, enum rxrpc_propose_ack_trace why,
+                    u8 ack_reason, rxrpc_serial_t serial, bool immediate,
+                    bool background, enum rxrpc_propose_ack_outcome outcome),
+
+           TP_ARGS(call, why, ack_reason, serial, immediate, background,
+                   outcome),
+
+           TP_STRUCT__entry(
+                   __field(struct rxrpc_call *,                call            )
+                   __field(enum rxrpc_propose_ack_trace,       why             )
+                   __field(rxrpc_serial_t,                     serial          )
+                   __field(u8,                                 ack_reason      )
+                   __field(bool,                               immediate       )
+                   __field(bool,                               background      )
+                   __field(enum rxrpc_propose_ack_outcome,     outcome         )
+                            ),
+
+           TP_fast_assign(
+                   __entry->call       = call;
+                   __entry->why        = why;
+                   __entry->serial     = serial;
+                   __entry->ack_reason = ack_reason;
+                   __entry->immediate  = immediate;
+                   __entry->background = background;
+                   __entry->outcome    = outcome;
+                          ),
+
+           TP_printk("c=%p %s %s r=%08x i=%u b=%u%s",
+                     __entry->call,
+                     rxrpc_propose_ack_traces[__entry->why],
+                     rxrpc_ack_names[__entry->ack_reason],
+                     __entry->serial,
+                     __entry->immediate,
+                     __entry->background,
+                     rxrpc_propose_ack_outcomes[__entry->outcome])
+           );
+
+TRACE_EVENT(rxrpc_retransmit,
+           TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq, u8 annotation,
+                    s64 expiry),
+
+           TP_ARGS(call, seq, annotation, expiry),
+
+           TP_STRUCT__entry(
+                   __field(struct rxrpc_call *,        call            )
+                   __field(rxrpc_seq_t,                seq             )
+                   __field(u8,                         annotation      )
+                   __field(s64,                        expiry          )
+                            ),
+
+           TP_fast_assign(
+                   __entry->call = call;
+                   __entry->seq = seq;
+                   __entry->annotation = annotation;
+                   __entry->expiry = expiry;
+                          ),
+
+           TP_printk("c=%p q=%x a=%02x xp=%lld",
+                     __entry->call,
+                     __entry->seq,
+                     __entry->annotation,
+                     __entry->expiry)
+           );
+
 #endif /* _TRACE_RXRPC_H */
 
 /* This part must be outside protection */
index b13754a..042dbcc 100644 (file)
@@ -508,7 +508,9 @@ struct rxrpc_call {
 #define RXRPC_TX_ANNO_NAK      2
 #define RXRPC_TX_ANNO_RETRANS  3
 #define RXRPC_TX_ANNO_MASK     0x03
-#define RXRPC_TX_ANNO_RESENT   0x04
+#define RXRPC_TX_ANNO_LAST     0x04
+#define RXRPC_TX_ANNO_RESENT   0x08
+
 #define RXRPC_RX_ANNO_JUMBO    0x3f            /* Jumbo subpacket number + 1 if not zero */
 #define RXRPC_RX_ANNO_JLAST    0x40            /* Set if last element of a jumbo packet */
 #define RXRPC_RX_ANNO_VERIFIED 0x80            /* Set if verified and decrypted */
@@ -621,9 +623,10 @@ extern const char rxrpc_call_traces[rxrpc_call__nr_trace][4];
 enum rxrpc_transmit_trace {
        rxrpc_transmit_wait,
        rxrpc_transmit_queue,
-       rxrpc_transmit_queue_reqack,
        rxrpc_transmit_queue_last,
        rxrpc_transmit_rotate,
+       rxrpc_transmit_rotate_last,
+       rxrpc_transmit_await_reply,
        rxrpc_transmit_end,
        rxrpc_transmit__nr_trace
 };
@@ -675,8 +678,39 @@ enum rxrpc_rtt_rx_trace {
 
 extern const char rxrpc_rtt_rx_traces[rxrpc_rtt_rx__nr_trace][5];
 
+enum rxrpc_timer_trace {
+       rxrpc_timer_begin,
+       rxrpc_timer_expired,
+       rxrpc_timer_set_for_ack,
+       rxrpc_timer_set_for_resend,
+       rxrpc_timer_set_for_send,
+       rxrpc_timer__nr_trace
+};
+
+extern const char rxrpc_timer_traces[rxrpc_timer__nr_trace][8];
+
+enum rxrpc_propose_ack_trace {
+       rxrpc_propose_ack_input_data,
+       rxrpc_propose_ack_ping_for_params,
+       rxrpc_propose_ack_respond_to_ack,
+       rxrpc_propose_ack_respond_to_ping,
+       rxrpc_propose_ack_retry_tx,
+       rxrpc_propose_ack_terminal_ack,
+       rxrpc_propose_ack__nr_trace
+};
+
+enum rxrpc_propose_ack_outcome {
+       rxrpc_propose_ack_use,
+       rxrpc_propose_ack_update,
+       rxrpc_propose_ack_subsume,
+       rxrpc_propose_ack__nr_outcomes
+};
+
+extern const char rxrpc_propose_ack_traces[rxrpc_propose_ack__nr_trace][8];
+extern const char *const rxrpc_propose_ack_outcomes[rxrpc_propose_ack__nr_outcomes];
+
 extern const char *const rxrpc_pkts[];
-extern const char *rxrpc_acks(u8 reason);
+extern const char const rxrpc_ack_names[RXRPC_ACK__INVALID + 1][4];
 
 #include <trace/events/rxrpc.h>
 
@@ -704,7 +738,9 @@ int rxrpc_reject_call(struct rxrpc_sock *);
 /*
  * call_event.c
  */
-void rxrpc_propose_ACK(struct rxrpc_call *, u8, u16, u32, bool, bool);
+void rxrpc_set_timer(struct rxrpc_call *, enum rxrpc_timer_trace);
+void rxrpc_propose_ACK(struct rxrpc_call *, u8, u16, u32, bool, bool,
+                      enum rxrpc_propose_ack_trace);
 void rxrpc_process_call(struct work_struct *);
 
 /*
@@ -758,6 +794,7 @@ static inline bool __rxrpc_set_call_completion(struct rxrpc_call *call,
                call->error = error;
                call->completion = compl,
                call->state = RXRPC_CALL_COMPLETE;
+               wake_up(&call->waitq);
                return true;
        }
        return false;
index 6e2ea8f..a78a92f 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)
 {
        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);
 
        if (call->state < RXRPC_CALL_COMPLETE) {
-               t = call->ack_at;
-               if (time_before(call->resend_at, t))
+               t = call->expire_at;
+               if (time_before_eq(t, now))
+                       goto out;
+
+               if (time_after(call->resend_at, now) &&
+                   time_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);
+
+               if (time_after(call->ack_at, now) &&
+                   time_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);
                }
        }
+
+out:
        read_unlock_bh(&call->state_lock);
 }
 
@@ -54,14 +58,13 @@ static void rxrpc_set_timer(struct rxrpc_call *call)
  */
 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)
 {
+       enum rxrpc_propose_ack_outcome outcome = rxrpc_propose_ack_use;
        unsigned long now, ack_at, expiry = rxrpc_soft_ack_delay;
        s8 prior = rxrpc_ack_priority[ack_reason];
 
-       _enter("{%d},%s,%%%x,%u",
-              call->debug_id, rxrpc_acks(ack_reason), serial, immediate);
-
        /* Update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial
         * numbers, but we don't alter the timeout.
         */
@@ -70,15 +73,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) {
@@ -117,20 +123,25 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                _debug("deferred ACK %ld < %ld", expiry, call->ack_at - now);
                if (time_before(ack_at, call->ack_at)) {
                        call->ack_at = ack_at;
-                       rxrpc_set_timer(call);
+                       rxrpc_set_timer(call, rxrpc_timer_set_for_ack);
                }
        }
+
+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);
 }
 
@@ -185,9 +196,11 @@ static void rxrpc_resend(struct rxrpc_call *call)
 
                /* Okay, we need to retransmit a packet. */
                call->rxtx_annotations[ix] = RXRPC_TX_ANNO_RETRANS | annotation;
+               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);
+       resend_at = ktime_sub(ktime_add_ms(oldest, rxrpc_resend_timeout), now);
        call->resend_at = jiffies + nsecs_to_jiffies(ktime_to_ns(resend_at));
 
        /* Now go through the Tx window and perform the retransmissions.  We
@@ -290,7 +303,7 @@ recheck_state:
                goto recheck_state;
        }
 
-       rxrpc_set_timer(call);
+       rxrpc_set_timer(call, rxrpc_timer_set_for_resend);
 
        /* other events may have been raised since we started checking */
        if (call->events && call->state < RXRPC_CALL_COMPLETE) {
index f50a609..a53f4c2 100644 (file)
@@ -76,8 +76,10 @@ static void rxrpc_call_timer_expired(unsigned long _call)
 
        _enter("%d", call->debug_id);
 
-       if (call->state < RXRPC_CALL_COMPLETE)
+       if (call->state < RXRPC_CALL_COMPLETE) {
+               trace_rxrpc_timer(call, rxrpc_timer_expired, jiffies);
                rxrpc_queue_call(call);
+       }
 }
 
 /*
@@ -199,8 +201,8 @@ static void rxrpc_start_call_timer(struct rxrpc_call *call)
        call->expire_at = expire_at;
        call->ack_at = expire_at;
        call->resend_at = expire_at;
-       call->timer.expires = expire_at;
-       add_timer(&call->timer);
+       call->timer.expires = expire_at + 1;
+       rxrpc_set_timer(call, rxrpc_timer_begin);
 }
 
 /*
index 75a15a4..a1cf1ec 100644 (file)
@@ -98,9 +98,6 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
                pkt.info.rwind          = htonl(rxrpc_rx_window_size);
                pkt.info.jumbo_max      = htonl(rxrpc_rx_jumbo_max);
                len += sizeof(pkt.ack) + sizeof(pkt.info);
-
-               trace_rxrpc_tx_ack(NULL, chan->last_seq, 0,
-                                  RXRPC_ACK_DUPLICATE, 0);
                break;
        }
 
@@ -122,6 +119,8 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
                _proto("Tx ABORT %%%u { %d } [re]", serial, conn->local_abort);
                break;
        case RXRPC_PACKET_TYPE_ACK:
+               trace_rxrpc_tx_ack(NULL, serial, chan->last_seq, 0,
+                                  RXRPC_ACK_DUPLICATE, 0);
                _proto("Tx ACK %%%u [re]", serial);
                break;
        }
index cbb5d53..349698d 100644 (file)
@@ -49,7 +49,8 @@ static void rxrpc_send_ping(struct rxrpc_call *call, struct sk_buff *skb,
        if (call->peer->rtt_usage < 3 ||
            ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), now))
                rxrpc_propose_ACK(call, RXRPC_ACK_PING, skew, sp->hdr.serial,
-                                 true, true);
+                                 true, true,
+                                 rxrpc_propose_ack_ping_for_params);
 }
 
 /*
@@ -59,6 +60,7 @@ static void rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to)
 {
        struct sk_buff *skb, *list = NULL;
        int ix;
+       u8 annotation;
 
        spin_lock(&call->lock);
 
@@ -66,16 +68,22 @@ static void rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to)
                call->tx_hard_ack++;
                ix = call->tx_hard_ack & RXRPC_RXTX_BUFF_MASK;
                skb = call->rxtx_buffer[ix];
+               annotation = call->rxtx_annotations[ix];
                rxrpc_see_skb(skb, rxrpc_skb_tx_rotated);
                call->rxtx_buffer[ix] = NULL;
                call->rxtx_annotations[ix] = 0;
                skb->next = list;
                list = skb;
+
+               if (annotation & RXRPC_TX_ANNO_LAST)
+                       set_bit(RXRPC_CALL_TX_LAST, &call->flags);
        }
 
        spin_unlock(&call->lock);
 
-       trace_rxrpc_transmit(call, rxrpc_transmit_rotate);
+       trace_rxrpc_transmit(call, (test_bit(RXRPC_CALL_TX_LAST, &call->flags) ?
+                                   rxrpc_transmit_rotate_last :
+                                   rxrpc_transmit_rotate));
        wake_up(&call->waitq);
 
        while (list) {
@@ -92,42 +100,65 @@ static void rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to)
  * This occurs when we get an ACKALL packet, the first DATA packet of a reply,
  * or a final ACK packet.
  */
-static bool rxrpc_end_tx_phase(struct rxrpc_call *call, const char *abort_why)
+static bool rxrpc_end_tx_phase(struct rxrpc_call *call, bool reply_begun,
+                              const char *abort_why)
 {
-       _enter("");
 
-       switch (call->state) {
-       case RXRPC_CALL_CLIENT_RECV_REPLY:
-               return true;
-       case RXRPC_CALL_CLIENT_AWAIT_REPLY:
-       case RXRPC_CALL_SERVER_AWAIT_ACK:
-               break;
-       default:
-               rxrpc_proto_abort(abort_why, call, call->tx_top);
-               return false;
-       }
-
-       rxrpc_rotate_tx_window(call, call->tx_top);
+       ASSERT(test_bit(RXRPC_CALL_TX_LAST, &call->flags));
 
        write_lock(&call->state_lock);
 
        switch (call->state) {
-       default:
-               break;
+       case RXRPC_CALL_CLIENT_SEND_REQUEST:
        case RXRPC_CALL_CLIENT_AWAIT_REPLY:
-               call->tx_phase = false;
-               call->state = RXRPC_CALL_CLIENT_RECV_REPLY;
+               if (reply_begun)
+                       call->state = RXRPC_CALL_CLIENT_RECV_REPLY;
+               else
+                       call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY;
                break;
+
        case RXRPC_CALL_SERVER_AWAIT_ACK:
                __rxrpc_call_completed(call);
                rxrpc_notify_socket(call);
                break;
+
+       default:
+               goto bad_state;
        }
 
        write_unlock(&call->state_lock);
-       trace_rxrpc_transmit(call, rxrpc_transmit_end);
+       if (call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY) {
+               trace_rxrpc_transmit(call, rxrpc_transmit_await_reply);
+       } else {
+               trace_rxrpc_transmit(call, rxrpc_transmit_end);
+       }
        _leave(" = ok");
        return true;
+
+bad_state:
+       write_unlock(&call->state_lock);
+       kdebug("end_tx %s", rxrpc_call_states[call->state]);
+       rxrpc_proto_abort(abort_why, call, call->tx_top);
+       return false;
+}
+
+/*
+ * Begin the reply reception phase of a call.
+ */
+static bool rxrpc_receiving_reply(struct rxrpc_call *call)
+{
+       rxrpc_seq_t top = READ_ONCE(call->tx_top);
+
+       if (!test_bit(RXRPC_CALL_TX_LAST, &call->flags))
+               rxrpc_rotate_tx_window(call, top);
+       if (!test_bit(RXRPC_CALL_TX_LAST, &call->flags)) {
+               rxrpc_proto_abort("TXL", call, top);
+               return false;
+       }
+       if (!rxrpc_end_tx_phase(call, true, "ETD"))
+               return false;
+       call->tx_phase = false;
+       return true;
 }
 
 /*
@@ -226,8 +257,9 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb,
        /* Received data implicitly ACKs all of the request packets we sent
         * when we're acting as a client.
         */
-       if (call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY &&
-           !rxrpc_end_tx_phase(call, "ETD"))
+       if ((call->state == RXRPC_CALL_CLIENT_SEND_REQUEST ||
+            call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY) &&
+           !rxrpc_receiving_reply(call))
                return;
 
        call->ackr_prev_seq = seq;
@@ -351,7 +383,8 @@ skip:
 ack:
        if (ack)
                rxrpc_propose_ACK(call, ack, skew, ack_serial,
-                                 immediate_ack, true);
+                                 immediate_ack, true,
+                                 rxrpc_propose_ack_input_data);
 
        if (sp->hdr.seq == READ_ONCE(call->rx_hard_ack) + 1)
                rxrpc_notify_socket(call);
@@ -479,6 +512,8 @@ static void rxrpc_input_soft_acks(struct rxrpc_call *call, u8 *acks,
                case RXRPC_ACK_TYPE_NACK:
                        if (anno_type == RXRPC_TX_ANNO_NAK)
                                continue;
+                       if (anno_type == RXRPC_TX_ANNO_RETRANS)
+                               continue;
                        call->rxtx_annotations[ix] =
                                RXRPC_TX_ANNO_NAK | annotation;
                        resend = true;
@@ -506,6 +541,7 @@ static void rxrpc_input_soft_acks(struct rxrpc_call *call, u8 *acks,
 static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
                            u16 skew)
 {
+       u8 ack_reason;
        struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
        union {
                struct rxrpc_ackpacket ack;
@@ -528,8 +564,10 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
        first_soft_ack = ntohl(buf.ack.firstPacket);
        hard_ack = first_soft_ack - 1;
        nr_acks = buf.ack.nAcks;
+       ack_reason = (buf.ack.reason < RXRPC_ACK__INVALID ?
+                     buf.ack.reason : RXRPC_ACK__INVALID);
 
-       trace_rxrpc_rx_ack(call, first_soft_ack, buf.ack.reason, nr_acks);
+       trace_rxrpc_rx_ack(call, first_soft_ack, ack_reason, nr_acks);
 
        _proto("Rx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }",
               sp->hdr.serial,
@@ -537,7 +575,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
               first_soft_ack,
               ntohl(buf.ack.previousPacket),
               acked_serial,
-              rxrpc_acks(buf.ack.reason),
+              rxrpc_ack_names[ack_reason],
               buf.ack.nAcks);
 
        if (buf.ack.reason == RXRPC_ACK_PING_RESPONSE)
@@ -550,10 +588,12 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
        if (buf.ack.reason == RXRPC_ACK_PING) {
                _proto("Rx ACK %%%u PING Request", sp->hdr.serial);
                rxrpc_propose_ACK(call, RXRPC_ACK_PING_RESPONSE,
-                                 skew, sp->hdr.serial, true, true);
+                                 skew, sp->hdr.serial, true, true,
+                                 rxrpc_propose_ack_respond_to_ping);
        } else if (sp->hdr.flags & RXRPC_REQUEST_ACK) {
                rxrpc_propose_ACK(call, RXRPC_ACK_REQUESTED,
-                                 skew, sp->hdr.serial, true, true);
+                                 skew, sp->hdr.serial, true, true,
+                                 rxrpc_propose_ack_respond_to_ack);
        }
 
        offset = sp->offset + nr_acks + 3;
@@ -578,34 +618,33 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
        }
 
        /* Discard any out-of-order or duplicate ACKs. */
-       if ((int)sp->hdr.serial - (int)call->acks_latest <= 0) {
+       if (before_eq(sp->hdr.serial, call->acks_latest)) {
                _debug("discard ACK %d <= %d",
                       sp->hdr.serial, call->acks_latest);
                return;
        }
        call->acks_latest = sp->hdr.serial;
 
-       if (test_bit(RXRPC_CALL_TX_LAST, &call->flags) &&
-           hard_ack == call->tx_top) {
-               rxrpc_end_tx_phase(call, "ETA");
-               return;
-       }
-
        if (before(hard_ack, call->tx_hard_ack) ||
            after(hard_ack, call->tx_top))
                return rxrpc_proto_abort("AKW", call, 0);
+       if (nr_acks > call->tx_top - hard_ack)
+               return rxrpc_proto_abort("AKN", call, 0);
 
        if (after(hard_ack, call->tx_hard_ack))
                rxrpc_rotate_tx_window(call, hard_ack);
 
-       if (after(first_soft_ack, call->tx_top))
+       if (nr_acks > 0) {
+               if (skb_copy_bits(skb, sp->offset, buf.acks, nr_acks) < 0)
+                       return rxrpc_proto_abort("XSA", call, 0);
+               rxrpc_input_soft_acks(call, buf.acks, first_soft_ack, nr_acks);
+       }
+
+       if (test_bit(RXRPC_CALL_TX_LAST, &call->flags)) {
+               rxrpc_end_tx_phase(call, false, "ETA");
                return;
+       }
 
-       if (nr_acks > call->tx_top - first_soft_ack + 1)
-               nr_acks = first_soft_ack - call->tx_top + 1;
-       if (skb_copy_bits(skb, sp->offset, buf.acks, nr_acks) < 0)
-               return rxrpc_proto_abort("XSA", call, 0);
-       rxrpc_input_soft_acks(call, buf.acks, first_soft_ack, nr_acks);
 }
 
 /*
@@ -617,7 +656,9 @@ static void rxrpc_input_ackall(struct rxrpc_call *call, struct sk_buff *skb)
 
        _proto("Rx ACKALL %%%u", sp->hdr.serial);
 
-       rxrpc_end_tx_phase(call, "ETL");
+       rxrpc_rotate_tx_window(call, call->tx_top);
+       if (test_bit(RXRPC_CALL_TX_LAST, &call->flags))
+               rxrpc_end_tx_phase(call, false, "ETL");
 }
 
 /*
@@ -803,20 +844,19 @@ void rxrpc_data_ready(struct sock *udp_sk)
        skb_orphan(skb);
        sp = rxrpc_skb(skb);
 
+       /* dig out the RxRPC connection details */
+       if (rxrpc_extract_header(sp, skb) < 0)
+               goto bad_message;
+
        if (IS_ENABLED(CONFIG_AF_RXRPC_INJECT_LOSS)) {
                static int lose;
                if ((lose++ & 7) == 7) {
+                       trace_rxrpc_rx_lose(sp);
                        rxrpc_lose_skb(skb, rxrpc_skb_rx_lost);
                        return;
                }
        }
 
-       _net("Rx UDP packet from %08x:%04hu",
-            ntohl(ip_hdr(skb)->saddr), ntohs(udp_hdr(skb)->source));
-
-       /* dig out the RxRPC connection details */
-       if (rxrpc_extract_header(sp, skb) < 0)
-               goto bad_message;
        trace_rxrpc_rx_packet(sp);
 
        _net("Rx RxRPC %s ep=%x call=%x:%x",
index 0d425e7..1ca1483 100644 (file)
@@ -91,17 +91,10 @@ const s8 rxrpc_ack_priority[] = {
        [RXRPC_ACK_PING]                = 9,
 };
 
-const char *rxrpc_acks(u8 reason)
-{
-       static const char *const str[] = {
-               "---", "REQ", "DUP", "OOS", "WIN", "MEM", "PNG", "PNR", "DLY",
-               "IDL", "-?-"
-       };
-
-       if (reason >= ARRAY_SIZE(str))
-               reason = ARRAY_SIZE(str) - 1;
-       return str[reason];
-}
+const char const rxrpc_ack_names[RXRPC_ACK__INVALID + 1][4] = {
+       "---", "REQ", "DUP", "OOS", "WIN", "MEM", "PNG", "PNR", "DLY",
+       "IDL", "-?-"
+};
 
 const char rxrpc_skb_traces[rxrpc_skb__nr_trace][7] = {
        [rxrpc_skb_rx_cleaned]          = "Rx CLN",
@@ -155,9 +148,10 @@ const char rxrpc_client_traces[rxrpc_client__nr_trace][7] = {
 const char rxrpc_transmit_traces[rxrpc_transmit__nr_trace][4] = {
        [rxrpc_transmit_wait]           = "WAI",
        [rxrpc_transmit_queue]          = "QUE",
-       [rxrpc_transmit_queue_reqack]   = "QRA",
        [rxrpc_transmit_queue_last]     = "QLS",
        [rxrpc_transmit_rotate]         = "ROT",
+       [rxrpc_transmit_rotate_last]    = "RLS",
+       [rxrpc_transmit_await_reply]    = "AWR",
        [rxrpc_transmit_end]            = "END",
 };
 
@@ -193,3 +187,26 @@ const char rxrpc_rtt_rx_traces[rxrpc_rtt_rx__nr_trace][5] = {
        [rxrpc_rtt_rx_ping_response]    = "PONG",
        [rxrpc_rtt_rx_requested_ack]    = "RACK",
 };
+
+const char rxrpc_timer_traces[rxrpc_timer__nr_trace][8] = {
+       [rxrpc_timer_begin]                     = "Begin ",
+       [rxrpc_timer_expired]                   = "*EXPR*",
+       [rxrpc_timer_set_for_ack]               = "SetAck",
+       [rxrpc_timer_set_for_send]              = "SetTx ",
+       [rxrpc_timer_set_for_resend]            = "SetRTx",
+};
+
+const char rxrpc_propose_ack_traces[rxrpc_propose_ack__nr_trace][8] = {
+       [rxrpc_propose_ack_input_data]          = "DataIn ",
+       [rxrpc_propose_ack_ping_for_params]     = "Params ",
+       [rxrpc_propose_ack_respond_to_ack]      = "Rsp2Ack",
+       [rxrpc_propose_ack_respond_to_ping]     = "Rsp2Png",
+       [rxrpc_propose_ack_retry_tx]            = "RetryTx",
+       [rxrpc_propose_ack_terminal_ack]        = "ClTerm ",
+};
+
+const char *const rxrpc_propose_ack_outcomes[rxrpc_propose_ack__nr_outcomes] = {
+       [rxrpc_propose_ack_use]                 = "",
+       [rxrpc_propose_ack_update]              = " Update",
+       [rxrpc_propose_ack_subsume]             = " Subsume",
+};
index 282cb1e..0c563e3 100644 (file)
@@ -80,9 +80,6 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_call *call,
        pkt->ackinfo.rwind      = htonl(call->rx_winsize);
        pkt->ackinfo.jumbo_max  = htonl(jmax);
 
-       trace_rxrpc_tx_ack(call, hard_ack + 1, serial, call->ackr_reason,
-                          top - hard_ack);
-
        *ackp++ = 0;
        *ackp++ = 0;
        *ackp++ = 0;
@@ -119,8 +116,6 @@ int rxrpc_send_call_packet(struct rxrpc_call *call, u8 type)
                return -ENOMEM;
        }
 
-       serial = atomic_inc_return(&conn->serial);
-
        msg.msg_name    = &call->peer->srx.transport;
        msg.msg_namelen = call->peer->srx.transport_len;
        msg.msg_control = NULL;
@@ -131,7 +126,6 @@ int rxrpc_send_call_packet(struct rxrpc_call *call, u8 type)
        pkt->whdr.cid           = htonl(call->cid);
        pkt->whdr.callNumber    = htonl(call->call_id);
        pkt->whdr.seq           = 0;
-       pkt->whdr.serial        = htonl(serial);
        pkt->whdr.type          = type;
        pkt->whdr.flags         = conn->out_clientflag;
        pkt->whdr.userStatus    = 0;
@@ -157,14 +151,6 @@ int rxrpc_send_call_packet(struct rxrpc_call *call, u8 type)
 
                spin_unlock_bh(&call->lock);
 
-               _proto("Tx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }",
-                      serial,
-                      ntohs(pkt->ack.maxSkew),
-                      ntohl(pkt->ack.firstPacket),
-                      ntohl(pkt->ack.previousPacket),
-                      ntohl(pkt->ack.serial),
-                      rxrpc_acks(pkt->ack.reason),
-                      pkt->ack.nAcks);
 
                iov[0].iov_len += sizeof(pkt->ack) + n;
                iov[1].iov_base = &pkt->ackinfo;
@@ -176,7 +162,6 @@ int rxrpc_send_call_packet(struct rxrpc_call *call, u8 type)
        case RXRPC_PACKET_TYPE_ABORT:
                abort_code = call->abort_code;
                pkt->abort_code = htonl(abort_code);
-               _proto("Tx ABORT %%%u { %d }", serial, abort_code);
                iov[0].iov_len += sizeof(pkt->abort_code);
                len += sizeof(pkt->abort_code);
                ioc = 1;
@@ -188,6 +173,17 @@ int rxrpc_send_call_packet(struct rxrpc_call *call, u8 type)
                goto out;
        }
 
+       serial = atomic_inc_return(&conn->serial);
+       pkt->whdr.serial = htonl(serial);
+       switch (type) {
+       case RXRPC_PACKET_TYPE_ACK:
+               trace_rxrpc_tx_ack(call, serial,
+                                  ntohl(pkt->ack.firstPacket),
+                                  ntohl(pkt->ack.serial),
+                                  pkt->ack.reason, pkt->ack.nAcks);
+               break;
+       }
+
        if (ping) {
                call->ackr_ping = serial;
                smp_wmb();
@@ -214,7 +210,8 @@ int rxrpc_send_call_packet(struct rxrpc_call *call, u8 type)
                        rxrpc_propose_ACK(call, pkt->ack.reason,
                                          ntohs(pkt->ack.maxSkew),
                                          ntohl(pkt->ack.serial),
-                                         true, true);
+                                         true, true,
+                                         rxrpc_propose_ack_retry_tx);
                        break;
                case RXRPC_PACKET_TYPE_ABORT:
                        break;
@@ -279,6 +276,8 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb)
        if (IS_ENABLED(CONFIG_AF_RXRPC_INJECT_LOSS)) {
                static int lose;
                if ((lose++ & 7) == 7) {
+                       trace_rxrpc_tx_data(call, sp->hdr.seq, serial,
+                                           whdr.flags, true);
                        rxrpc_lose_skb(skb, rxrpc_skb_tx_lost);
                        _leave(" = 0 [lose]");
                        return 0;
@@ -306,6 +305,7 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb)
                goto send_fragmentable;
 
 done:
+       trace_rxrpc_tx_data(call, sp->hdr.seq, serial, whdr.flags, false);
        if (ret >= 0) {
                ktime_t now = ktime_get_real();
                skb->tstamp = now;
index 6ba4af5..8c7f3de 100644 (file)
@@ -141,10 +141,9 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call)
        ASSERTCMP(call->rx_hard_ack, ==, call->rx_top);
 
        if (call->state == RXRPC_CALL_CLIENT_RECV_REPLY) {
-               rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, 0, 0, true, false);
+               rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, 0, 0, true, false,
+                                 rxrpc_propose_ack_terminal_ack);
                rxrpc_send_call_packet(call, RXRPC_PACKET_TYPE_ACK);
-       } else {
-               rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, 0, 0, false, false);
        }
 
        write_lock_bh(&call->state_lock);
index ca7c3be..9993937 100644 (file)
@@ -94,25 +94,30 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb,
        struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
        rxrpc_seq_t seq = sp->hdr.seq;
        int ret, ix;
+       u8 annotation = RXRPC_TX_ANNO_UNACK;
 
        _net("queue skb %p [%d]", skb, seq);
 
        ASSERTCMP(seq, ==, call->tx_top + 1);
 
+       if (last)
+               annotation |= RXRPC_TX_ANNO_LAST;
+
+       /* We have to set the timestamp before queueing as the retransmit
+        * algorithm can see the packet as soon as we queue it.
+        */
+       skb->tstamp = ktime_get_real();
+
        ix = seq & RXRPC_RXTX_BUFF_MASK;
        rxrpc_get_skb(skb, rxrpc_skb_tx_got);
-       call->rxtx_annotations[ix] = RXRPC_TX_ANNO_UNACK;
+       call->rxtx_annotations[ix] = annotation;
        smp_wmb();
        call->rxtx_buffer[ix] = skb;
        call->tx_top = seq;
-       if (last) {
-               set_bit(RXRPC_CALL_TX_LAST, &call->flags);
+       if (last)
                trace_rxrpc_transmit(call, rxrpc_transmit_queue_last);
-       } else if (sp->hdr.flags & RXRPC_REQUEST_ACK) {
-               trace_rxrpc_transmit(call, rxrpc_transmit_queue_reqack);
-       } else {
+       else
                trace_rxrpc_transmit(call, rxrpc_transmit_queue);
-       }
 
        if (last || call->state == RXRPC_CALL_SERVER_ACK_REQUEST) {
                _debug("________awaiting reply/ACK__________");
@@ -141,6 +146,15 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb,
        if (ret < 0) {
                _debug("need instant resend %d", ret);
                rxrpc_instant_resend(call, ix);
+       } else {
+               unsigned long resend_at;
+
+               resend_at = jiffies + msecs_to_jiffies(rxrpc_resend_timeout);
+
+               if (time_before(resend_at, call->resend_at)) {
+                       call->resend_at = resend_at;
+                       rxrpc_set_timer(call, rxrpc_timer_set_for_send);
+               }
        }
 
        rxrpc_free_skb(skb, rxrpc_skb_tx_freed);