rxrpc: Support service upgrade from a kernel service

Provide support for a kernel service to make use of the service upgrade
facility.  This involves:

 (1) Pass an upgrade request flag to rxrpc_kernel_begin_call().

 (2) Make rxrpc_kernel_recv_data() return the call's current service ID so
     that the caller can detect service upgrade and see what the service
     was upgraded to.

Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
David Howells 2017-10-18 11:36:39 +01:00
parent 8a5f2166a6
commit a68f4a27f5
6 changed files with 34 additions and 10 deletions

View File

@ -782,7 +782,9 @@ The kernel interface functions are as follows:
struct key *key, struct key *key,
unsigned long user_call_ID, unsigned long user_call_ID,
s64 tx_total_len, s64 tx_total_len,
gfp_t gfp); gfp_t gfp,
rxrpc_notify_rx_t notify_rx,
bool upgrade);
This allocates the infrastructure to make a new RxRPC call and assigns This allocates the infrastructure to make a new RxRPC call and assigns
call and connection numbers. The call will be made on the UDP port that call and connection numbers. The call will be made on the UDP port that
@ -803,6 +805,13 @@ The kernel interface functions are as follows:
allows the kernel to encrypt directly to the packet buffers, thereby allows the kernel to encrypt directly to the packet buffers, thereby
saving a copy. The value may not be less than -1. saving a copy. The value may not be less than -1.
notify_rx is a pointer to a function to be called when events such as
incoming data packets or remote aborts happen.
upgrade should be set to true if a client operation should request that
the server upgrade the service to a better one. The resultant service ID
is returned by rxrpc_kernel_recv_data().
If this function is successful, an opaque reference to the RxRPC call is If this function is successful, an opaque reference to the RxRPC call is
returned. The caller now holds a reference on this and it must be returned. The caller now holds a reference on this and it must be
properly ended. properly ended.
@ -850,7 +859,8 @@ The kernel interface functions are as follows:
size_t size, size_t size,
size_t *_offset, size_t *_offset,
bool want_more, bool want_more,
u32 *_abort) u32 *_abort,
u16 *_service)
This is used to receive data from either the reply part of a client call This is used to receive data from either the reply part of a client call
or the request part of a service call. buf and size specify how much or the request part of a service call. buf and size specify how much
@ -873,6 +883,9 @@ The kernel interface functions are as follows:
If a remote ABORT is detected, the abort code received will be stored in If a remote ABORT is detected, the abort code received will be stored in
*_abort and ECONNABORTED will be returned. *_abort and ECONNABORTED will be returned.
The service ID that the call ended up with is returned into *_service.
This can be used to see if a call got a service upgrade.
(*) Abort a call. (*) Abort a call.
void rxrpc_kernel_abort_call(struct socket *sock, void rxrpc_kernel_abort_call(struct socket *sock,

View File

@ -100,6 +100,7 @@ struct afs_call {
bool send_pages; /* T if data from mapping should be sent */ bool send_pages; /* T if data from mapping should be sent */
bool need_attention; /* T if RxRPC poked us */ bool need_attention; /* T if RxRPC poked us */
bool async; /* T if asynchronous */ bool async; /* T if asynchronous */
bool upgrade; /* T to request service upgrade */
u16 service_id; /* RxRPC service ID to call */ u16 service_id; /* RxRPC service ID to call */
__be16 port; /* target UDP port */ __be16 port; /* target UDP port */
u32 operation_ID; /* operation ID for an incoming call */ u32 operation_ID; /* operation ID for an incoming call */

View File

@ -387,7 +387,8 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp,
tx_total_len, gfp, tx_total_len, gfp,
(async ? (async ?
afs_wake_up_async_call : afs_wake_up_async_call :
afs_wake_up_call_waiter)); afs_wake_up_call_waiter),
call->upgrade);
call->key = NULL; call->key = NULL;
if (IS_ERR(rxcall)) { if (IS_ERR(rxcall)) {
ret = PTR_ERR(rxcall); ret = PTR_ERR(rxcall);
@ -443,7 +444,7 @@ error_do_abort:
abort_code = 0; abort_code = 0;
offset = 0; offset = 0;
rxrpc_kernel_recv_data(afs_socket, rxcall, NULL, 0, &offset, rxrpc_kernel_recv_data(afs_socket, rxcall, NULL, 0, &offset,
false, &abort_code); false, &abort_code, &call->service_id);
ret = call->type->abort_to_error(abort_code); ret = call->type->abort_to_error(abort_code);
} }
error_kill_call: error_kill_call:
@ -471,7 +472,8 @@ static void afs_deliver_to_call(struct afs_call *call)
size_t offset = 0; size_t offset = 0;
ret = rxrpc_kernel_recv_data(afs_socket, call->rxcall, ret = rxrpc_kernel_recv_data(afs_socket, call->rxcall,
NULL, 0, &offset, false, NULL, 0, &offset, false,
&call->abort_code); &call->abort_code,
&call->service_id);
trace_afs_recv_data(call, 0, offset, false, ret); trace_afs_recv_data(call, 0, offset, false, ret);
if (ret == -EINPROGRESS || ret == -EAGAIN) if (ret == -EINPROGRESS || ret == -EAGAIN)
@ -851,7 +853,8 @@ int afs_extract_data(struct afs_call *call, void *buf, size_t count,
ret = rxrpc_kernel_recv_data(afs_socket, call->rxcall, ret = rxrpc_kernel_recv_data(afs_socket, call->rxcall,
buf, count, &call->offset, buf, count, &call->offset,
want_more, &call->abort_code); want_more, &call->abort_code,
&call->service_id);
trace_afs_recv_data(call, count, call->offset, want_more, ret); trace_afs_recv_data(call, count, call->offset, want_more, ret);
if (ret == 0 || ret == -EAGAIN) if (ret == 0 || ret == -EAGAIN)
return ret; return ret;

View File

@ -49,12 +49,13 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *,
unsigned long, unsigned long,
s64, s64,
gfp_t, gfp_t,
rxrpc_notify_rx_t); rxrpc_notify_rx_t,
bool);
int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *, int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *,
struct msghdr *, size_t, struct msghdr *, size_t,
rxrpc_notify_end_tx_t); rxrpc_notify_end_tx_t);
int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *, int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *,
void *, size_t, size_t *, bool, u32 *); void *, size_t, size_t *, bool, u32 *, u16 *);
bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *, bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *,
u32, int, const char *); u32, int, const char *);
void rxrpc_kernel_end_call(struct socket *, struct rxrpc_call *); void rxrpc_kernel_end_call(struct socket *, struct rxrpc_call *);

