Skip to content

Commit

Permalink
Merge tag 'rxrpc-next-20170829' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/kernel/git/dhowells/linux-fs

David Howells says:

====================
rxrpc: Miscellany

Here are a number of patches that make some changes/fixes and add a couple
of extensions to AF_RXRPC for kernel services to use.  The changes and
fixes are:

 (1) Use time64_t rather than u32 outside of protocol or
     UAPI-representative structures.

 (2) Use the correct time stamp when loading a key from an XDR-encoded
     Kerberos 5 key.

 (3) Fix IPv6 support.

 (4) Fix some places where the error code is being incorrectly made
     positive before returning.

 (5) Remove some white space.

And the extensions:

 (6) Add an end-of-Tx phase notification, thereby allowing kAFS to
     transition the state on its own call record at the correct point,
     rather than having to do it in advance and risk non-completion of the
     call in the wrong state.

 (7) Allow a kernel client call to be retried if it fails on a network
     error, thereby making it possible for kAFS to iterate over a number of
     IP addresses without having to reload the Tx queue and re-encrypt data
     each time.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 29, 2017
2 parents 3d86e35 + c038a58 commit d0fcece
Show file tree
Hide file tree
Showing 17 changed files with 419 additions and 90 deletions.
57 changes: 56 additions & 1 deletion Documentation/networking/rxrpc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -818,10 +818,15 @@ The kernel interface functions are as follows:

(*) Send data through a call.

typedef void (*rxrpc_notify_end_tx_t)(struct sock *sk,
unsigned long user_call_ID,
struct sk_buff *skb);

int rxrpc_kernel_send_data(struct socket *sock,
struct rxrpc_call *call,
struct msghdr *msg,
size_t len);
size_t len,
rxrpc_notify_end_tx_t notify_end_rx);

This is used to supply either the request part of a client call or the
reply part of a server call. msg.msg_iovlen and msg.msg_iov specify the
Expand All @@ -832,6 +837,11 @@ The kernel interface functions are as follows:
The msg must not specify a destination address, control data or any flags
other than MSG_MORE. len is the total amount of data to transmit.

notify_end_rx can be NULL or it can be used to specify a function to be
called when the call changes state to end the Tx phase. This function is
called with the call-state spinlock held to prevent any reply or final ACK
from being delivered first.

(*) Receive data from a call.

int rxrpc_kernel_recv_data(struct socket *sock,
Expand Down Expand Up @@ -965,6 +975,51 @@ The kernel interface functions are as follows:
size should be set when the call is begun. tx_total_len may not be less
than zero.

(*) Check to see the completion state of a call so that the caller can assess
whether it needs to be retried.

enum rxrpc_call_completion {
RXRPC_CALL_SUCCEEDED,
RXRPC_CALL_REMOTELY_ABORTED,
RXRPC_CALL_LOCALLY_ABORTED,
RXRPC_CALL_LOCAL_ERROR,
RXRPC_CALL_NETWORK_ERROR,
};

int rxrpc_kernel_check_call(struct socket *sock, struct rxrpc_call *call,
enum rxrpc_call_completion *_compl,
u32 *_abort_code);

On return, -EINPROGRESS will be returned if the call is still ongoing; if
it is finished, *_compl will be set to indicate the manner of completion,
*_abort_code will be set to any abort code that occurred. 0 will be
returned on a successful completion, -ECONNABORTED will be returned if the
client failed due to a remote abort and anything else will return an
appropriate error code.

The caller should look at this information to decide if it's worth
retrying the call.

(*) Retry a client call.

int rxrpc_kernel_retry_call(struct socket *sock,
struct rxrpc_call *call,
struct sockaddr_rxrpc *srx,
struct key *key);

This attempts to partially reinitialise a call and submit it again whilst
reusing the original call's Tx queue to avoid the need to repackage and
re-encrypt the data to be sent. call indicates the call to retry, srx the
new address to send it to and key the encryption key to use for signing or
encrypting the packets.

For this to work, the first Tx data packet must still be in the transmit
queue, and currently this is only permitted for local and network errors
and the call must not have been aborted. Any partially constructed Tx
packet is left as is and can continue being filled afterwards.

It returns 0 if the call was requeued and an error otherwise.


=======================
CONFIGURABLE PARAMETERS
Expand Down
46 changes: 35 additions & 11 deletions fs/afs/rxrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,19 @@ static void afs_load_bvec(struct afs_call *call, struct msghdr *msg,
iov_iter_bvec(&msg->msg_iter, WRITE | ITER_BVEC, bv, nr, bytes);
}

/*
* Advance the AFS call state when the RxRPC call ends the transmit phase.
*/
static void afs_notify_end_request_tx(struct sock *sock,
struct rxrpc_call *rxcall,
unsigned long call_user_ID)
{
struct afs_call *call = (struct afs_call *)call_user_ID;

if (call->state == AFS_CALL_REQUESTING)
call->state = AFS_CALL_AWAIT_REPLY;
}

/*
* attach the data from a bunch of pages on an inode to a call
*/
Expand All @@ -310,14 +323,8 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
bytes = msg->msg_iter.count;
nr = msg->msg_iter.nr_segs;

/* Have to change the state *before* sending the last
* packet as RxRPC might give us the reply before it
* returns from sending the request.
*/
if (first + nr - 1 >= last)
call->state = AFS_CALL_AWAIT_REPLY;
ret = rxrpc_kernel_send_data(afs_socket, call->rxcall,
msg, bytes);
ret = rxrpc_kernel_send_data(afs_socket, call->rxcall, msg,
bytes, afs_notify_end_request_tx);
for (loop = 0; loop < nr; loop++)
put_page(bv[loop].bv_page);
if (ret < 0)
Expand Down Expand Up @@ -409,7 +416,8 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp,
if (!call->send_pages)
call->state = AFS_CALL_AWAIT_REPLY;
ret = rxrpc_kernel_send_data(afs_socket, rxcall,
&msg, call->request_size);
&msg, call->request_size,
afs_notify_end_request_tx);
if (ret < 0)
goto error_do_abort;

