Skip to content

Commit c038a58

Browse files
committed
rxrpc: Allow failed client calls to be retried
Allow a client call that failed on network error to be retried, provided that the Tx queue still holds DATA packet 1. This allows an operation to be submitted to another server or another address for the same server without having to repackage and re-encrypt the data so far processed. Two new functions are provided: (1) rxrpc_kernel_check_call() - This is used to find out the completion state of a call to guess whether it can be retried and whether it should be retried. (2) rxrpc_kernel_retry_call() - Disconnect the call from its current connection, reset the state and submit it as a new client call to a new address. The new address need not match the previous address. A call may be retried even if all the data hasn't been loaded into it yet; a partially constructed will be retained at the same point it was at when an error condition was detected. msg_data_left() can be used to find out how much data was packaged before the error occurred. Signed-off-by: David Howells <[email protected]>
1 parent e833251 commit c038a58

File tree

7 files changed

+261
-31
lines changed

7 files changed

+261
-31
lines changed

Documentation/networking/rxrpc.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,51 @@ The kernel interface functions are as follows:
975975
size should be set when the call is begun. tx_total_len may not be less
976976
than zero.
977977

978+
(*) Check to see the completion state of a call so that the caller can assess
979+
whether it needs to be retried.
980+
981+
enum rxrpc_call_completion {
982+
RXRPC_CALL_SUCCEEDED,
983+
RXRPC_CALL_REMOTELY_ABORTED,
984+
RXRPC_CALL_LOCALLY_ABORTED,
985+
RXRPC_CALL_LOCAL_ERROR,
986+
RXRPC_CALL_NETWORK_ERROR,
987+
};
988+
989+
int rxrpc_kernel_check_call(struct socket *sock, struct rxrpc_call *call,
990+
enum rxrpc_call_completion *_compl,
991+
u32 *_abort_code);
992+
993+
On return, -EINPROGRESS will be returned if the call is still ongoing; if
994+
it is finished, *_compl will be set to indicate the manner of completion,
995+
*_abort_code will be set to any abort code that occurred. 0 will be
996+
returned on a successful completion, -ECONNABORTED will be returned if the
997+
client failed due to a remote abort and anything else will return an
998+
appropriate error code.
999+
1000+
The caller should look at this information to decide if it's worth
1001+
retrying the call.
1002+
1003+
(*) Retry a client call.
1004+
1005+
int rxrpc_kernel_retry_call(struct socket *sock,
1006+
struct rxrpc_call *call,
1007+
struct sockaddr_rxrpc *srx,
1008+
struct key *key);
1009+
1010+
This attempts to partially reinitialise a call and submit it again whilst
1011+
reusing the original call's Tx queue to avoid the need to repackage and
1012+
re-encrypt the data to be sent. call indicates the call to retry, srx the
1013+
new address to send it to and key the encryption key to use for signing or
1014+
encrypting the packets.
1015+
1016+
For this to work, the first Tx data packet must still be in the transmit
1017+
queue, and currently this is only permitted for local and network errors
1018+
and the call must not have been aborted. Any partially constructed Tx
1019+
packet is left as is and can continue being filled afterwards.
1020+
1021+
It returns 0 if the call was requeued and an error otherwise.
1022+
9781023

9791024
=======================
9801025
CONFIGURABLE PARAMETERS

include/net/af_rxrpc.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ struct sock;
1919
struct socket;
2020
struct rxrpc_call;
2121