View File

@ -265,6 +265,7 @@ static int rxrpc_listen(struct socket *sock, int backlog)
* @tx_total_len: Total length of data to transmit during the call (or -1) * @tx_total_len: Total length of data to transmit during the call (or -1)
* @gfp: The allocation constraints * @gfp: The allocation constraints
* @notify_rx: Where to send notifications instead of socket queue * @notify_rx: Where to send notifications instead of socket queue
* @upgrade: Request service upgrade for call
* *
* Allow a kernel service to begin a call on the nominated socket. This just * Allow a kernel service to begin a call on the nominated socket. This just
* sets up all the internal tracking structures and allocates connection and * sets up all the internal tracking structures and allocates connection and
@ -279,7 +280,8 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
unsigned long user_call_ID, unsigned long user_call_ID,
s64 tx_total_len, s64 tx_total_len,
gfp_t gfp, gfp_t gfp,
rxrpc_notify_rx_t notify_rx) rxrpc_notify_rx_t notify_rx,
bool upgrade)
{ {
struct rxrpc_conn_parameters cp; struct rxrpc_conn_parameters cp;
struct rxrpc_call *call; struct rxrpc_call *call;
@ -304,6 +306,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
cp.key = key; cp.key = key;
cp.security_level = 0; cp.security_level = 0;
cp.exclusive = false; cp.exclusive = false;
cp.upgrade = upgrade;
cp.service_id = srx->srx_service; cp.service_id = srx->srx_service;
call = rxrpc_new_client_call(rx, &cp, srx, user_call_ID, tx_total_len, call = rxrpc_new_client_call(rx, &cp, srx, user_call_ID, tx_total_len,
gfp); gfp);

View File

@ -607,6 +607,7 @@ wait_error:
* @_offset: The running offset into the buffer. * @_offset: The running offset into the buffer.
* @want_more: True if more data is expected to be read * @want_more: True if more data is expected to be read
* @_abort: Where the abort code is stored if -ECONNABORTED is returned * @_abort: Where the abort code is stored if -ECONNABORTED is returned
* @_service: Where to store the actual service ID (may be upgraded)
* *
* Allow a kernel service to receive data and pick up information about the * Allow a kernel service to receive data and pick up information about the
* state of a call. Returns 0 if got what was asked for and there's more * state of a call. Returns 0 if got what was asked for and there's more
@ -624,7 +625,7 @@ wait_error:
*/ */
int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call, int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call,
void *buf, size_t size, size_t *_offset, void *buf, size_t size, size_t *_offset,
bool want_more, u32 *_abort) bool want_more, u32 *_abort, u16 *_service)
{ {
struct iov_iter iter; struct iov_iter iter;
struct kvec iov; struct kvec iov;
@ -680,6 +681,8 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call,
read_phase_complete: read_phase_complete:
ret = 1; ret = 1;
out: out:
if (_service)
*_service = call->service_id;
mutex_unlock(&call->user_mutex); mutex_unlock(&call->user_mutex);
_leave(" = %d [%zu,%d]", ret, *_offset, *_abort); _leave(" = %d [%zu,%d]", ret, *_offset, *_abort);
return ret; return ret;