Expand Down Expand Up @@ -740,6 +748,20 @@ static int afs_deliver_cm_op_id(struct afs_call *call)
return call->type->deliver(call);
}

/*
* Advance the AFS call state when an RxRPC service call ends the transmit
* phase.
*/
static void afs_notify_end_reply_tx(struct sock *sock,
struct rxrpc_call *rxcall,
unsigned long call_user_ID)
{
struct afs_call *call = (struct afs_call *)call_user_ID;

if (call->state == AFS_CALL_REPLYING)
call->state = AFS_CALL_AWAIT_ACK;
}

/*
* send an empty reply
*/
Expand All @@ -759,7 +781,8 @@ void afs_send_empty_reply(struct afs_call *call)
msg.msg_flags = 0;

call->state = AFS_CALL_AWAIT_ACK;
switch (rxrpc_kernel_send_data(afs_socket, call->rxcall, &msg, 0)) {
switch (rxrpc_kernel_send_data(afs_socket, call->rxcall, &msg, 0,
afs_notify_end_reply_tx)) {
case 0:
_leave(" [replied]");
return;
Expand Down Expand Up @@ -797,7 +820,8 @@ void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len)
msg.msg_flags = 0;

call->state = AFS_CALL_AWAIT_ACK;
n = rxrpc_kernel_send_data(afs_socket, call->rxcall, &msg, len);
n = rxrpc_kernel_send_data(afs_socket, call->rxcall, &msg, len,
afs_notify_end_reply_tx);
if (n >= 0) {
/* Success */
_leave(" [replied]");
Expand Down
23 changes: 23 additions & 0 deletions include/keys/rxrpc-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,27 @@ struct rxrpc_key_data_v1 {
#define AFSTOKEN_K5_ADDRESSES_MAX 16 /* max K5 addresses */
#define AFSTOKEN_K5_AUTHDATA_MAX 16 /* max K5 pieces of auth data */

/*
* Truncate a time64_t to the range from 1970 to 2106 as in the network
* protocol.
*/
static inline u32 rxrpc_time64_to_u32(time64_t time)
{
if (time < 0)
return 0;

if (time > UINT_MAX)
return UINT_MAX;

return (u32)time;
}

/*
* Extend u32 back to time64_t using the same 1970-2106 range.
*/
static inline time64_t rxrpc_u32_to_time64(u32 time)
{
return (time64_t)time;
}

#endif /* _KEYS_RXRPC_TYPE_H */
21 changes: 20 additions & 1 deletion include/net/af_rxrpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,22 @@ struct sock;
struct socket;
struct rxrpc_call;

/*
* Call completion condition (state == RXRPC_CALL_COMPLETE).
*/
enum rxrpc_call_completion {
RXRPC_CALL_SUCCEEDED, /* - Normal termination */
RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */
RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */
RXRPC_CALL_LOCAL_ERROR, /* - call failed due to local error */
RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */
NR__RXRPC_CALL_COMPLETIONS
};

typedef void (*rxrpc_notify_rx_t)(struct sock *, struct rxrpc_call *,
unsigned long);
typedef void (*rxrpc_notify_end_tx_t)(struct sock *, struct rxrpc_call *,
unsigned long);
typedef void (*rxrpc_notify_new_call_t)(struct sock *, struct rxrpc_call *,
unsigned long);
typedef void (*rxrpc_discard_new_call_t)(struct rxrpc_call *, unsigned long);
Expand All @@ -37,7 +51,8 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *,
gfp_t,
rxrpc_notify_rx_t);
int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *,
struct msghdr *, size_t);
struct msghdr *, size_t,
rxrpc_notify_end_tx_t);
int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *,
void *, size_t, size_t *, bool, u32 *);
bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *,
Expand All @@ -48,5 +63,9 @@ void rxrpc_kernel_get_peer(struct socket *, struct rxrpc_call *,
int rxrpc_kernel_charge_accept(struct socket *, rxrpc_notify_rx_t,
rxrpc_user_attach_call_t, unsigned long, gfp_t);
void rxrpc_kernel_set_tx_length(struct socket *, struct rxrpc_call *, s64);
int rxrpc_kernel_retry_call(struct socket *, struct rxrpc_call *,
struct sockaddr_rxrpc *, struct key *);
int rxrpc_kernel_check_call(struct socket *, struct rxrpc_call *,
enum rxrpc_call_completion *, u32 *);

#endif /* _NET_RXRPC_H */
75 changes: 72 additions & 3 deletions net/rxrpc/af_rxrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,75 @@ void rxrpc_kernel_end_call(struct socket *sock, struct rxrpc_call *call)
}
EXPORT_SYMBOL(rxrpc_kernel_end_call);

