Commit 56bbc22d authored by David S. Miller's avatar David S. Miller
Browse files


David Howells says:

====================
rxrpc: Allow more calls to same peer

Here are some development patches for AF_RXRPC that allow more simultaneous
calls to be made to the same peer with the same security parameters.  The
current code allows a maximum of 4 simultaneous calls, which limits the afs
filesystem to that many simultaneous threads.  This increases the limit to
16.

To make this work, the way client connections are limited has to be changed
(incoming call/connection limits are unaffected) as the current code
depends on queuing calls on a connection and then pushing the connection
through a queue.  The limit is on the number of available connections.

This is changed such that there's a limit[*] on the total number of calls
systemwide across all namespaces, but the limit on the number of client
connections is removed.

Once a call is allowed to proceed, it finds a bundle of connections and
tries to grab a call slot.  If there's a spare call slot, fine, otherwise
it will wait.  If there's already a waiter, it will try to create another
connection in the bundle, unless the limit of 4 is reached (4 calls per
connection, giving 16).

A number of things throttle someone trying to set up endless connections:

 - Calls that fail immediately have their conns deleted immediately,

 - Calls that don't fail immediately have to wait for a timeout,

 - Connections normally get automatically reaped if they haven't been used
   for 2m, but this is sped up to 2s if the number of connections rises
   over 900.  This number is tunable by sysctl.

[*] Technically two limits - kernel sockets and userspace rxrpc sockets are
    accounted separately.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents cdd84a93 288827d5
Loading
Loading
Loading
Loading
+4 −29
Original line number Diff line number Diff line
@@ -68,21 +68,14 @@ enum rxrpc_client_trace {
	rxrpc_client_chan_activate,
	rxrpc_client_chan_disconnect,
	rxrpc_client_chan_pass,
	rxrpc_client_chan_unstarted,
	rxrpc_client_chan_wait_failed,
	rxrpc_client_cleanup,
	rxrpc_client_count,
	rxrpc_client_discard,
	rxrpc_client_duplicate,
	rxrpc_client_exposed,
	rxrpc_client_replace,
	rxrpc_client_to_active,
	rxrpc_client_to_culled,
	rxrpc_client_to_idle,
	rxrpc_client_to_inactive,
	rxrpc_client_to_upgrade,
	rxrpc_client_to_waiting,
	rxrpc_client_uncount,
};

