diff options
-rw-r--r-- | net/rxrpc/ar-internal.h | 1 | ||||
-rw-r--r-- | net/rxrpc/net_ns.c | 3 | ||||
-rw-r--r-- | net/rxrpc/proc.c | 126 |
3 files changed, 130 insertions, 0 deletions
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 8cee7644965c..0a7c49e8e053 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h | |||
@@ -1062,6 +1062,7 @@ void rxrpc_put_peer(struct rxrpc_peer *); | |||
1062 | */ | 1062 | */ |
1063 | extern const struct seq_operations rxrpc_call_seq_ops; | 1063 | extern const struct seq_operations rxrpc_call_seq_ops; |
1064 | extern const struct seq_operations rxrpc_connection_seq_ops; | 1064 | extern const struct seq_operations rxrpc_connection_seq_ops; |
1065 | extern const struct seq_operations rxrpc_peer_seq_ops; | ||
1065 | 1066 | ||
1066 | /* | 1067 | /* |
1067 | * recvmsg.c | 1068 | * recvmsg.c |
diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c index 417d80867c4f..fd7eba8467fa 100644 --- a/net/rxrpc/net_ns.c +++ b/net/rxrpc/net_ns.c | |||
@@ -102,6 +102,9 @@ static __net_init int rxrpc_init_net(struct net *net) | |||
102 | proc_create_net("conns", 0444, rxnet->proc_net, | 102 | proc_create_net("conns", 0444, rxnet->proc_net, |
103 | &rxrpc_connection_seq_ops, | 103 | &rxrpc_connection_seq_ops, |
104 | sizeof(struct seq_net_private)); | 104 | sizeof(struct seq_net_private)); |
105 | proc_create_net("peers", 0444, rxnet->proc_net, | ||
106 | &rxrpc_peer_seq_ops, | ||
107 | sizeof(struct seq_net_private)); | ||
105 | return 0; | 108 | return 0; |
106 | 109 | ||
107 | err_proc: | 110 | err_proc: |
diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 9805e3b85c36..c7d976859d40 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c | |||
@@ -212,3 +212,129 @@ const struct seq_operations rxrpc_connection_seq_ops = { | |||
212 | .stop = rxrpc_connection_seq_stop, | 212 | .stop = rxrpc_connection_seq_stop, |
213 | .show = rxrpc_connection_seq_show, | 213 | .show = rxrpc_connection_seq_show, |
214 | }; | 214 | }; |
215 | |||
216 | /* | ||
217 | * generate a list of extant virtual peers in /proc/net/rxrpc/peers | ||
218 | */ | ||
219 | static int rxrpc_peer_seq_show(struct seq_file *seq, void *v) | ||
220 | { | ||
221 | struct rxrpc_peer *peer; | ||
222 | time64_t now; | ||
223 | char lbuff[50], rbuff[50]; | ||
224 | |||
225 | if (v == SEQ_START_TOKEN) { | ||
226 | seq_puts(seq, | ||
227 | "Proto Local " | ||
228 | " Remote " | ||
229 | " Use CW MTU LastUse RTT Rc\n" | ||
230 | ); | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | peer = list_entry(v, struct rxrpc_peer, hash_link); | ||
235 | |||
236 | sprintf(lbuff, "%pISpc", &peer->local->srx.transport); | ||
237 | |||
238 | sprintf(rbuff, "%pISpc", &peer->srx.transport); | ||
239 | |||
240 | now = ktime_get_seconds(); | ||
241 | seq_printf(seq, | ||
242 | "UDP %-47.47s %-47.47s %3u" | ||
243 | " %3u %5u %6llus %12llu %2u\n", | ||
244 | lbuff, | ||
245 | rbuff, | ||
246 | atomic_read(&peer->usage), | ||
247 | peer->cong_cwnd, | ||
248 | peer->mtu, | ||
249 | now - peer->last_tx_at, | ||
250 | peer->rtt, | ||
251 | peer->rtt_cursor); | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static void *rxrpc_peer_seq_start(struct seq_file *seq, loff_t *_pos) | ||
257 | __acquires(rcu) | ||
258 | { | ||
259 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); | ||
260 | unsigned int bucket, n; | ||
261 | unsigned int shift = 32 - HASH_BITS(rxnet->peer_hash); | ||
262 | void *p; | ||
263 | |||
264 | rcu_read_lock(); | ||
265 | |||
266 | if (*_pos >= UINT_MAX) | ||
267 | return NULL; | ||
268 | |||
269 | n = *_pos & ((1U << shift) - 1); | ||
270 | bucket = *_pos >> shift; | ||
271 | for (;;) { | ||
272 | if (bucket >= HASH_SIZE(rxnet->peer_hash)) { | ||
273 | *_pos = UINT_MAX; | ||
274 | return NULL; | ||
275 | } | ||
276 | if (n == 0) { | ||
277 | if (bucket == 0) | ||
278 | return SEQ_START_TOKEN; | ||
279 | *_pos += 1; | ||
280 | n++; | ||
281 | } | ||
282 | |||
283 | p = seq_hlist_start_rcu(&rxnet->peer_hash[bucket], n - 1); | ||
284 | if (p) | ||
285 | return p; | ||
286 | bucket++; | ||
287 | n = 1; | ||
288 | *_pos = (bucket << shift) | n; | ||
289 | } | ||
290 | } | ||
291 | |||
292 | static void *rxrpc_peer_seq_next(struct seq_file *seq, void *v, loff_t *_pos) | ||
293 | { | ||
294 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); | ||
295 | unsigned int bucket, n; | ||
296 | unsigned int shift = 32 - HASH_BITS(rxnet->peer_hash); | ||
297 | void *p; | ||
298 | |||
299 | if (*_pos >= UINT_MAX) | ||
300 | return NULL; | ||
301 | |||
302 | bucket = *_pos >> shift; | ||
303 | |||
304 | p = seq_hlist_next_rcu(v, &rxnet->peer_hash[bucket], _pos); | ||
305 | if (p) | ||
306 | return p; | ||
307 | |||
308 | for (;;) { | ||
309 | bucket++; | ||
310 | n = 1; | ||
311 | *_pos = (bucket << shift) | n; | ||
312 | |||
313 | if (bucket >= HASH_SIZE(rxnet->peer_hash)) { | ||
314 | *_pos = UINT_MAX; | ||
315 | return NULL; | ||
316 | } | ||
317 | if (n == 0) { | ||
318 | *_pos += 1; | ||
319 | n++; | ||
320 | } | ||
321 | |||
322 | p = seq_hlist_start_rcu(&rxnet->peer_hash[bucket], n - 1); | ||
323 | if (p) | ||
324 | return p; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | static void rxrpc_peer_seq_stop(struct seq_file *seq, void *v) | ||
329 | __releases(rcu) | ||
330 | { | ||
331 | rcu_read_unlock(); | ||
332 | } | ||
333 | |||
334 | |||
335 | const struct seq_operations rxrpc_peer_seq_ops = { | ||
336 | .start = rxrpc_peer_seq_start, | ||
337 | .next = rxrpc_peer_seq_next, | ||
338 | .stop = rxrpc_peer_seq_stop, | ||
339 | .show = rxrpc_peer_seq_show, | ||
340 | }; | ||