rxrpc: Fix loss of PING RESPONSE ACK production due to PING ACKs

Separate the output of PING ACKs from the output of other sorts of ACK so
that if we receive a PING ACK and schedule transmission of a PING RESPONSE
ACK, the response doesn't get cancelled by a PING ACK we happen to be
scheduling transmission of at the same time.

If a PING RESPONSE gets lost, the other side might just sit there waiting
for it and refuse to proceed otherwise.

Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
David Howells
2016-10-06 08:11:49 +01:00
parent 26cb02aa6d
commit a5af7e1fc6
8 changed files with 82 additions and 29 deletions

View File

@@ -54,6 +54,14 @@ void rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why,
t = call->ack_at;
}
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;
@@ -77,6 +85,27 @@ out:
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
*/
@@ -90,6 +119,14 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
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.
*/
@@ -125,7 +162,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;
@@ -253,7 +289,7 @@ static void rxrpc_resend(struct rxrpc_call *call, ktime_t now)
goto out;
rxrpc_propose_ACK(call, RXRPC_ACK_PING, 0, 0, true, false,
rxrpc_propose_ack_ping_for_lost_ack);
rxrpc_send_ack_packet(call);
rxrpc_send_ack_packet(call, true);
goto out;
}
@@ -345,13 +381,17 @@ recheck_state:
}
if (test_and_clear_bit(RXRPC_CALL_EV_ACK, &call->events)) {
call->ack_at = call->expire_at;
if (call->ackr_reason) {
rxrpc_send_ack_packet(call);
rxrpc_send_ack_packet(call, false);
goto recheck_state;
}
}
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;