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: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David Howells 2022-05-21 08:45:15 +01:00 committed by David S. Miller
parent 0598cec957
commit 33912c2639
4 changed files with 94 additions and 22 deletions

View File

@ -88,7 +88,7 @@ struct rxrpc_net {
struct work_struct client_conn_reaper; struct work_struct client_conn_reaper;
struct timer_list client_conn_reap_timer; 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 */ struct mutex local_mutex; /* Lock for ->local_endpoints */
DECLARE_HASHTABLE (peer_hash, 10); 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 active_users; /* Number of users of the local endpoint */
atomic_t usage; /* Number of references to the structure */ atomic_t usage; /* Number of references to the structure */
struct rxrpc_net *rxnet; /* The network ns in which this resides */ 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 socket *socket; /* my UDP socket */
struct work_struct processor; struct work_struct processor;
struct rxrpc_sock __rcu *service; /* Service(s) listening on this endpoint */ 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_call_seq_ops;
extern const struct seq_operations rxrpc_connection_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_peer_seq_ops;
extern const struct seq_operations rxrpc_local_seq_ops;
/* /*
* recvmsg.c * recvmsg.c

View File

@ -82,7 +82,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet,
atomic_set(&local->usage, 1); atomic_set(&local->usage, 1);
atomic_set(&local->active_users, 1); atomic_set(&local->active_users, 1);
local->rxnet = rxnet; local->rxnet = rxnet;
INIT_LIST_HEAD(&local->link); INIT_HLIST_NODE(&local->link);
INIT_WORK(&local->processor, rxrpc_local_processor); INIT_WORK(&local->processor, rxrpc_local_processor);
init_rwsem(&local->defrag_sem); init_rwsem(&local->defrag_sem);
skb_queue_head_init(&local->reject_queue); 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_local *local;
struct rxrpc_net *rxnet = rxrpc_net(net); struct rxrpc_net *rxnet = rxrpc_net(net);
struct list_head *cursor; struct hlist_node *cursor;
const char *age; const char *age;
long diff; long diff;
int ret; int ret;
@ -190,16 +190,12 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
mutex_lock(&rxnet->local_mutex); mutex_lock(&rxnet->local_mutex);
for (cursor = rxnet->local_endpoints.next; hlist_for_each(cursor, &rxnet->local_endpoints) {
cursor != &rxnet->local_endpoints; local = hlist_entry(cursor, struct rxrpc_local, link);
cursor = cursor->next) {
local = list_entry(cursor, struct rxrpc_local, link);
diff = rxrpc_local_cmp_key(local, srx); diff = rxrpc_local_cmp_key(local, srx);
if (diff < 0) if (diff != 0)
continue; continue;
if (diff > 0)
break;
/* Services aren't allowed to share transport sockets, so /* Services aren't allowed to share transport sockets, so
* reject that here. It is possible that the object is dying - * 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; goto addr_in_use;
} }
/* Found a match. We replace a dying object. Attempting to /* Found a match. We want to replace a dying object.
* bind the transport socket may still fail if we're attempting * Attempting to bind the transport socket may still fail if
* to use a local address that the dying object is still using. * we're attempting to use a local address that the dying
* object is still using.
*/ */
if (!rxrpc_use_local(local)) if (!rxrpc_use_local(local))
break; break;
@ -230,10 +227,12 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
if (ret < 0) if (ret < 0)
goto sock_error; goto sock_error;
if (cursor != &rxnet->local_endpoints) if (cursor) {
list_replace_init(cursor, &local->link); hlist_replace_rcu(cursor, &local->link);
else cursor->pprev = NULL;
list_add_tail(&local->link, cursor); } else {
hlist_add_head_rcu(&local->link, &rxnet->local_endpoints);
}
age = "new"; age = "new";
found: found:
@ -374,7 +373,7 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local)
local->dead = true; local->dead = true;
mutex_lock(&rxnet->local_mutex); mutex_lock(&rxnet->local_mutex);
list_del_init(&local->link); hlist_del_init_rcu(&local->link);
mutex_unlock(&rxnet->local_mutex); mutex_unlock(&rxnet->local_mutex);
rxrpc_clean_up_local_conns(local); rxrpc_clean_up_local_conns(local);
@ -458,9 +457,9 @@ void rxrpc_destroy_all_locals(struct rxrpc_net *rxnet)
flush_workqueue(rxrpc_workqueue); flush_workqueue(rxrpc_workqueue);
if (!list_empty(&rxnet->local_endpoints)) { if (!hlist_empty(&rxnet->local_endpoints)) {
mutex_lock(&rxnet->local_mutex); 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", pr_err("AF_RXRPC: Leaked local %p {%d}\n",
local, atomic_read(&local->usage)); local, atomic_read(&local->usage));
} }

View File

@ -72,7 +72,7 @@ static __net_init int rxrpc_init_net(struct net *net)
timer_setup(&rxnet->client_conn_reap_timer, timer_setup(&rxnet->client_conn_reap_timer,
rxrpc_client_conn_reap_timeout, 0); rxrpc_client_conn_reap_timeout, 0);
INIT_LIST_HEAD(&rxnet->local_endpoints); INIT_HLIST_HEAD(&rxnet->local_endpoints);
mutex_init(&rxnet->local_mutex); mutex_init(&rxnet->local_mutex);
hash_init(rxnet->peer_hash); 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, proc_create_net("peers", 0444, rxnet->proc_net,
&rxrpc_peer_seq_ops, &rxrpc_peer_seq_ops,
sizeof(struct seq_net_private)); sizeof(struct seq_net_private));
proc_create_net("locals", 0444, rxnet->proc_net,
&rxrpc_local_seq_ops,
sizeof(struct seq_net_private));
return 0; return 0;
err_proc: err_proc:

View File

@ -334,3 +334,72 @@ const struct seq_operations rxrpc_peer_seq_ops = {
.stop = rxrpc_peer_seq_stop, .stop = rxrpc_peer_seq_stop,
.show = rxrpc_peer_seq_show, .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,
};