Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

David Howells and committed by
David S. Miller
33912c26 0598cec9

+94 -22
+3 -2
net/rxrpc/ar-internal.h
··· 88 88 struct work_struct client_conn_reaper; 89 89 struct timer_list client_conn_reap_timer; 90 90 91 - struct list_head local_endpoints; 91 + struct hlist_head local_endpoints; 92 92 struct mutex local_mutex; /* Lock for ->local_endpoints */ 93 93 94 94 DECLARE_HASHTABLE (peer_hash, 10); ··· 281 281 atomic_t active_users; /* Number of users of the local endpoint */ 282 282 atomic_t usage; /* Number of references to the structure */ 283 283 struct rxrpc_net *rxnet; /* The network ns in which this resides */ 284 - struct list_head link; 284 + struct hlist_node link; 285 285 struct socket *socket; /* my UDP socket */ 286 286 struct work_struct processor; 287 287 struct rxrpc_sock __rcu *service; /* Service(s) listening on this endpoint */ ··· 1014 1014 extern const struct seq_operations rxrpc_call_seq_ops; 1015 1015 extern const struct seq_operations rxrpc_connection_seq_ops; 1016 1016 extern const struct seq_operations rxrpc_peer_seq_ops; 1017 + extern const struct seq_operations rxrpc_local_seq_ops; 1017 1018 1018 1019 /* 1019 1020 * recvmsg.c
+18 -19
net/rxrpc/local_object.c
··· 82 82 atomic_set(&local->usage, 1); 83 83 atomic_set(&local->active_users, 1); 84 84 local->rxnet = rxnet; 85 - INIT_LIST_HEAD(&local->link); 85 + INIT_HLIST_NODE(&local->link); 86 86 INIT_WORK(&local->processor, rxrpc_local_processor); 87 87 init_rwsem(&local->defrag_sem); 88 88 skb_queue_head_init(&local->reject_queue); ··· 180 180 { 181 181 struct rxrpc_local *local; 182 182 struct rxrpc_net *rxnet = rxrpc_net(net); 183 - struct list_head *cursor; 183 + struct hlist_node *cursor; 184 184 const char *age; 185 185 long diff; 186 186 int ret; ··· 190 190 191 191 mutex_lock(&rxnet->local_mutex); 192 192 193 - for (cursor = rxnet->local_endpoints.next; 194 - cursor != &rxnet->local_endpoints; 195 - cursor = cursor->next) { 196 - local = list_entry(cursor, struct rxrpc_local, link); 193 + hlist_for_each(cursor, &rxnet->local_endpoints) { 194 + local = hlist_entry(cursor, struct rxrpc_local, link); 197 195 198 196 diff = rxrpc_local_cmp_key(local, srx); 199 - if (diff < 0) 197 + if (diff != 0) 200 198 continue; 201 - if (diff > 0) 202 - break; 203 199 204 200 /* Services aren't allowed to share transport sockets, so 205 201 * reject that here. It is possible that the object is dying - ··· 207 211 goto addr_in_use; 208 212 } 209 213 210 - /* Found a match. We replace a dying object. Attempting to 211 - * bind the transport socket may still fail if we're attempting 212 - * to use a local address that the dying object is still using. 214 + /* Found a match. We want to replace a dying object. 215 + * Attempting to bind the transport socket may still fail if 216 + * we're attempting to use a local address that the dying 217 + * object is still using. 213 218 */ 214 219 if (!rxrpc_use_local(local)) 215 220 break; ··· 227 230 if (ret < 0) 228 231 goto sock_error; 229 232 230 - if (cursor != &rxnet->local_endpoints) 231 - list_replace_init(cursor, &local->link); 232 - else 233 - list_add_tail(&local->link, cursor); 233 + if (cursor) { 234 + hlist_replace_rcu(cursor, &local->link); 235 + cursor->pprev = NULL; 236 + } else { 237 + hlist_add_head_rcu(&local->link, &rxnet->local_endpoints); 238 + } 234 239 age = "new"; 235 240 236 241 found: ··· 373 374 local->dead = true; 374 375 375 376 mutex_lock(&rxnet->local_mutex); 376 - list_del_init(&local->link); 377 + hlist_del_init_rcu(&local->link); 377 378 mutex_unlock(&rxnet->local_mutex); 378 379 379 380 rxrpc_clean_up_local_conns(local); ··· 457 458 458 459 flush_workqueue(rxrpc_workqueue); 459 460 460 - if (!list_empty(&rxnet->local_endpoints)) { 461 + if (!hlist_empty(&rxnet->local_endpoints)) { 461 462 mutex_lock(&rxnet->local_mutex); 462 - list_for_each_entry(local, &rxnet->local_endpoints, link) { 463 + hlist_for_each_entry(local, &rxnet->local_endpoints, link) { 463 464 pr_err("AF_RXRPC: Leaked local %p {%d}\n", 464 465 local, atomic_read(&local->usage)); 465 466 }
+4 -1
net/rxrpc/net_ns.c
··· 72 72 timer_setup(&rxnet->client_conn_reap_timer, 73 73 rxrpc_client_conn_reap_timeout, 0); 74 74 75 - INIT_LIST_HEAD(&rxnet->local_endpoints); 75 + INIT_HLIST_HEAD(&rxnet->local_endpoints); 76 76 mutex_init(&rxnet->local_mutex); 77 77 78 78 hash_init(rxnet->peer_hash); ··· 97 97 sizeof(struct seq_net_private)); 98 98 proc_create_net("peers", 0444, rxnet->proc_net, 99 99 &rxrpc_peer_seq_ops, 100 + sizeof(struct seq_net_private)); 101 + proc_create_net("locals", 0444, rxnet->proc_net, 102 + &rxrpc_local_seq_ops, 100 103 sizeof(struct seq_net_private)); 101 104 return 0; 102 105
+69
net/rxrpc/proc.c
··· 334 334 .stop = rxrpc_peer_seq_stop, 335 335 .show = rxrpc_peer_seq_show, 336 336 }; 337 + 338 + /* 339 + * Generate a list of extant virtual local endpoints in /proc/net/rxrpc/locals 340 + */ 341 + static int rxrpc_local_seq_show(struct seq_file *seq, void *v) 342 + { 343 + struct rxrpc_local *local; 344 + char lbuff[50]; 345 + 346 + if (v == SEQ_START_TOKEN) { 347 + seq_puts(seq, 348 + "Proto Local " 349 + " Use Act\n"); 350 + return 0; 351 + } 352 + 353 + local = hlist_entry(v, struct rxrpc_local, link); 354 + 355 + sprintf(lbuff, "%pISpc", &local->srx.transport); 356 + 357 + seq_printf(seq, 358 + "UDP %-47.47s %3u %3u\n", 359 + lbuff, 360 + atomic_read(&local->usage), 361 + atomic_read(&local->active_users)); 362 + 363 + return 0; 364 + } 365 + 366 + static void *rxrpc_local_seq_start(struct seq_file *seq, loff_t *_pos) 367 + __acquires(rcu) 368 + { 369 + struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); 370 + unsigned int n; 371 + 372 + rcu_read_lock(); 373 + 374 + if (*_pos >= UINT_MAX) 375 + return NULL; 376 + 377 + n = *_pos; 378 + if (n == 0) 379 + return SEQ_START_TOKEN; 380 + 381 + return seq_hlist_start_rcu(&rxnet->local_endpoints, n - 1); 382 + } 383 + 384 + static void *rxrpc_local_seq_next(struct seq_file *seq, void *v, loff_t *_pos) 385 + { 386 + struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); 387 + 388 + if (*_pos >= UINT_MAX) 389 + return NULL; 390 + 391 + return seq_hlist_next_rcu(v, &rxnet->local_endpoints, _pos); 392 + } 393 + 394 + static void rxrpc_local_seq_stop(struct seq_file *seq, void *v) 395 + __releases(rcu) 396 + { 397 + rcu_read_unlock(); 398 + } 399 + 400 + const struct seq_operations rxrpc_local_seq_ops = { 401 + .start = rxrpc_local_seq_start, 402 + .next = rxrpc_local_seq_next, 403 + .stop = rxrpc_local_seq_stop, 404 + .show = rxrpc_local_seq_show, 405 + };