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

Commit 6546c78e authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'rxrpc-rewrite-20160824-2' of...

Merge tag 'rxrpc-rewrite-20160824-2' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs



David Howells says:

====================
rxrpc: Add better client conn management strategy

These two patches add a better client connection management strategy.  They
need to be applied on top of the just-posted fixes.

 (1) Duplicate the connection list and separate out procfs iteration from
     garbage collection.  This is necessary for the next patch as with that
     client connections no longer appear on a single list and may not
     appear on a list at all - and really don't want to be exposed to the
     old garbage collector.

     (Note that client conns aren't left dangling, they're also in a tree
     rooted in the local endpoint so that they can be found by a user
     wanting to make a new client call.  Service conns do not appear in
     this tree.)

 (2) Implement a better lifetime management and garbage collection strategy
     for client connections.

     In this, a client connection can be in one of five cache states
     (inactive, waiting, active, culled and idle).  Limits are set on the
     number of client conns that may be active at any one time and makes
     users wait if they want to start a new call when there isn't capacity
     available.

     To make capacity available, active and idle connections can be culled,
     after a short delay (to allow for retransmission).  The delay is
     reduced if the capacity exceeds a tunable threshold.

     If there is spare capacity, client conns are permitted to hang around
     a fair bit longer (tunable) so as to allow reuse of negotiated
     security contexts.

     After this patch, the client conn strategy is separate from that of
     service conns (which continues to use the old code for the moment).

     This difference in strategy is because the client side retains control
     over when it allows a connection to become active, whereas the service
     side has no control over when it sees a new connection or a new call
     on an old connection.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d3c10db1 45025bce
Loading
Loading
Loading
Loading
+48 −10
Original line number Diff line number Diff line
@@ -255,6 +255,9 @@ enum rxrpc_conn_flag {
	RXRPC_CONN_HAS_IDR,		/* Has a client conn ID assigned */
	RXRPC_CONN_IN_SERVICE_CONNS,	/* Conn is in peer->service_conns */
	RXRPC_CONN_IN_CLIENT_CONNS,	/* Conn is in local->client_conns */
	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 */
};

/*
@@ -264,6 +267,17 @@ enum rxrpc_conn_event {
	RXRPC_CONN_EV_CHALLENGE,	/* Send challenge packet */
};

/*
 * The connection cache state.
 */
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_CULLED,	/* Conn is culled and delisted, doing calls */
	RXRPC_CONN_CLIENT_IDLE,		/* Conn is on idle list, doing mostly nothing */
};

/*
 * The connection protocol state.
 */
@@ -276,6 +290,7 @@ enum rxrpc_conn_proto_state {
	RXRPC_CONN_REMOTELY_ABORTED,	/* Conn aborted by peer */
	RXRPC_CONN_LOCALLY_ABORTED,	/* Conn aborted locally */
	RXRPC_CONN_NETWORK_ERROR,	/* Conn terminated by network error */
	RXRPC_CONN_LOCAL_ERROR,		/* Conn terminated by local error */
	RXRPC_CONN__NR_STATES
};