/**
* rxrpc_kernel_check_call - Check a call's state
* @sock: The socket the call is on
* @call: The call to check
* @_compl: Where to store the completion state
* @_abort_code: Where to store any abort code
*
* Allow a kernel service to query the state of a call and find out the manner
* of its termination if it has completed. Returns -EINPROGRESS if the call is
* still going, 0 if the call finished successfully, -ECONNABORTED if the call
* was aborted and an appropriate error if the call failed in some other way.
*/
int rxrpc_kernel_check_call(struct socket *sock, struct rxrpc_call *call,
enum rxrpc_call_completion *_compl, u32 *_abort_code)
{
if (call->state != RXRPC_CALL_COMPLETE)
return -EINPROGRESS;
smp_rmb();
*_compl = call->completion;
*_abort_code = call->abort_code;
return call->error;
}
EXPORT_SYMBOL(rxrpc_kernel_check_call);

/**
* rxrpc_kernel_retry_call - Allow a kernel service to retry a call
* @sock: The socket the call is on
* @call: The call to retry
* @srx: The address of the peer to contact
* @key: The security context to use (defaults to socket setting)
*
* Allow a kernel service to try resending a client call that failed due to a
* network error to a new address. The Tx queue is maintained intact, thereby
* relieving the need to re-encrypt any request data that has already been
* buffered.
*/
int rxrpc_kernel_retry_call(struct socket *sock, struct rxrpc_call *call,
struct sockaddr_rxrpc *srx, struct key *key)
{
struct rxrpc_conn_parameters cp;
struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
int ret;

_enter("%d{%d}", call->debug_id, atomic_read(&call->usage));

if (!key)
key = rx->key;
if (key && !key->payload.data[0])
key = NULL; /* a no-security key */

memset(&cp, 0, sizeof(cp));
cp.local = rx->local;
cp.key = key;
cp.security_level = 0;
cp.exclusive = false;
cp.service_id = srx->srx_service;

mutex_lock(&call->user_mutex);

ret = rxrpc_prepare_call_for_retry(rx, call);
if (ret == 0)
ret = rxrpc_retry_client_call(rx, call, &cp, srx, GFP_KERNEL);

mutex_unlock(&call->user_mutex);
_leave(" = %d", ret);
return ret;
}
EXPORT_SYMBOL(rxrpc_kernel_retry_call);

/**
* rxrpc_kernel_new_call_notification - Get notifications of new calls
* @sock: The socket to intercept received messages on
Expand Down Expand Up @@ -591,13 +660,13 @@ static int rxrpc_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *_optlen)
{
int optlen;

if (level != SOL_RXRPC)
return -EOPNOTSUPP;

if (get_user(optlen, _optlen))
return -EFAULT;

switch (optname) {
case RXRPC_SUPPORTED_CMSG:
if (optlen < sizeof(int))
Expand All @@ -606,7 +675,7 @@ static int rxrpc_getsockopt(struct socket *sock, int level, int optname,
put_user(sizeof(int), _optlen))
return -EFAULT;
return 0;

default:
return -EOPNOTSUPP;
}
Expand Down
Loading

0 comments on commit d0fcece

Please sign in to comment.