enum rxrpc_call_trace {
@@ -271,29 +264,14 @@ enum rxrpc_tx_point {
	EM(rxrpc_client_chan_activate,		"ChActv") \
	EM(rxrpc_client_chan_disconnect,	"ChDisc") \
	EM(rxrpc_client_chan_pass,		"ChPass") \
	EM(rxrpc_client_chan_unstarted,		"ChUnst") \
	EM(rxrpc_client_chan_wait_failed,	"ChWtFl") \
	EM(rxrpc_client_cleanup,		"Clean ") \
	EM(rxrpc_client_count,			"Count ") \
	EM(rxrpc_client_discard,		"Discar") \
	EM(rxrpc_client_duplicate,		"Duplic") \
	EM(rxrpc_client_exposed,		"Expose") \
	EM(rxrpc_client_replace,		"Replac") \
	EM(rxrpc_client_to_active,		"->Actv") \
	EM(rxrpc_client_to_culled,		"->Cull") \
	EM(rxrpc_client_to_idle,		"->Idle") \
	EM(rxrpc_client_to_inactive,		"->Inac") \
	EM(rxrpc_client_to_upgrade,		"->Upgd") \
	EM(rxrpc_client_to_waiting,		"->Wait") \
	E_(rxrpc_client_uncount,		"Uncoun")

#define rxrpc_conn_cache_states \
	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") \
	E_(rxrpc_client_to_idle,		"->Idle")

#define rxrpc_call_traces \
	EM(rxrpc_call_connected,		"CON") \
@@ -594,23 +572,20 @@ TRACE_EVENT(rxrpc_client,
		    __field(int,			channel		)
		    __field(int,			usage		)
		    __field(enum rxrpc_client_trace,	op		)
		    __field(enum rxrpc_conn_cache_state, cs		)
			     ),

	    TP_fast_assign(
		    __entry->conn = conn->debug_id;
		    __entry->conn = conn ? conn->debug_id : 0;
		    __entry->channel = channel;
		    __entry->usage = atomic_read(&conn->usage);
		    __entry->usage = conn ? atomic_read(&conn->usage) : -2;
		    __entry->op = op;
		    __entry->cid = conn->proto.cid;
		    __entry->cs = conn->cache_state;
			   ),

	    TP_printk("C=%08x h=%2d %s %s i=%08x u=%d",
	    TP_printk("C=%08x h=%2d %s i=%08x u=%d",
		      __entry->conn,
		      __entry->channel,
		      __print_symbolic(__entry->op, rxrpc_client_traces),
		      __print_symbolic(__entry->cs, rxrpc_conn_cache_states),
		      __entry->cid,
		      __entry->usage)
	    );
+4 −3
Original line number Diff line number Diff line
@@ -311,6 +311,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
	p.user_call_ID		= user_call_ID;
	p.tx_total_len		= tx_total_len;
	p.interruptibility	= interruptibility;
	p.kernel		= true;

	memset(&cp, 0, sizeof(cp));
	cp.local		= rx->local;
+34 −36
Original line number Diff line number Diff line
@@ -76,14 +76,12 @@ struct rxrpc_net {
	struct work_struct	service_conn_reaper;
	struct timer_list	service_conn_reap_timer;

	unsigned int		nr_client_conns;
	unsigned int		nr_active_client_conns;
	bool			kill_all_client_conns;
	bool			live;

	bool			kill_all_client_conns;
	atomic_t		nr_client_conns;
	spinlock_t		client_conn_cache_lock; /* Lock for ->*_client_conns */
	spinlock_t		client_conn_discard_lock; /* Prevent multiple discarders */
	struct list_head	waiting_client_conns;
	struct list_head	active_client_conns;
	struct list_head	idle_client_conns;
	struct work_struct	client_conn_reaper;
	struct timer_list	client_conn_reap_timer;
@@ -275,8 +273,8 @@ struct rxrpc_local {
	struct rw_semaphore	defrag_sem;	/* control re-enablement of IP DF bit */
	struct sk_buff_head	reject_queue;	/* packets awaiting rejection */
	struct sk_buff_head	event_queue;	/* endpoint event packets awaiting processing */
	struct rb_root		client_conns;	/* Client connections by socket params */
	spinlock_t		client_conns_lock; /* Lock for client_conns */
	struct rb_root		client_bundles;	/* Client connection bundles by socket params */
	spinlock_t		client_bundles_lock; /* Lock for client_bundles */
	spinlock_t		lock;		/* access lock */
	rwlock_t		services_lock;	/* lock for services list */
	int			debug_id;	/* debug ID for printks */
@@ -353,10 +351,7 @@ struct rxrpc_conn_parameters {
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 */
	RXRPC_CONN_PROBING_FOR_UPGRADE,	/* Probing for service upgrade */
	RXRPC_CONN_FINAL_ACK_0,		/* Need final ACK for channel 0 */
	RXRPC_CONN_FINAL_ACK_1,		/* Need final ACK for channel 1 */
@@ -376,19 +371,6 @@ 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_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
};

/*
 * The connection protocol state.
 */
@@ -404,6 +386,23 @@ enum rxrpc_conn_proto_state {
	RXRPC_CONN__NR_STATES
};

/*
 * RxRPC client connection bundle.
 */
struct rxrpc_bundle {
	struct rxrpc_conn_parameters params;
	atomic_t		usage;
	unsigned int		debug_id;
	bool			try_upgrade;	/* True if the bundle is attempting upgrade */
	bool			alloc_conn;	/* True if someone's getting a conn */
	unsigned short		alloc_error;	/* Error from last conn allocation */
	spinlock_t		channel_lock;
	struct rb_node		local_node;	/* Node in local->client_conns */
	struct list_head	waiting_calls;	/* Calls waiting for channels */
	unsigned long		avail_chans;	/* Mask of available channels */
	struct rxrpc_connection	*conns[4];	/* The connections in the bundle (max 4) */
};

/*
 * RxRPC connection definition
 * - matched by { local, peer, epoch, conn_id, direction }
@@ -417,10 +416,7 @@ struct rxrpc_connection {
	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 */
	unsigned char		act_chans;	/* Mask of active channels */
	struct rxrpc_channel {
		unsigned long		final_ack_at;	/* Time at which to issue final ACK */
		struct rxrpc_call __rcu	*call;		/* Active call */
@@ -437,10 +433,8 @@ struct rxrpc_connection {

	struct timer_list	timer;		/* Conn event timer */
	struct work_struct	processor;	/* connection event processor */
	union {
		struct rb_node	client_node;	/* Node in local->client_conns */
	struct rxrpc_bundle	*bundle;	/* Client connection bundle */
	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 */
@@ -452,7 +446,6 @@ struct rxrpc_connection {
	unsigned long		events;
	unsigned long		idle_timestamp;	/* Time at which last became idle */
	spinlock_t		state_lock;	/* state-change lock */
	enum rxrpc_conn_cache_state cache_state;
	enum rxrpc_conn_proto_state state;	/* current state of connection */
	u32			abort_code;	/* Abort code of connection abort */
	int			debug_id;	/* debug ID for printks */
@@ -464,6 +457,7 @@ struct rxrpc_connection {
	u8			security_size;	/* security header size */
	u8			security_ix;	/* security type */
	u8			out_clientflag;	/* RXRPC_CLIENT_INITIATED if we are client */
	u8			bundle_shift;	/* Index into bundle->avail_chans */
	short			error;		/* Local error code */
};

@@ -493,6 +487,8 @@ enum rxrpc_call_flag {
	RXRPC_CALL_RX_HEARD,		/* The peer responded at least once to this call */
	RXRPC_CALL_RX_UNDERRUN,		/* Got data underrun */
	RXRPC_CALL_DISCONNECTED,	/* The call has been disconnected */
	RXRPC_CALL_KERNEL,		/* The call was made by the kernel */
	RXRPC_CALL_UPGRADE,		/* Service upgrade was requested for the call */
};

/*
@@ -577,7 +573,7 @@ struct rxrpc_call {
	struct work_struct	processor;	/* Event processor */
	rxrpc_notify_rx_t	notify_rx;	/* kernel service Rx notification function */
	struct list_head	link;		/* link in master call list */
	struct list_head	chan_wait_link;	/* Link in conn->waiting_calls */
	struct list_head	chan_wait_link;	/* Link in conn->bundle->waiting_calls */
	struct hlist_node	error_link;	/* link in error distribution list */
	struct list_head	accept_link;	/* Link in rx->acceptq */
	struct list_head	recvmsg_link;	/* Link in rx->recvmsg_q */
@@ -727,6 +723,7 @@ struct rxrpc_call_params {
		u32		normal;		/* Max time since last call packet (msec) */
	} timeouts;
	u8			nr_timeouts;	/* Number of timeouts specified */
	bool			kernel;		/* T if kernel is making the call */
	enum rxrpc_interruptibility interruptibility; /* How is interruptible is the call? */
};

@@ -815,18 +812,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 long rxrpc_conn_idle_client_expiry;
extern unsigned long rxrpc_conn_idle_client_fast_expiry;
extern struct idr rxrpc_client_conn_ids;

void rxrpc_destroy_client_conn_ids(void);
struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *);
void rxrpc_put_bundle(struct rxrpc_bundle *);
int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_call *,
		       struct rxrpc_conn_parameters *, struct sockaddr_rxrpc *,
		       gfp_t);
void rxrpc_expose_client_call(struct rxrpc_call *);
void rxrpc_disconnect_client_call(struct rxrpc_call *);
void rxrpc_disconnect_client_call(struct rxrpc_bundle *, struct rxrpc_call *);
void rxrpc_put_client_conn(struct rxrpc_connection *);
void rxrpc_discard_expired_client_conns(struct work_struct *);
void rxrpc_destroy_all_client_connections(struct rxrpc_net *);
@@ -852,7 +850,7 @@ void rxrpc_disconnect_call(struct rxrpc_call *);
void rxrpc_kill_connection(struct rxrpc_connection *);
bool rxrpc_queue_conn(struct rxrpc_connection *);
void rxrpc_see_connection(struct rxrpc_connection *);
void rxrpc_get_connection(struct rxrpc_connection *);
struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *);
struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *);
void rxrpc_put_service_conn(struct rxrpc_connection *);
void rxrpc_service_connection_reaper(struct work_struct *);
+43 −0
Original line number Diff line number Diff line
@@ -41,6 +41,11 @@ const char *const rxrpc_call_completions[NR__RXRPC_CALL_COMPLETIONS] = {

struct kmem_cache *rxrpc_call_jar;

static struct semaphore rxrpc_call_limiter =
	__SEMAPHORE_INITIALIZER(rxrpc_call_limiter, 1000);
static struct semaphore rxrpc_kernel_call_limiter =
	__SEMAPHORE_INITIALIZER(rxrpc_kernel_call_limiter, 1000);

static void rxrpc_call_timer_expired(struct timer_list *t)
{
	struct rxrpc_call *call = from_timer(call, t, timer);
@@ -209,6 +214,34 @@ static void rxrpc_start_call_timer(struct rxrpc_call *call)
	call->timer.expires = now;
}

/*
 * Wait for a call slot to become available.
 */
static struct semaphore *rxrpc_get_call_slot(struct rxrpc_call_params *p, gfp_t gfp)
{
	struct semaphore *limiter = &rxrpc_call_limiter;

	if (p->kernel)
		limiter = &rxrpc_kernel_call_limiter;
	if (p->interruptibility == RXRPC_UNINTERRUPTIBLE) {
		down(limiter);
		return limiter;
	}
	return down_interruptible(limiter) < 0 ? NULL : limiter;
}

/*
 * Release a call slot.
 */
static void rxrpc_put_call_slot(struct rxrpc_call *call)
{
	struct semaphore *limiter = &rxrpc_call_limiter;

	if (test_bit(RXRPC_CALL_KERNEL, &call->flags))
		limiter = &rxrpc_kernel_call_limiter;
	up(limiter);
}

/*
 * Set up a call for the given parameters.
 * - Called with the socket lock held, which it must release.
@@ -225,15 +258,21 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
{
	struct rxrpc_call *call, *xcall;
	struct rxrpc_net *rxnet;
	struct semaphore *limiter;
	struct rb_node *parent, **pp;
	const void *here = __builtin_return_address(0);
	int ret;

	_enter("%p,%lx", rx, p->user_call_ID);

	limiter = rxrpc_get_call_slot(p, gfp);
	if (!limiter)
		return ERR_PTR(-ERESTARTSYS);

	call = rxrpc_alloc_client_call(rx, srx, gfp, debug_id);
	if (IS_ERR(call)) {
		release_sock(&rx->sk);
		up(limiter);
		_leave(" = %ld", PTR_ERR(call));
		return call;
	}
@@ -243,6 +282,8 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
	trace_rxrpc_call(call->debug_id, rxrpc_call_new_client,
			 atomic_read(&call->usage),
			 here, (const void *)p->user_call_ID);
	if (p->kernel)
		__set_bit(RXRPC_CALL_KERNEL, &call->flags);

	/* We need to protect a partially set up call against the user as we
	 * will be acting outside the socket lock.
@@ -471,6 +512,8 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
		BUG();
	spin_unlock_bh(&call->lock);

	rxrpc_put_call_slot(call);

	del_timer_sync(&call->timer);

	/* Make sure we don't get any more notifications */
+493 −594

File changed.

Preview size limit exceeded, changes collapsed.

Loading