Commit 33912c26 authored by David Howells's avatar David Howells Committed by David S. Miller
Browse files

rxrpc: Allow list of in-use local UDP endpoints to be viewed in /proc



Allow the list of in-use local UDP endpoints in the current network
namespace to be viewed in /proc.

To aid with this, the endpoint list is converted to an hlist and RCU-safe
manipulation is used so that the list can be read with only the RCU
read lock held.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0598cec9
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ struct rxrpc_net {
	struct work_struct	client_conn_reaper;
	struct timer_list	client_conn_reap_timer;

	struct list_head	local_endpoints;
	struct hlist_head	local_endpoints;
	struct mutex		local_mutex;	/* Lock for ->local_endpoints */

	DECLARE_HASHTABLE	(peer_hash, 10);
@@ -281,7 +281,7 @@ struct rxrpc_local {
	atomic_t		active_users;	/* Number of users of the local endpoint */
	atomic_t		usage;		/* Number of references to the structure */
	struct rxrpc_net	*rxnet;		/* The network ns in which this resides */
	struct list_head	link;
	struct hlist_node	link;
	struct socket		*socket;	/* my UDP socket */
	struct work_struct	processor;
	struct rxrpc_sock __rcu	*service;	/* Service(s) listening on this endpoint */
@@ -1014,6 +1014,7 @@ void rxrpc_put_peer_locked(struct rxrpc_peer *);
extern const struct seq_operations rxrpc_call_seq_ops;
extern const struct seq_operations rxrpc_connection_seq_ops;
extern const struct seq_operations rxrpc_peer_seq_ops;
extern const struct seq_operations rxrpc_local_seq_ops;

/*
 * recvmsg.c
+18 −19
Original line number Diff line number Diff line
@@ -82,7 +82,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet,
		atomic_set(&local->usage, 1);
		atomic_set(&local->active_users, 1);
		local->rxnet = rxnet;
		INIT_LIST_HEAD(&local->link);
		INIT_HLIST_NODE(&local->link);
		INIT_WORK(&local->processor, rxrpc_local_processor);
		init_rwsem(&local->defrag_sem);
		skb_queue_head_init(&local->reject_queue);
@@ -180,7 +180,7 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
{
	struct rxrpc_local *local;
	struct rxrpc_net *rxnet = rxrpc_net(net);
	struct list_head *cursor;
	struct hlist_node *cursor;
	const char *age;
	long diff;
	int ret;
@@ -190,16 +190,12 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,

	mutex_lock(&rxnet->local_mutex);

	for (cursor = rxnet->local_endpoints.next;
	     cursor != &rxnet->local_endpoints;
	     cursor = cursor->next) {
		local = list_entry(cursor, struct rxrpc_local, link);
	hlist_for_each(cursor, &rxnet->local_endpoints) {
		local = hlist_entry(cursor, struct rxrpc_local, link);

		diff = rxrpc_local_cmp_key(local, srx);
		if (diff < 0)
		if (diff != 0)
			continue;
		if (diff > 0)
			break;

		/* Services aren't allowed to share transport sockets, so
		 * reject that here.  It is possible that the object is dying -
@@ -211,9 +207,10 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
			goto addr_in_use;
		}

		/* Found a match.  We replace a dying object.  Attempting to
		 * bind the transport socket may still fail if we're attempting
		 * to use a local address that the dying object is still using.
		/* Found a match.  We want to replace a dying object.
		 * Attempting to bind the transport socket may still fail if
		 * we're attempting to use a local address that the dying
		 * object is still using.
		 */
		if (!rxrpc_use_local(local))
			break;
@@ -230,10 +227,12 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
	if (ret < 0)
		goto sock_error;

	if (cursor != &rxnet->local_endpoints)
		list_replace_init(cursor, &local->link);
	else
		list_add_tail(&local->link, cursor);
	if (cursor) {
		hlist_replace_rcu(cursor, &local->link);
		cursor->pprev = NULL;
	} else {
		hlist_add_head_rcu(&local->link, &rxnet->local_endpoints);
	}
	age = "new";

found:
@@ -374,7 +373,7 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local)
	local->dead = true;

	mutex_lock(&rxnet->local_mutex);
	list_del_init(&local->link);
	hlist_del_init_rcu(&local->link);
	mutex_unlock(&rxnet->local_mutex);

	rxrpc_clean_up_local_conns(local);
@@ -458,9 +457,9 @@ void rxrpc_destroy_all_locals(struct rxrpc_net *rxnet)

	flush_workqueue(rxrpc_workqueue);

	if (!list_empty(&rxnet->local_endpoints)) {
	if (!hlist_empty(&rxnet->local_endpoints)) {
		mutex_lock(&rxnet->local_mutex);
		list_for_each_entry(local, &rxnet->local_endpoints, link) {
		hlist_for_each_entry(local, &rxnet->local_endpoints, link) {
			pr_err("AF_RXRPC: Leaked local %p {%d}\n",
			       local, atomic_read(&local->usage));
		}
+4 −1
Original line number Diff line number Diff line
@@ -72,7 +72,7 @@ static __net_init int rxrpc_init_net(struct net *net)
	timer_setup(&rxnet->client_conn_reap_timer,
		    rxrpc_client_conn_reap_timeout, 0);

	INIT_LIST_HEAD(&rxnet->local_endpoints);
	INIT_HLIST_HEAD(&rxnet->local_endpoints);
	mutex_init(&rxnet->local_mutex);

	hash_init(rxnet->peer_hash);
@@ -98,6 +98,9 @@ static __net_init int rxrpc_init_net(struct net *net)
	proc_create_net("peers", 0444, rxnet->proc_net,
			&rxrpc_peer_seq_ops,
			sizeof(struct seq_net_private));
	proc_create_net("locals", 0444, rxnet->proc_net,
			&rxrpc_local_seq_ops,
			sizeof(struct seq_net_private));
	return 0;

err_proc:
+69 −0
Original line number Diff line number Diff line
@@ -334,3 +334,72 @@ const struct seq_operations rxrpc_peer_seq_ops = {
	.stop   = rxrpc_peer_seq_stop,
	.show   = rxrpc_peer_seq_show,
};

/*
 * Generate a list of extant virtual local endpoints in /proc/net/rxrpc/locals
 */
static int rxrpc_local_seq_show(struct seq_file *seq, void *v)
{
	struct rxrpc_local *local;
	char lbuff[50];

	if (v == SEQ_START_TOKEN) {
		seq_puts(seq,
			 "Proto Local                                          "
			 " Use Act\n");
		return 0;
	}

	local = hlist_entry(v, struct rxrpc_local, link);

	sprintf(lbuff, "%pISpc", &local->srx.transport);

	seq_printf(seq,
		   "UDP   %-47.47s %3u %3u\n",
		   lbuff,
		   atomic_read(&local->usage),
		   atomic_read(&local->active_users));

	return 0;
}

static void *rxrpc_local_seq_start(struct seq_file *seq, loff_t *_pos)
	__acquires(rcu)
{
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
	unsigned int n;

	rcu_read_lock();

	if (*_pos >= UINT_MAX)
		return NULL;

	n = *_pos;
	if (n == 0)
		return SEQ_START_TOKEN;

	return seq_hlist_start_rcu(&rxnet->local_endpoints, n - 1);
}

static void *rxrpc_local_seq_next(struct seq_file *seq, void *v, loff_t *_pos)
{
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));

	if (*_pos >= UINT_MAX)
		return NULL;

	return seq_hlist_next_rcu(v, &rxnet->local_endpoints, _pos);
}

static void rxrpc_local_seq_stop(struct seq_file *seq, void *v)
	__releases(rcu)
{
	rcu_read_unlock();
}

const struct seq_operations rxrpc_local_seq_ops = {
	.start  = rxrpc_local_seq_start,
	.next   = rxrpc_local_seq_next,
	.stop   = rxrpc_local_seq_stop,
	.show   = rxrpc_local_seq_show,
};