22+
/*
23+
* Call completion condition (state == RXRPC_CALL_COMPLETE).
24+
*/
25+
enum rxrpc_call_completion {
26+
RXRPC_CALL_SUCCEEDED, /* - Normal termination */
27+
RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */
28+
RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */
29+
RXRPC_CALL_LOCAL_ERROR, /* - call failed due to local error */
30+
RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */
31+
NR__RXRPC_CALL_COMPLETIONS
32+
};
33+
2234
typedef void (*rxrpc_notify_rx_t)(struct sock *, struct rxrpc_call *,
2335
unsigned long);
2436
typedef void (*rxrpc_notify_end_tx_t)(struct sock *, struct rxrpc_call *,
@@ -51,5 +63,9 @@ void rxrpc_kernel_get_peer(struct socket *, struct rxrpc_call *,
5163
int rxrpc_kernel_charge_accept(struct socket *, rxrpc_notify_rx_t,
5264
rxrpc_user_attach_call_t, unsigned long, gfp_t);
5365
void rxrpc_kernel_set_tx_length(struct socket *, struct rxrpc_call *, s64);
66+
int rxrpc_kernel_retry_call(struct socket *, struct rxrpc_call *,
67+
struct sockaddr_rxrpc *, struct key *);
68+
int rxrpc_kernel_check_call(struct socket *, struct rxrpc_call *,
69+
enum rxrpc_call_completion *, u32 *);
5470

5571
#endif /* _NET_RXRPC_H */

net/rxrpc/af_rxrpc.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,75 @@ void rxrpc_kernel_end_call(struct socket *sock, struct rxrpc_call *call)
336336
}
337337
EXPORT_SYMBOL(rxrpc_kernel_end_call);
338338

339+
/**
340+
* rxrpc_kernel_check_call - Check a call's state
341+
* @sock: The socket the call is on
342+
* @call: The call to check
343+
* @_compl: Where to store the completion state
344+
* @_abort_code: Where to store any abort code
345+
*
346+
* Allow a kernel service to query the state of a call and find out the manner
347+
* of its termination if it has completed. Returns -EINPROGRESS if the call is
348+
* still going, 0 if the call finished successfully, -ECONNABORTED if the call
349+
* was aborted and an appropriate error if the call failed in some other way.
350+
*/
351+
int rxrpc_kernel_check_call(struct socket *sock, struct rxrpc_call *call,
352+
enum rxrpc_call_completion *_compl, u32 *_abort_code)
353+
{
354+
if (call->state != RXRPC_CALL_COMPLETE)
355+
return -EINPROGRESS;
356+
smp_rmb();
357+
*_compl = call->completion;
358+
*_abort_code = call->abort_code;
359+
return call->error;
360+
}
361+
EXPORT_SYMBOL(rxrpc_kernel_check_call);
362+
363+
/**
364+
* rxrpc_kernel_retry_call - Allow a kernel service to retry a call
365+
* @sock: The socket the call is on
366+
* @call: The call to retry
367+
* @srx: The address of the peer to contact
368+
* @key: The security context to use (defaults to socket setting)
369+
*
370+
* Allow a kernel service to try resending a client call that failed due to a
371+
* network error to a new address. The Tx queue is maintained intact, thereby
372+
* relieving the need to re-encrypt any request data that has already been
373+
* buffered.
374+
*/
375+
int rxrpc_kernel_retry_call(struct socket *sock, struct rxrpc_call *call,
376+
struct sockaddr_rxrpc *srx, struct key *key)
377+
{
378+
struct rxrpc_conn_parameters cp;
379+
struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
380+
int ret;
381+
382+
_enter("%d{%d}", call->debug_id, atomic_read(&call->usage));
383+
384+
if (!key)
385+
key = rx->key;
386+
if (key && !key->payload.data[0])
387+
key = NULL; /* a no-security key */
388+
389+
memset(&cp, 0, sizeof(cp));
390+
cp.local = rx->local;
391+
cp.key = key;
392+
cp.security_level = 0;
393+
cp.exclusive = false;
394+
cp.service_id = srx->srx_service;
395+
396+
mutex_lock(&call->user_mutex);
397+
398+
ret = rxrpc_prepare_call_for_retry(rx, call);
399+
if (ret == 0)
400+
ret = rxrpc_retry_client_call(rx, call, &cp, srx, GFP_KERNEL);
401+
402+
mutex_unlock(&call->user_mutex);
403+
_leave(" = %d", ret);
404+
return ret;
405+
}
406+
EXPORT_SYMBOL(rxrpc_kernel_retry_call);
407+
339408
/**
340409
* rxrpc_kernel_new_call_notification - Get notifications of new calls
341410
* @sock: The socket to intercept received messages on

net/rxrpc/ar-internal.h

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ enum rxrpc_call_flag {
445445
RXRPC_CALL_EXPOSED, /* The call was exposed to the world */
446446
RXRPC_CALL_RX_LAST, /* Received the last packet (at rxtx_top) */
447447
RXRPC_CALL_TX_LAST, /* Last packet in Tx buffer (at rxtx_top) */
448+
RXRPC_CALL_TX_LASTQ, /* Last packet has been queued */
448449
RXRPC_CALL_SEND_PING, /* A ping will need to be sent */
449450
RXRPC_CALL_PINGING, /* Ping in process */
450451
RXRPC_CALL_RETRANS_TIMEOUT, /* Retransmission due to timeout occurred */
@@ -481,18 +482,6 @@ enum rxrpc_call_state {
481482
NR__RXRPC_CALL_STATES
482483
};
483484

484-
/*
485-
* Call completion condition (state == RXRPC_CALL_COMPLETE).
486-
*/
487-
enum rxrpc_call_completion {
488-
RXRPC_CALL_SUCCEEDED, /* - Normal termination */
489-
RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */
490-
RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */
491-
RXRPC_CALL_LOCAL_ERROR, /* - call failed due to local error */
492-
RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */
493-
NR__RXRPC_CALL_COMPLETIONS
494-
};
495-
496485
/*
497486
* Call Tx congestion management modes.
498487
*/
@@ -687,9 +676,15 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *,
687676
struct rxrpc_conn_parameters *,
688677
struct sockaddr_rxrpc *,
689678
unsigned long, s64, gfp_t);
679+
int rxrpc_retry_client_call(struct rxrpc_sock *,
680+
struct rxrpc_call *,
681+
struct rxrpc_conn_parameters *,
682+
struct sockaddr_rxrpc *,
683+
gfp_t);
690684
void rxrpc_incoming_call(struct rxrpc_sock *, struct rxrpc_call *,
691685
struct sk_buff *);
692686
void rxrpc_release_call(struct rxrpc_sock *, struct rxrpc_call *);
687+
int rxrpc_prepare_call_for_retry(struct rxrpc_sock *, struct rxrpc_call *);
693688
void rxrpc_release_calls_on_socket(struct rxrpc_sock *);
694689
bool __rxrpc_queue_call(struct rxrpc_call *);
695690
bool rxrpc_queue_call(struct rxrpc_call *);

