Skip to content

Commit 4700c4d

Browse files
committed
rxrpc: Fix loss of RTT samples due to interposed ACK
The Rx protocol has a mechanism to help generate RTT samples that works by a client transmitting a REQUESTED-type ACK when it receives a DATA packet that has the REQUEST_ACK flag set. The peer, however, may interpose other ACKs before transmitting the REQUESTED-ACK, as can be seen in the following trace excerpt: rxrpc_tx_data: c=00000044 DATA d0b5ece8:00000001 00000001 q=00000001 fl=07 rxrpc_rx_ack: c=00000044 00000001 PNG r=00000000 f=00000002 p=00000000 n=0 rxrpc_rx_ack: c=00000044 00000002 REQ r=00000001 f=00000002 p=00000001 n=0 ... DATA packet 1 (q=xx) has REQUEST_ACK set (bit 1 of fl=xx). The incoming ping (labelled PNG) hard-acks the request DATA packet (f=xx exceeds the sequence number of the DATA packet), causing it to be discarded from the Tx ring. The ACK that was requested (labelled REQ, r=xx references the serial of the DATA packet) comes after the ping, but the sk_buff holding the timestamp has gone and the RTT sample is lost. This is particularly noticeable on RPC calls used to probe the service offered by the peer. A lot of peers end up with an unknown RTT because we only ever sent a single RPC. This confuses the server rotation algorithm. Fix this by caching the information about the outgoing packet in RTT calculations in the rxrpc_call struct rather than looking in the Tx ring. A four-deep buffer is maintained and both REQUEST_ACK-flagged DATA and PING-ACK transmissions are recorded in there. When the appropriate response ACK is received, the buffer is checked for a match and, if found, an RTT sample is recorded. If a received ACK refers to a packet with a later serial number than an entry in the cache, that entry is presumed lost and the entry is made available to record a new transmission. ACKs types other than REQUESTED-type and PING-type cause any matching sample to be cancelled as they don't necessarily represent a useful measurement. If there's no space in the buffer on ping/data transmission, the sample base is discarded. Fixes: 50235c4 ("rxrpc: Obtain RTT data by requesting ACKs on DATA packets") Signed-off-by: David Howells <[email protected]>
1 parent 68528d9 commit 4700c4d

File tree

6 files changed

+154
-76
lines changed

6 files changed

+154
-76
lines changed

include/trace/events/rxrpc.h

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,16 @@ enum rxrpc_recvmsg_trace {
138138
};
139139

140140
enum rxrpc_rtt_tx_trace {
141+
rxrpc_rtt_tx_cancel,
141142
rxrpc_rtt_tx_data,
143+
rxrpc_rtt_tx_no_slot,
142144
rxrpc_rtt_tx_ping,
143145
};
144146