@@ -288,8 +303,14 @@ struct rxrpc_connection {
	struct rxrpc_conn_proto	proto;
	struct rxrpc_conn_parameters params;

	spinlock_t		channel_lock;
	atomic_t		usage;
	struct rcu_head		rcu;
	struct list_head	cache_link;

	spinlock_t		channel_lock;
	unsigned char		active_chans;	/* Mask of active channels */
#define RXRPC_ACTIVE_CHANS_MASK	((1 << RXRPC_MAXCALLS) - 1)
	struct list_head	waiting_calls;	/* Calls waiting for channels */
	struct rxrpc_channel {
		struct rxrpc_call __rcu	*call;		/* Active call */
		u32			call_id;	/* ID of current call */
@@ -302,14 +323,13 @@ struct rxrpc_connection {
			u32		last_abort;
		};
	} channels[RXRPC_MAXCALLS];
	wait_queue_head_t	channel_wq;	/* queue to wait for channel to become available */

	struct rcu_head		rcu;
	struct work_struct	processor;	/* connection event processor */
	union {
		struct rb_node	client_node;	/* Node in local->client_conns */
		struct rb_node	service_node;	/* Node in peer->service_conns */
	};
	struct list_head	proc_link;	/* link in procfs list */
	struct list_head	link;		/* link in master connection list */
	struct sk_buff_head	rx_queue;	/* received conn-level packets */
	const struct rxrpc_security *security;	/* applied security module */
@@ -320,7 +340,7 @@ struct rxrpc_connection {
	unsigned long		events;
	unsigned long		idle_timestamp;	/* Time at which last became idle */
	spinlock_t		state_lock;	/* state-change lock */
	atomic_t		usage;
	enum rxrpc_conn_cache_state cache_state : 8;
	enum rxrpc_conn_proto_state state : 8;	/* current state of connection */
	u32			local_abort;	/* local abort code */
	u32			remote_abort;	/* remote abort code */
@@ -328,7 +348,6 @@ struct rxrpc_connection {
	int			debug_id;	/* debug ID for printks */
	atomic_t		serial;		/* packet serial number counter */
	unsigned int		hi_serial;	/* highest serial number received */
	atomic_t		avail_chans;	/* number of channels available */
	u8			size_align;	/* data size alignment (for security) */
	u8			header_size;	/* rxrpc + security header size */
	u8			security_size;	/* security header size */
@@ -350,6 +369,7 @@ enum rxrpc_call_flag {
	RXRPC_CALL_HAS_USERID,		/* has a user ID attached */
	RXRPC_CALL_EXPECT_OOS,		/* expect out of sequence packets */
	RXRPC_CALL_IS_SERVICE,		/* Call is service call */
	RXRPC_CALL_EXPOSED,		/* The call was exposed to the world */
};

/*
@@ -416,13 +436,14 @@ struct rxrpc_call {
	struct work_struct	destroyer;	/* call destroyer */
	struct work_struct	processor;	/* packet processor and ACK generator */
	struct list_head	link;		/* link in master call list */
	struct list_head	chan_wait_link;	/* Link in conn->waiting_calls */
	struct hlist_node	error_link;	/* link in error distribution list */
	struct list_head	accept_link;	/* calls awaiting acceptance */
	struct rb_node		sock_node;	/* node in socket call tree */
	struct sk_buff_head	rx_queue;	/* received packets */
	struct sk_buff_head	rx_oos_queue;	/* packets received out of sequence */
	struct sk_buff		*tx_pending;	/* Tx socket buffer being filled */
	wait_queue_head_t	tx_waitq;	/* wait for Tx window space to become available */
	wait_queue_head_t	waitq;		/* Wait queue for channel or Tx */
	__be32			crypto_buf[2];	/* Temporary packet crypto buffer */
	unsigned long		user_call_ID;	/* user-defined call ID */
	unsigned long		creation_jif;	/* time of call creation */
@@ -545,12 +566,19 @@ static inline bool rxrpc_is_client_call(const struct rxrpc_call *call)
/*
 * conn_client.c
 */
extern unsigned int rxrpc_max_client_connections;
extern unsigned int rxrpc_reap_client_connections;
extern unsigned int rxrpc_conn_idle_client_expiry;
extern unsigned int rxrpc_conn_idle_client_fast_expiry;
extern struct idr rxrpc_client_conn_ids;

void rxrpc_destroy_client_conn_ids(void);
int rxrpc_connect_call(struct rxrpc_call *, struct rxrpc_conn_parameters *,
		       struct sockaddr_rxrpc *, gfp_t);
void rxrpc_unpublish_client_conn(struct rxrpc_connection *);
void rxrpc_expose_client_call(struct rxrpc_call *);
void rxrpc_disconnect_client_call(struct rxrpc_call *);
void rxrpc_put_client_conn(struct rxrpc_connection *);
void __exit rxrpc_destroy_all_client_connections(void);

/*
 * conn_event.c
@@ -564,14 +592,16 @@ void rxrpc_reject_packets(struct rxrpc_local *);
 */
extern unsigned int rxrpc_connection_expiry;
extern struct list_head rxrpc_connections;
extern struct list_head rxrpc_connection_proc_list;
extern rwlock_t rxrpc_connection_lock;

int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *);
struct rxrpc_connection *rxrpc_alloc_connection(gfp_t);
struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *,
						   struct sk_buff *);
void __rxrpc_disconnect_call(struct rxrpc_call *);
void __rxrpc_disconnect_call(struct rxrpc_connection *, struct rxrpc_call *);
void rxrpc_disconnect_call(struct rxrpc_call *);
void rxrpc_kill_connection(struct rxrpc_connection *);
void __rxrpc_put_connection(struct rxrpc_connection *);
void __exit rxrpc_destroy_all_connections(void);

@@ -598,9 +628,17 @@ struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *con

static inline void rxrpc_put_connection(struct rxrpc_connection *conn)
{
	if (conn && atomic_dec_return(&conn->usage) == 1)
	if (!conn)
		return;

	if (rxrpc_conn_is_client(conn)) {
		if (atomic_dec_and_test(&conn->usage))
			rxrpc_put_client_conn(conn);
	} else {
		if (atomic_dec_return(&conn->usage) == 1)
			__rxrpc_put_connection(conn);
	}
}


static inline bool rxrpc_queue_conn(struct rxrpc_connection *conn)
+3 −1
Original line number Diff line number Diff line
@@ -193,6 +193,8 @@ static void rxrpc_resend(struct rxrpc_call *call)
				stop = true;
				sp->resend_at = jiffies + 3;
			} else {
				if (rxrpc_is_client_call(call))
					rxrpc_expose_client_call(call);
				sp->resend_at =
					jiffies + rxrpc_resend_timeout;
			}