net/rxrpc/call_object.c

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,6 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
269269
trace_rxrpc_call(call, rxrpc_call_connected, atomic_read(&call->usage),
270270
here, NULL);
271271

272-
spin_lock_bh(&call->conn->params.peer->lock);
273-
hlist_add_head(&call->error_link,
274-
&call->conn->params.peer->error_targets);
275-
spin_unlock_bh(&call->conn->params.peer->lock);
276-
277272
rxrpc_start_call_timer(call);
278273

279274
_net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id);
@@ -303,6 +298,48 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
303298
return ERR_PTR(ret);
304299
}
305300

301+
/*
302+
* Retry a call to a new address. It is expected that the Tx queue of the call
303+
* will contain data previously packaged for an old call.
304+
*/
305+
int rxrpc_retry_client_call(struct rxrpc_sock *rx,
306+
struct rxrpc_call *call,
307+
struct rxrpc_conn_parameters *cp,
308+
struct sockaddr_rxrpc *srx,
309+
gfp_t gfp)
310+
{
311+
const void *here = __builtin_return_address(0);
312+
int ret;
313+
314+
/* Set up or get a connection record and set the protocol parameters,
315+
* including channel number and call ID.
316+
*/
317+
ret = rxrpc_connect_call(call, cp, srx, gfp);
318+
if (ret < 0)
319+
goto error;
320+
321+
trace_rxrpc_call(call, rxrpc_call_connected, atomic_read(&call->usage),
322+
here, NULL);
323+
324+
rxrpc_start_call_timer(call);
325+
326+
_net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id);
327+
328+
if (!test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events))
329+
rxrpc_queue_call(call);
330+
331+
_leave(" = 0");
332+
return 0;
333+
334+
error:
335+
rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR,
336+
RX_CALL_DEAD, ret);
337+
trace_rxrpc_call(call, rxrpc_call_error, atomic_read(&call->usage),
338+
here, ERR_PTR(ret));
339+
_leave(" = %d", ret);
340+
return ret;
341+
}
342+
306343
/*
307344
* Set up an incoming call. call->conn points to the connection.
308345
* This is called in BH context and isn't allowed to fail.
@@ -470,6 +507,61 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
470507
_leave("");
471508
}
472509

510+
/*
511+
* Prepare a kernel service call for retry.
512+
*/
513+
int rxrpc_prepare_call_for_retry(struct rxrpc_sock *rx, struct rxrpc_call *call)
514+
{
515+
const void *here = __builtin_return_address(0);
516+
int i;
517+
u8 last = 0;
518+
519+
_enter("{%d,%d}", call->debug_id, atomic_read(&call->usage));
520+
521+
trace_rxrpc_call(call, rxrpc_call_release, atomic_read(&call->usage),
522+
here, (const void *)call->flags);
523+
524+
ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE);
525+
ASSERTCMP(call->completion, !=, RXRPC_CALL_REMOTELY_ABORTED);
526+
ASSERTCMP(call->completion, !=, RXRPC_CALL_LOCALLY_ABORTED);
527+
ASSERT(list_empty(&call->recvmsg_link));
528+
529+
del_timer_sync(&call->timer);
530+
531+
_debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, call->conn);
532+
533+
if (call->conn)
534+
rxrpc_disconnect_call(call);
535+
536+
if (rxrpc_is_service_call(call) ||
537+
!call->tx_phase ||
538+
call->tx_hard_ack != 0 ||
539+
call->rx_hard_ack != 0 ||
540+
call->rx_top != 0)
541+
return -EINVAL;
542+
543+
call->state = RXRPC_CALL_UNINITIALISED;
544+
call->completion = RXRPC_CALL_SUCCEEDED;
545+
call->call_id = 0;
546+
call->cid = 0;
547+
call->cong_cwnd = 0;
548+
call->cong_extra = 0;
549+
call->cong_ssthresh = 0;
550+
call->cong_mode = 0;
551+
call->cong_dup_acks = 0;
552+
call->cong_cumul_acks = 0;
553+
call->acks_lowest_nak = 0;
554+
555+
for (i = 0; i < RXRPC_RXTX_BUFF_SIZE; i++) {
556+
last |= call->rxtx_annotations[i];
557+
call->rxtx_annotations[i] &= RXRPC_TX_ANNO_LAST;
558+
call->rxtx_annotations[i] |= RXRPC_TX_ANNO_RETRANS;
559+
}
560+
561+
_leave(" = 0");
562+
return 0;
563+
}
564+
473565
/*
474566
* release all the calls associated with a socket
475567
*/

