Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e255721 authored by David Howells's avatar David Howells
Browse files

rxrpc: Add service upgrade support for client connections



Make it possible for a client to use AuriStor's service upgrade facility.

The client does this by adding an RXRPC_UPGRADE_SERVICE control message to
the first sendmsg() of a call.  This takes no parameters.

When recvmsg() starts returning data from the call, the service ID field in
the returned msg_name will reflect the result of the upgrade attempt.  If
the upgrade was ignored, srx_service will match what was set in the
sendmsg(); if the upgrade happened the srx_service will be altered to
indicate the service the server upgraded to.

Note that:

 (1) The choice of upgrade service is up to the server

 (2) Further client calls to the same server that would share a connection
     are blocked if an upgrade probe is in progress.

 (3) This should only be used to probe the service.  Clients should then
     use the returned service ID in all subsequent communications with that
     server (and not set the upgrade).  Note that the kernel will not
     retain this information should the connection expire from its cache.

 (4) If a server that supports upgrading is replaced by one that doesn't,
     whilst a connection is live, and if the replacement is running, say,
     OpenAFS 1.6.4 or older or an older IBM AFS, then the replacement
     server will not respond to packets sent to the upgraded connection.

     At this point, calls will time out and the server must be reprobed.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 4722974d
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -325,6 +325,8 @@ calls, to invoke certain actions and to report certain conditions. These are:
	RXRPC_LOCAL_ERROR	-rt error num	Local error encountered
	RXRPC_NEW_CALL		-r- n/a		New call received
	RXRPC_ACCEPT		s-- n/a		Accept new call
	RXRPC_EXCLUSIVE_CALL	s-- n/a		Make an exclusive client call
	RXRPC_UPGRADE_SERVICE	s-- n/a		Client call can be upgraded

	(SRT = usable in Sendmsg / delivered by Recvmsg / Terminal message)

@@ -387,6 +389,23 @@ calls, to invoke certain actions and to report certain conditions. These are:
     return error ENODATA.  If the user ID is already in use by another call,
     then error EBADSLT will be returned.

 (*) RXRPC_EXCLUSIVE_CALL

     This is used to indicate that a client call should be made on a one-off
     connection.  The connection is discarded once the call has terminated.

 (*) RXRPC_UPGRADE_SERVICE

     This is used to make a client call to probe if the specified service ID
     may be upgraded by the server.  The caller must check msg_name returned to
     recvmsg() for the service ID actually in use.  The operation probed must
     be one that takes the same arguments in both services.

     Once this has been used to establish the upgrade capability (or lack
     thereof) of the server, the service ID returned should be used for all
     future communication to that server and RXRPC_UPGRADE_SERVICE should no
     longer be set.


==============
SOCKET OPTIONS
@@ -566,6 +585,17 @@ A client would issue an operation by:
     buffer instead, and MSG_EOR will be flagged to indicate the end of that
     call.

A client may ask for a service ID it knows and ask that this be upgraded to a
better service if one is available by supplying RXRPC_UPGRADE_SERVICE on the
first sendmsg() of a call.  The client should then check srx_service in the
msg_name filled in by recvmsg() when collecting the result.  srx_service will
hold the same value as given to sendmsg() if the upgrade request was ignored by
the service - otherwise it will be altered to indicate the service ID the
server upgraded to.  Note that the upgraded service ID is chosen by the server.
The caller has to wait until it sees the service ID in the reply before sending
any more calls (further calls to the same destination will be blocked until the
probe is concluded).


====================
EXAMPLE SERVER USAGE
+1 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ struct sockaddr_rxrpc {
#define RXRPC_NEW_CALL		8	/* -r: [Service] new incoming call notification */
#define RXRPC_ACCEPT		9	/* s-: [Service] accept request */
#define RXRPC_EXCLUSIVE_CALL	10	/* s-: Call should be on exclusive connection */
#define RXRPC_UPGRADE_SERVICE	11	/* s-: Request service upgrade for client call */

/*
 * RxRPC security levels
+1 −0
Original line number Diff line number Diff line
@@ -233,6 +233,7 @@ enum rxrpc_congest_change {
	EM(RXRPC_CONN_CLIENT_INACTIVE,		"Inac") \
	EM(RXRPC_CONN_CLIENT_WAITING,		"Wait") \
	EM(RXRPC_CONN_CLIENT_ACTIVE,		"Actv") \
	EM(RXRPC_CONN_CLIENT_UPGRADE,		"Upgd") \
	EM(RXRPC_CONN_CLIENT_CULLED,		"Cull") \
	E_(RXRPC_CONN_CLIENT_IDLE,		"Idle") \

+3 −0
Original line number Diff line number Diff line
@@ -320,6 +320,7 @@ struct rxrpc_conn_parameters {
	struct rxrpc_peer	*peer;		/* Remote endpoint */
	struct key		*key;		/* Security details */
	bool			exclusive;	/* T if conn is exclusive */
	bool			upgrade;	/* T if service ID can be upgraded */
	u16			service_id;	/* Service ID for this connection */
	u32			security_level;	/* Security level selected */
};
@@ -334,6 +335,7 @@ enum rxrpc_conn_flag {
	RXRPC_CONN_EXPOSED,		/* Conn has extra ref for exposure */
	RXRPC_CONN_DONT_REUSE,		/* Don't reuse this connection */
	RXRPC_CONN_COUNTED,		/* Counted by rxrpc_nr_client_conns */
	RXRPC_CONN_PROBING_FOR_UPGRADE,	/* Probing for service upgrade */
};

/*
@@ -350,6 +352,7 @@ enum rxrpc_conn_cache_state {
	RXRPC_CONN_CLIENT_INACTIVE,	/* Conn is not yet listed */
	RXRPC_CONN_CLIENT_WAITING,	/* Conn is on wait list, waiting for capacity */
	RXRPC_CONN_CLIENT_ACTIVE,	/* Conn is on active list, doing calls */
	RXRPC_CONN_CLIENT_UPGRADE,	/* Conn is on active list, probing for upgrade */
	RXRPC_CONN_CLIENT_CULLED,	/* Conn is culled and delisted, doing calls */
	RXRPC_CONN_CLIENT_IDLE,		/* Conn is on idle list, doing mostly nothing */
	RXRPC_CONN__NR_CACHE_STATES
+35 −8
Original line number Diff line number Diff line
@@ -36,12 +36,15 @@
 *
 *	rxrpc_nr_active_client_conns is held incremented also.
 *
 *  (4) CULLED - The connection got summarily culled to try and free up
 *  (4) UPGRADE - As for ACTIVE, but only one call may be in progress and is
 *      being used to probe for service upgrade.
 *
 *  (5) CULLED - The connection got summarily culled to try and free up
 *      capacity.  Calls currently in progress on the connection are allowed to
 *      continue, but new calls will have to wait.  There can be no waiters in
 *      this state - the conn would have to go to the WAITING state instead.
 *
 *  (5) IDLE - The connection has no calls in progress upon it and must have
 *  (6) IDLE - The connection has no calls in progress upon it and must have
 *      been exposed to the world (ie. the EXPOSED flag must be set).  When it
 *      expires, the EXPOSED flag is cleared and the connection transitions to
 *      the INACTIVE state.
@@ -184,6 +187,8 @@ rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp)
	atomic_set(&conn->usage, 1);
	if (cp->exclusive)
		__set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
	if (cp->upgrade)
		__set_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags);

	conn->params		= *cp;
	conn->out_clientflag	= RXRPC_CLIENT_INITIATED;
@@ -300,7 +305,8 @@ static int rxrpc_get_client_conn(struct rxrpc_call *call,
#define cmp(X) ((long)conn->params.X - (long)cp->X)
			diff = (cmp(peer) ?:
				cmp(key) ?:
				cmp(security_level));
				cmp(security_level) ?:
				cmp(upgrade));