145147
enum rxrpc_rtt_rx_trace {
148+
rxrpc_rtt_rx_cancel,
149+
rxrpc_rtt_rx_lost,
150+
rxrpc_rtt_rx_obsolete,
146151
rxrpc_rtt_rx_ping_response,
147152
rxrpc_rtt_rx_requested_ack,
148153
};
@@ -339,10 +344,15 @@ enum rxrpc_tx_point {
339344
E_(rxrpc_recvmsg_wait, "WAIT")
340345

341346
#define rxrpc_rtt_tx_traces \
347+
EM(rxrpc_rtt_tx_cancel, "CNCE") \
342348
EM(rxrpc_rtt_tx_data, "DATA") \
349+
EM(rxrpc_rtt_tx_no_slot, "FULL") \
343350
E_(rxrpc_rtt_tx_ping, "PING")
344351

345352
#define rxrpc_rtt_rx_traces \
353+
EM(rxrpc_rtt_rx_cancel, "CNCL") \
354+
EM(rxrpc_rtt_rx_obsolete, "OBSL") \
355+
EM(rxrpc_rtt_rx_lost, "LOST") \
346356
EM(rxrpc_rtt_rx_ping_response, "PONG") \
347357
E_(rxrpc_rtt_rx_requested_ack, "RACK")
348358

@@ -1087,38 +1097,43 @@ TRACE_EVENT(rxrpc_recvmsg,
10871097

10881098
TRACE_EVENT(rxrpc_rtt_tx,
10891099
TP_PROTO(struct rxrpc_call *call, enum rxrpc_rtt_tx_trace why,
1090-
rxrpc_serial_t send_serial),
1100+
int slot, rxrpc_serial_t send_serial),
10911101

1092-
TP_ARGS(call, why, send_serial),
1102+
TP_ARGS(call, why, slot, send_serial),
10931103

10941104
TP_STRUCT__entry(
10951105
__field(unsigned int, call )
10961106
__field(enum rxrpc_rtt_tx_trace, why )
1107+
__field(int, slot )
10971108
__field(rxrpc_serial_t, send_serial )
10981109
),
10991110

11001111
TP_fast_assign(
11011112
__entry->call = call->debug_id;
11021113
__entry->why = why;
1114+
__entry->slot = slot;
11031115
__entry->send_serial = send_serial;
11041116
),
11051117

1106-
TP_printk("c=%08x %s sr=%08x",
1118+
TP_printk("c=%08x [%d] %s sr=%08x",
11071119
__entry->call,
1120+
__entry->slot,
11081121
__print_symbolic(__entry->why, rxrpc_rtt_tx_traces),
11091122
__entry->send_serial)
11101123
);
11111124

11121125
TRACE_EVENT(rxrpc_rtt_rx,
11131126
TP_PROTO(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why,
1127+
int slot,
11141128
rxrpc_serial_t send_serial, rxrpc_serial_t resp_serial,
11151129
u32 rtt, u32 rto),
11161130

1117-
TP_ARGS(call, why, send_serial, resp_serial, rtt, rto),
1131+
TP_ARGS(call, why, slot, send_serial, resp_serial, rtt, rto),
11181132

11191133
TP_STRUCT__entry(
11201134
__field(unsigned int, call )
11211135
__field(enum rxrpc_rtt_rx_trace, why )
1136+
__field(int, slot )
11221137
__field(rxrpc_serial_t, send_serial )
11231138
__field(rxrpc_serial_t, resp_serial )
11241139
__field(u32, rtt )
@@ -1128,14 +1143,16 @@ TRACE_EVENT(rxrpc_rtt_rx,
11281143
TP_fast_assign(
11291144
__entry->call = call->debug_id;
11301145
__entry->why = why;
1146+
__entry->slot = slot;
11311147
__entry->send_serial = send_serial;
11321148
__entry->resp_serial = resp_serial;
11331149
__entry->rtt = rtt;
11341150
__entry->rto = rto;
11351151
),
11361152

1137-
TP_printk("c=%08x %s sr=%08x rr=%08x rtt=%u rto=%u",
1153+
TP_printk("c=%08x [%d] %s sr=%08x rr=%08x rtt=%u rto=%u",
11381154
__entry->call,
1155+
__entry->slot,
11391156
__print_symbolic(__entry->why, rxrpc_rtt_rx_traces),
11401157
__entry->send_serial,
11411158
__entry->resp_serial,

net/rxrpc/ar-internal.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,6 @@ enum rxrpc_call_flag {
488488
RXRPC_CALL_RX_LAST, /* Received the last packet (at rxtx_top) */
489489
RXRPC_CALL_TX_LAST, /* Last packet in Tx buffer (at rxtx_top) */
490490
RXRPC_CALL_SEND_PING, /* A ping will need to be sent */
491-
RXRPC_CALL_PINGING, /* Ping in process */
492491
RXRPC_CALL_RETRANS_TIMEOUT, /* Retransmission due to timeout occurred */
493492
RXRPC_CALL_BEGAN_RX_TIMER, /* We began the expect_rx_by timer */
494493
RXRPC_CALL_RX_HEARD, /* The peer responded at least once to this call */
@@ -673,9 +672,13 @@ struct rxrpc_call {
673672
rxrpc_seq_t ackr_consumed; /* Highest packet shown consumed */
674673
rxrpc_seq_t ackr_seen; /* Highest packet shown seen */
675674

676-
/* ping management */
677-
rxrpc_serial_t ping_serial; /* Last ping sent */
678-
ktime_t ping_time; /* Time last ping sent */
675+
/* RTT management */
676+
rxrpc_serial_t rtt_serial[4]; /* Serial number of DATA or PING sent */
677+
ktime_t rtt_sent_at[4]; /* Time packet sent */
678+
unsigned long rtt_avail; /* Mask of available slots in bits 0-3,
679+
* Mask of pending samples in 8-11 */
680+
#define RXRPC_CALL_RTT_AVAIL_MASK 0xf
681+
#define RXRPC_CALL_RTT_PEND_SHIFT 8
679682

680683
/* transmission-phase ACK management */
681684
ktime_t acks_latest_ts; /* Timestamp of latest ACK received */
@@ -1037,7 +1040,7 @@ static inline bool __rxrpc_abort_eproto(struct rxrpc_call *call,
10371040
/*
10381041
* rtt.c
10391042
*/
1040-
void rxrpc_peer_add_rtt(struct rxrpc_call *, enum rxrpc_rtt_rx_trace,
1043+
void rxrpc_peer_add_rtt(struct rxrpc_call *, enum rxrpc_rtt_rx_trace, int,
10411044
rxrpc_serial_t, rxrpc_serial_t, ktime_t, ktime_t);
10421045
unsigned long rxrpc_get_rto_backoff(struct rxrpc_peer *, bool);
10431046
void rxrpc_peer_init_rtt(struct rxrpc_peer *);

net/rxrpc/call_object.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp,
153153
call->cong_ssthresh = RXRPC_RXTX_BUFF_SIZE - 1;
154154

155155
call->rxnet = rxnet;
156+
call->rtt_avail = RXRPC_CALL_RTT_AVAIL_MASK;
156157
atomic_inc(&rxnet->nr_calls);
157158
return call;
158159

net/rxrpc/input.c

Lines changed: 60 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -608,36 +608,57 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb)
608608
}
609609

610610
/*
611-
* Process a requested ACK.
611+
* See if there's a cached RTT probe to complete.
612612
*/
613-
static void rxrpc_input_requested_ack(struct rxrpc_call *call,
614-
ktime_t resp_time,
615-
rxrpc_serial_t orig_serial,
616-
rxrpc_serial_t ack_serial)
613+
static void rxrpc_complete_rtt_probe(struct rxrpc_call *call,
614+
ktime_t resp_time,
615+
rxrpc_serial_t acked_serial,
616+
rxrpc_serial_t ack_serial,
617+
enum rxrpc_rtt_rx_trace type)
617618
{
618-
struct rxrpc_skb_priv *sp;
619-
struct sk_buff *skb;
619+
rxrpc_serial_t orig_serial;
620+
unsigned long avail;
620621
ktime_t sent_at;
621-
int ix;
622+
bool matched = false;
623+
int i;
622624

623-
for (ix = 0; ix < RXRPC_RXTX_BUFF_SIZE; ix++) {
624-
skb = call->rxtx_buffer[ix];
625-
if (!skb)
626-
continue;
625+
avail = READ_ONCE(call->rtt_avail);
626+
smp_rmb(); /* Read avail bits before accessing data. */
627627

628-
sent_at = skb->tstamp;
629-
smp_rmb(); /* Read timestamp before serial. */
630-
sp = rxrpc_skb(skb);
631-
if (sp->hdr.serial != orig_serial)
628+
for (i = 0; i < ARRAY_SIZE(call->rtt_serial); i++) {
629+
if (!test_bit(i + RXRPC_CALL_RTT_PEND_SHIFT, &avail))
632630
continue;
633-
goto found;
634-
}
635631

636-
return;
632+
sent_at = call->rtt_sent_at[i];
633+
orig_serial = call->rtt_serial[i];
634+
635+
if (orig_serial == acked_serial) {
636+
clear_bit(i + RXRPC_CALL_RTT_PEND_SHIFT, &call->rtt_avail);
637+
smp_mb(); /* Read data before setting avail bit */
638+
set_bit(i, &call->rtt_avail);
639+
if (type != rxrpc_rtt_rx_cancel)
640+
rxrpc_peer_add_rtt(call, type, i, acked_serial, ack_serial,
641+
sent_at, resp_time);
642+
else
643+
trace_rxrpc_rtt_rx(call, rxrpc_rtt_rx_cancel, i,
644+
orig_serial, acked_serial, 0, 0);
645+
matched = true;
646+
}
647+
648+
/* If a later serial is being acked, then mark this slot as
649+
* being available.
650+
*/
651+
if (after(acked_serial, orig_serial)) {
652+
trace_rxrpc_rtt_rx(call, rxrpc_rtt_rx_obsolete, i,
653+
orig_serial, acked_serial, 0, 0);
654+
clear_bit(i + RXRPC_CALL_RTT_PEND_SHIFT, &call->rtt_avail);
655+
smp_wmb();
656+
set_bit(i, &call->rtt_avail);
657+
}
658+
}
637659

638-
found:
639-
rxrpc_peer_add_rtt(call, rxrpc_rtt_rx_requested_ack,
640-
orig_serial, ack_serial, sent_at, resp_time);
660+
if (!matched)
661+
trace_rxrpc_rtt_rx(call, rxrpc_rtt_rx_lost, 9, 0, acked_serial, 0, 0);
641662
}
642663

643664
/*
@@ -682,27 +703,11 @@ static void rxrpc_input_check_for_lost_ack(struct rxrpc_call *call)
682703
*/
683704
static void rxrpc_input_ping_response(struct rxrpc_call *call,
684705
ktime_t resp_time,
685-
rxrpc_serial_t orig_serial,
706+
rxrpc_serial_t acked_serial,
686707
rxrpc_serial_t ack_serial)
687708
{
688-
rxrpc_serial_t ping_serial;
689-
ktime_t ping_time;
690-
691-
ping_time = call->ping_time;
692-
smp_rmb();
693-
ping_serial = READ_ONCE(call->ping_serial);
694-
695-
if (orig_serial == call->acks_lost_ping)
709+
if (acked_serial == call->acks_lost_ping)
696710
rxrpc_input_check_for_lost_ack(call);
697-
698-
if (before(orig_serial, ping_serial) ||
699-
!test_and_clear_bit(RXRPC_CALL_PINGING, &call->flags))
700-
return;
701-
if (after(orig_serial, ping_serial))
702-
return;
703-
704-
rxrpc_peer_add_rtt(call, rxrpc_rtt_rx_ping_response,
705-
orig_serial, ack_serial, ping_time, resp_time);
706711
}
707712

708713
/*
@@ -869,12 +874,23 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb)
869874
first_soft_ack, prev_pkt,
870875
summary.ack_reason, nr_acks);
871876

872-
if (buf.ack.reason == RXRPC_ACK_PING_RESPONSE)
877+
switch (buf.ack.reason) {
878+
case RXRPC_ACK_PING_RESPONSE:
873879
rxrpc_input_ping_response(call, skb->tstamp, acked_serial,
874880
ack_serial);
875-
if (buf.ack.reason == RXRPC_ACK_REQUESTED)
876-
rxrpc_input_requested_ack(call, skb->tstamp, acked_serial,
877-
ack_serial);
881+
rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial,
882+
rxrpc_rtt_rx_ping_response);
883+
break;
884+
case RXRPC_ACK_REQUESTED:
885+
rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial,
886+
rxrpc_rtt_rx_requested_ack);
887+
break;
888+
default:
889+
if (acked_serial != 0)
890+
rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial,
891+
rxrpc_rtt_rx_cancel);
892+
break;
893+
}
878894

879895
if (buf.ack.reason == RXRPC_ACK_PING) {
880896
_proto("Rx ACK %%%u PING Request", ack_serial);

0 commit comments

Comments
 (0)