@@ -378,7 +380,7 @@ static void rxrpc_rotate_tx_window(struct rxrpc_call *call, u32 hard)
		call->acks_hard++;
	}

	wake_up(&call->tx_waitq);
	wake_up(&call->waitq);
}

/*
+3 −2
Original line number Diff line number Diff line
@@ -127,10 +127,11 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp)
	INIT_WORK(&call->destroyer, &rxrpc_destroy_call);
	INIT_WORK(&call->processor, &rxrpc_process_call);
	INIT_LIST_HEAD(&call->link);
	INIT_LIST_HEAD(&call->chan_wait_link);
	INIT_LIST_HEAD(&call->accept_link);
	skb_queue_head_init(&call->rx_queue);
	skb_queue_head_init(&call->rx_oos_queue);
	init_waitqueue_head(&call->tx_waitq);
	init_waitqueue_head(&call->waitq);
	spin_lock_init(&call->lock);
	rwlock_init(&call->state_lock);
	atomic_set(&call->usage, 1);
@@ -358,7 +359,7 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
		       call->debug_id, rxrpc_call_states[call->state]);

		if (call->state >= RXRPC_CALL_COMPLETE) {
			__rxrpc_disconnect_call(call);
			__rxrpc_disconnect_call(conn, call);
		} else {
			spin_unlock(&conn->channel_lock);
			kmem_cache_free(rxrpc_call_jar, candidate);
+796 −115

File changed.

Preview size limit exceeded, changes collapsed.

+51 −21
Original line number Diff line number Diff line
/* RxRPC virtual connection handler
/* RxRPC virtual connection handler, common bits.
 *
 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
@@ -15,8 +15,6 @@
#include <linux/slab.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include "ar-internal.h"

/*
@@ -27,9 +25,12 @@ unsigned int rxrpc_connection_expiry = 10 * 60;
static void rxrpc_connection_reaper(struct work_struct *work);

LIST_HEAD(rxrpc_connections);
LIST_HEAD(rxrpc_connection_proc_list);
DEFINE_RWLOCK(rxrpc_connection_lock);
static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper);

static void rxrpc_destroy_connection(struct rcu_head *);

/*
 * allocate a new connection
 */
@@ -41,19 +42,16 @@ struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp)

	conn = kzalloc(sizeof(struct rxrpc_connection), gfp);
	if (conn) {
		INIT_LIST_HEAD(&conn->cache_link);
		spin_lock_init(&conn->channel_lock);
		init_waitqueue_head(&conn->channel_wq);
		INIT_LIST_HEAD(&conn->waiting_calls);
		INIT_WORK(&conn->processor, &rxrpc_process_connection);
		INIT_LIST_HEAD(&conn->proc_link);
		INIT_LIST_HEAD(&conn->link);
		skb_queue_head_init(&conn->rx_queue);
		conn->security = &rxrpc_no_security;
		spin_lock_init(&conn->state_lock);
		/* We maintain an extra ref on the connection whilst it is
		 * on the rxrpc_connections list.
		 */
		atomic_set(&conn->usage, 2);
		conn->debug_id = atomic_inc_return(&rxrpc_debug_id);
		atomic_set(&conn->avail_chans, RXRPC_MAXCALLS);
		conn->size_align = 4;
		conn->header_size = sizeof(struct rxrpc_wire_header);
		conn->idle_timestamp = jiffies;
@@ -154,9 +152,9 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local,
 * terminates.  The caller must hold the channel_lock and must release the
 * call's ref on the connection.
 */
void __rxrpc_disconnect_call(struct rxrpc_call *call)
void __rxrpc_disconnect_call(struct rxrpc_connection *conn,
			     struct rxrpc_call *call)
{
	struct rxrpc_connection *conn = call->conn;
	struct rxrpc_channel *chan =
		&conn->channels[call->cid & RXRPC_CHANNELMASK];

@@ -180,8 +178,6 @@ void __rxrpc_disconnect_call(struct rxrpc_call *call)
		chan->call_id = chan->call_counter;

		rcu_assign_pointer(chan->call, NULL);
		atomic_inc(&conn->avail_chans);
		wake_up(&conn->channel_wq);
	}

	_leave("");
@@ -195,8 +191,11 @@ void rxrpc_disconnect_call(struct rxrpc_call *call)
{
	struct rxrpc_connection *conn = call->conn;

	if (rxrpc_is_client_call(call))
		return rxrpc_disconnect_client_call(call);

	spin_lock(&conn->channel_lock);
	__rxrpc_disconnect_call(call);
	__rxrpc_disconnect_call(conn, call);
	spin_unlock(&conn->channel_lock);

	call->conn = NULL;
@@ -204,6 +203,34 @@ void rxrpc_disconnect_call(struct rxrpc_call *call)
	rxrpc_put_connection(conn);
}

/*
 * Kill off a connection.
 */
void rxrpc_kill_connection(struct rxrpc_connection *conn)
{
	ASSERT(!rcu_access_pointer(conn->channels[0].call) &&
	       !rcu_access_pointer(conn->channels[1].call) &&
	       !rcu_access_pointer(conn->channels[2].call) &&
	       !rcu_access_pointer(conn->channels[3].call));
	ASSERT(list_empty(&conn->cache_link));

	write_lock(&rxrpc_connection_lock);
	list_del_init(&conn->proc_link);
	write_unlock(&rxrpc_connection_lock);

	/* Drain the Rx queue.  Note that even though we've unpublished, an
	 * incoming packet could still be being added to our Rx queue, so we
	 * will need to drain it again in the RCU cleanup handler.
	 */
	rxrpc_purge_queue(&conn->rx_queue);

	/* Leave final destruction to RCU.  The connection processor work item
	 * must carry a ref on the connection to prevent us getting here whilst
	 * it is queued or running.
	 */
	call_rcu(&conn->rcu, rxrpc_destroy_connection);
}

/*
 * release a virtual connection
 */
@@ -239,7 +266,7 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu)
}

/*
 * reap dead connections
 * reap dead service connections
 */
static void rxrpc_connection_reaper(struct work_struct *work)
{
@@ -278,7 +305,7 @@ static void rxrpc_connection_reaper(struct work_struct *work)
			continue;

		if (rxrpc_conn_is_client(conn))
			rxrpc_unpublish_client_conn(conn);
			BUG();
		else
			rxrpc_unpublish_service_conn(conn);

@@ -299,16 +326,15 @@ static void rxrpc_connection_reaper(struct work_struct *work)
		list_del_init(&conn->link);

		ASSERTCMP(atomic_read(&conn->usage), ==, 0);
		skb_queue_purge(&conn->rx_queue);
		call_rcu(&conn->rcu, rxrpc_destroy_connection);
		rxrpc_kill_connection(conn);
	}

	_leave("");
}

/*
 * preemptively destroy all the connection records rather than waiting for them
 * to time out
 * preemptively destroy all the service connection records rather than
 * waiting for them to time out
 */
void __exit rxrpc_destroy_all_connections(void)
{
@@ -317,6 +343,8 @@ void __exit rxrpc_destroy_all_connections(void)

	_enter("");

	rxrpc_destroy_all_client_connections();

	rxrpc_connection_expiry = 0;
	cancel_delayed_work(&rxrpc_connection_reap);
	rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0);
@@ -331,6 +359,8 @@ void __exit rxrpc_destroy_all_connections(void)
	write_unlock(&rxrpc_connection_lock);
	BUG_ON(leak);

	ASSERT(list_empty(&rxrpc_connection_proc_list));

	/* Make sure the local and peer records pinned by any dying connections
	 * are released.
	 */
Loading