#undef cmp
			if (diff < 0) {
				p = p->rb_left;
@@ -365,7 +371,8 @@ static int rxrpc_get_client_conn(struct rxrpc_call *call,
#define cmp(X) ((long)conn->params.X - (long)candidate->params.X)
		diff = (cmp(peer) ?:
			cmp(key) ?:
			cmp(security_level));
			cmp(security_level) ?:
			cmp(upgrade));
#undef cmp
		if (diff < 0) {
			pp = &(*pp)->rb_left;
@@ -436,8 +443,13 @@ static int rxrpc_get_client_conn(struct rxrpc_call *call,
static void rxrpc_activate_conn(struct rxrpc_net *rxnet,
				struct rxrpc_connection *conn)
{
	if (test_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags)) {
		trace_rxrpc_client(conn, -1, rxrpc_client_to_upgrade);
		conn->cache_state = RXRPC_CONN_CLIENT_UPGRADE;
	} else {
		trace_rxrpc_client(conn, -1, rxrpc_client_to_active);
		conn->cache_state = RXRPC_CONN_CLIENT_ACTIVE;
	}
	rxnet->nr_active_client_conns++;
	list_move_tail(&conn->cache_link, &rxnet->active_client_conns);
}
@@ -461,7 +473,8 @@ static void rxrpc_animate_client_conn(struct rxrpc_net *rxnet,

	_enter("%d,%d", conn->debug_id, conn->cache_state);

	if (conn->cache_state == RXRPC_CONN_CLIENT_ACTIVE)
	if (conn->cache_state == RXRPC_CONN_CLIENT_ACTIVE ||
	    conn->cache_state == RXRPC_CONN_CLIENT_UPGRADE)
		goto out;

	spin_lock(&rxnet->client_conn_cache_lock);
@@ -474,6 +487,7 @@ static void rxrpc_animate_client_conn(struct rxrpc_net *rxnet,

	switch (conn->cache_state) {
	case RXRPC_CONN_CLIENT_ACTIVE:
	case RXRPC_CONN_CLIENT_UPGRADE:
	case RXRPC_CONN_CLIENT_WAITING:
		break;

@@ -577,6 +591,9 @@ static void rxrpc_activate_channels_locked(struct rxrpc_connection *conn)
	case RXRPC_CONN_CLIENT_ACTIVE:
		mask = RXRPC_ACTIVE_CHANS_MASK;
		break;
	case RXRPC_CONN_CLIENT_UPGRADE:
		mask = 0x01;
		break;
	default:
		return;
	}
@@ -787,6 +804,15 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
	spin_lock(&rxnet->client_conn_cache_lock);

	switch (conn->cache_state) {
	case RXRPC_CONN_CLIENT_UPGRADE:
		/* Deal with termination of a service upgrade probe. */
		if (test_bit(RXRPC_CONN_EXPOSED, &conn->flags)) {
			clear_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags);
			trace_rxrpc_client(conn, channel, rxrpc_client_to_active);
			conn->cache_state = RXRPC_CONN_CLIENT_ACTIVE;
			rxrpc_activate_channels_locked(conn);
		}
		/* fall through */
	case RXRPC_CONN_CLIENT_ACTIVE:
		if (list_empty(&conn->waiting_calls)) {
			rxrpc_deactivate_one_channel(conn, channel);
@@ -941,7 +967,8 @@ static void rxrpc_cull_active_client_conns(struct rxrpc_net *rxnet)
		ASSERT(!list_empty(&rxnet->active_client_conns));
		conn = list_entry(rxnet->active_client_conns.next,
				  struct rxrpc_connection, cache_link);
		ASSERTCMP(conn->cache_state, ==, RXRPC_CONN_CLIENT_ACTIVE);
		ASSERTIFCMP(conn->cache_state != RXRPC_CONN_CLIENT_ACTIVE,
			    conn->cache_state, ==, RXRPC_CONN_CLIENT_UPGRADE);

		if (list_empty(&conn->waiting_calls)) {
			trace_rxrpc_client(conn, -1, rxrpc_client_to_culled);
Loading