Commit 1a2391c3 authored by Jeffrey Altman's avatar Jeffrey Altman Committed by David S. Miller

rxrpc: Fix detection of out of order acks

The rxrpc packet serial number cannot be safely used to compute out of
order ack packets for several reasons:

 1. The allocation of serial numbers cannot be assumed to imply the order
    by which acks are populated and transmitted.  In some rxrpc
    implementations, delayed acks and ping acks are transmitted
    asynchronously to the receipt of data packets and so may be transmitted
    out of order.  As a result, they can race with idle acks.

 2. Serial numbers are allocated by the rxrpc connection and not the call
    and as such may wrap independently if multiple channels are in use.

In any case, what matters is whether the ack packet provides new
information relating to the bounds of the window (the firstPacket and
previousPacket in the ACK data).

Fix this by discarding packets that appear to wind back the window bounds
rather than on serial number procession.

Fixes: 298bc15b ("rxrpc: Only take the rwind and mtu values from latest ACK")
Signed-off-by: default avatarJeffrey Altman <jaltman@auristor.com>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Tested-by: default avatarMarc Dionne <marc.dionne@auristor.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 39ce6755
...@@ -654,6 +654,7 @@ struct rxrpc_call { ...@@ -654,6 +654,7 @@ struct rxrpc_call {
u8 ackr_reason; /* reason to ACK */ u8 ackr_reason; /* reason to ACK */
u16 ackr_skew; /* skew on packet being ACK'd */ u16 ackr_skew; /* skew on packet being ACK'd */
rxrpc_serial_t ackr_serial; /* serial of packet being ACK'd */ rxrpc_serial_t ackr_serial; /* serial of packet being ACK'd */
rxrpc_serial_t ackr_first_seq; /* first sequence number received */
rxrpc_seq_t ackr_prev_seq; /* previous sequence number received */ rxrpc_seq_t ackr_prev_seq; /* previous sequence number received */
rxrpc_seq_t ackr_consumed; /* Highest packet shown consumed */ rxrpc_seq_t ackr_consumed; /* Highest packet shown consumed */
rxrpc_seq_t ackr_seen; /* Highest packet shown seen */ rxrpc_seq_t ackr_seen; /* Highest packet shown seen */
......
...@@ -837,7 +837,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb, ...@@ -837,7 +837,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
u8 acks[RXRPC_MAXACKS]; u8 acks[RXRPC_MAXACKS];
} buf; } buf;
rxrpc_serial_t acked_serial; rxrpc_serial_t acked_serial;
rxrpc_seq_t first_soft_ack, hard_ack; rxrpc_seq_t first_soft_ack, hard_ack, prev_pkt;
int nr_acks, offset, ioffset; int nr_acks, offset, ioffset;
_enter(""); _enter("");
...@@ -851,13 +851,14 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb, ...@@ -851,13 +851,14 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
acked_serial = ntohl(buf.ack.serial); acked_serial = ntohl(buf.ack.serial);
first_soft_ack = ntohl(buf.ack.firstPacket); first_soft_ack = ntohl(buf.ack.firstPacket);
prev_pkt = ntohl(buf.ack.previousPacket);
hard_ack = first_soft_ack - 1; hard_ack = first_soft_ack - 1;
nr_acks = buf.ack.nAcks; nr_acks = buf.ack.nAcks;
summary.ack_reason = (buf.ack.reason < RXRPC_ACK__INVALID ? summary.ack_reason = (buf.ack.reason < RXRPC_ACK__INVALID ?
buf.ack.reason : RXRPC_ACK__INVALID); buf.ack.reason : RXRPC_ACK__INVALID);
trace_rxrpc_rx_ack(call, sp->hdr.serial, acked_serial, trace_rxrpc_rx_ack(call, sp->hdr.serial, acked_serial,
first_soft_ack, ntohl(buf.ack.previousPacket), first_soft_ack, prev_pkt,
summary.ack_reason, nr_acks); summary.ack_reason, nr_acks);
if (buf.ack.reason == RXRPC_ACK_PING_RESPONSE) if (buf.ack.reason == RXRPC_ACK_PING_RESPONSE)
...@@ -878,8 +879,9 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb, ...@@ -878,8 +879,9 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
rxrpc_propose_ack_respond_to_ack); rxrpc_propose_ack_respond_to_ack);
} }
/* Discard any out-of-order or duplicate ACKs. */ /* Discard any out-of-order or duplicate ACKs (outside lock). */
if (before_eq(sp->hdr.serial, call->acks_latest)) if (before(first_soft_ack, call->ackr_first_seq) ||
before(prev_pkt, call->ackr_prev_seq))
return; return;
buf.info.rxMTU = 0; buf.info.rxMTU = 0;
...@@ -890,12 +892,16 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb, ...@@ -890,12 +892,16 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
spin_lock(&call->input_lock); spin_lock(&call->input_lock);
/* Discard any out-of-order or duplicate ACKs. */ /* Discard any out-of-order or duplicate ACKs (inside lock). */
if (before_eq(sp->hdr.serial, call->acks_latest)) if (before(first_soft_ack, call->ackr_first_seq) ||
before(prev_pkt, call->ackr_prev_seq))
goto out; goto out;
call->acks_latest_ts = skb->tstamp; call->acks_latest_ts = skb->tstamp;
call->acks_latest = sp->hdr.serial; call->acks_latest = sp->hdr.serial;
call->ackr_first_seq = first_soft_ack;
call->ackr_prev_seq = prev_pkt;
/* Parse rwind and mtu sizes if provided. */ /* Parse rwind and mtu sizes if provided. */
if (buf.info.rxMTU) if (buf.info.rxMTU)
rxrpc_input_ackinfo(call, skb, &buf.info); rxrpc_input_ackinfo(call, skb, &buf.info);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment