rxrpc: Allow list of in-use local UDP endpoints to be viewed in /proc
[ Upstream commit 33912c2639ad76660988c8ca97e4d18fca89b668 ] 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> Stable-dep-of: 3bcd6c7eaa53 ("rxrpc: Fix race between conn bundle lookup and bundle removal [ZDI-CAN-15975]") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
a2d5dba2fc
commit
bddde342c6
@@ -86,7 +86,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);
|
||||||
@@ -266,7 +266,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 */
|
||||||
@@ -1001,6 +1001,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
|
||||||
|
@@ -81,7 +81,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);
|
||||||
@@ -199,7 +199,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;
|
||||||
@@ -209,16 +209,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 -
|
||||||
@@ -230,9 +226,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;
|
||||||
@@ -249,10 +246,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:
|
||||||
@@ -393,7 +392,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);
|
||||||
@@ -480,9 +479,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));
|
||||||
}
|
}
|
||||||
|
@@ -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:
|
||||||
|
@@ -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,
|
||||||
|
};
|
||||||
|
Reference in New Issue
Block a user