net/rxrpc/conn_client.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,10 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn,
555555
trace_rxrpc_client(conn, channel, rxrpc_client_chan_activate);
556556

557557
write_lock_bh(&call->state_lock);
558-
call->state = RXRPC_CALL_CLIENT_SEND_REQUEST;
558+
if (!test_bit(RXRPC_CALL_TX_LASTQ, &call->flags))
559+
call->state = RXRPC_CALL_CLIENT_SEND_REQUEST;
560+
else
561+
call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY;
559562
write_unlock_bh(&call->state_lock);
560563

561564
rxrpc_see_call(call);
@@ -688,15 +691,23 @@ int rxrpc_connect_call(struct rxrpc_call *call,
688691

689692
ret = rxrpc_get_client_conn(call, cp, srx, gfp);
690693
if (ret < 0)
691-
return ret;
694+
goto out;
692695

693696
rxrpc_animate_client_conn(rxnet, call->conn);
694697
rxrpc_activate_channels(call->conn);
695698

696699
ret = rxrpc_wait_for_channel(call, gfp);
697-
if (ret < 0)
700+
if (ret < 0) {
698701
rxrpc_disconnect_client_call(call);
702+
goto out;
703+
}
704+
705+
spin_lock_bh(&call->conn->params.peer->lock);
706+
hlist_add_head(&call->error_link,
707+
&call->conn->params.peer->error_targets);
708+
spin_unlock_bh(&call->conn->params.peer->lock);
699709

710+
out:
700711
_leave(" = %d", ret);
701712
return ret;
702713
}

0 commit comments

Comments
 (0)