rxrpc: Pass the last Tx packet marker in the annotation buffer

When the last packet of data to be transmitted on a call is queued, tx_top
is set and then the RXRPC_CALL_TX_LAST flag is set.  Unfortunately, this
leaves a race in the ACK processing side of things because the flag affects
the interpretation of tx_top and also allows us to start receiving reply
data before we've finished transmitting.

To fix this, make the following changes:

 (1) rxrpc_queue_packet() now sets a marker in the annotation buffer
     instead of setting the RXRPC_CALL_TX_LAST flag.

 (2) rxrpc_rotate_tx_window() detects the marker and sets the flag in the
     same context as the routines that use it.

 (3) rxrpc_end_tx_phase() is simplified to just shift the call state.
     The Tx window must have been rotated before calling to discard the
     last packet.

 (4) rxrpc_receiving_reply() is added to handle the arrival of the first
     DATA packet of a reply to a client call (which is an implicit ACK of
     the Tx phase).

 (5) The last part of rxrpc_input_ack() is reordered to perform Tx
     rotation, then soft-ACK application and then to end the phase if we've
     rotated the last packet.  In the event of a terminal ACK, the soft-ACK
     application will be skipped as nAcks should be 0.

 (6) rxrpc_input_ackall() now has to rotate as well as ending the phase.

In addition:

 (7) Alter the transmit tracepoint to log the rotation of the last packet.

 (8) Remove the no-longer relevant queue_reqack tracepoint note.  The
     ACK-REQUESTED packet header flag is now set as needed when we actually
     transmit the packet and may vary by retransmission.

Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
David Howells
2016-09-23 12:39:22 +01:00
parent 01a88f7f6b
commit 70790dbe3f
4 changed files with 82 additions and 46 deletions

View File

@@ -59,6 +59,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 +67,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 +99,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 +256,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;
@@ -587,27 +618,26 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
}
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))
return;
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);
}
/*
@@ -619,7 +649,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");
}
/*