]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
17926a79 DH |
2 | /* /proc/net/ support for AF_RXRPC |
3 | * | |
4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | |
5 | * Written by David Howells (dhowells@redhat.com) | |
17926a79 DH |
6 | */ |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <net/sock.h> | |
10 | #include <net/af_rxrpc.h> | |
11 | #include "ar-internal.h" | |
12 | ||
bba304db DH |
13 | static const char *const rxrpc_conn_states[RXRPC_CONN__NR_STATES] = { |
14 | [RXRPC_CONN_UNUSED] = "Unused ", | |
15 | [RXRPC_CONN_CLIENT] = "Client ", | |
00e90712 | 16 | [RXRPC_CONN_SERVICE_PREALLOC] = "SvPrealc", |
bba304db DH |
17 | [RXRPC_CONN_SERVICE_UNSECURED] = "SvUnsec ", |
18 | [RXRPC_CONN_SERVICE_CHALLENGING] = "SvChall ", | |
19 | [RXRPC_CONN_SERVICE] = "SvSecure", | |
20 | [RXRPC_CONN_REMOTELY_ABORTED] = "RmtAbort", | |
21 | [RXRPC_CONN_LOCALLY_ABORTED] = "LocAbort", | |
17926a79 DH |
22 | }; |
23 | ||
17926a79 DH |
24 | /* |
25 | * generate a list of extant and dead calls in /proc/net/rxrpc_calls | |
26 | */ | |
27 | static void *rxrpc_call_seq_start(struct seq_file *seq, loff_t *_pos) | |
88f2a825 | 28 | __acquires(rcu) |
17926a79 | 29 | { |
2baec2c3 DH |
30 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); |
31 | ||
8d94aa38 | 32 | rcu_read_lock(); |
ad25f5cb | 33 | return seq_list_start_head_rcu(&rxnet->calls, *_pos); |
17926a79 DH |
34 | } |
35 | ||
36 | static void *rxrpc_call_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |
37 | { | |
2baec2c3 DH |
38 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); |
39 | ||
ad25f5cb | 40 | return seq_list_next_rcu(v, &rxnet->calls, pos); |
17926a79 DH |
41 | } |
42 | ||
43 | static void rxrpc_call_seq_stop(struct seq_file *seq, void *v) | |
88f2a825 | 44 | __releases(rcu) |
17926a79 | 45 | { |
8d94aa38 | 46 | rcu_read_unlock(); |
17926a79 DH |
47 | } |
48 | ||
49 | static int rxrpc_call_seq_show(struct seq_file *seq, void *v) | |
50 | { | |
df5d8bf7 DH |
51 | struct rxrpc_local *local; |
52 | struct rxrpc_sock *rx; | |
53 | struct rxrpc_peer *peer; | |
17926a79 | 54 | struct rxrpc_call *call; |
2baec2c3 | 55 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); |
770b26de | 56 | unsigned long timeout = 0; |
3e018daf | 57 | rxrpc_seq_t tx_hard_ack, rx_hard_ack; |
75b54cb5 | 58 | char lbuff[50], rbuff[50]; |
17926a79 | 59 | |
2baec2c3 | 60 | if (v == &rxnet->calls) { |
17926a79 | 61 | seq_puts(seq, |
75b54cb5 DH |
62 | "Proto Local " |
63 | " Remote " | |
17926a79 | 64 | " SvID ConnID CallID End Use State Abort " |
32f71aa4 | 65 | " DebugId TxSeq TW RxSeq RW RxSerial RxTimo\n"); |
17926a79 DH |
66 | return 0; |
67 | } | |
68 | ||
69 | call = list_entry(v, struct rxrpc_call, link); | |
17926a79 | 70 | |
8d94aa38 | 71 | rx = rcu_dereference(call->socket); |
df5d8bf7 DH |
72 | if (rx) { |
73 | local = READ_ONCE(rx->local); | |
74 | if (local) | |
75b54cb5 | 75 | sprintf(lbuff, "%pISpc", &local->srx.transport); |
df5d8bf7 DH |
76 | else |
77 | strcpy(lbuff, "no_local"); | |
78 | } else { | |
79 | strcpy(lbuff, "no_socket"); | |
80 | } | |
17926a79 | 81 | |
df5d8bf7 DH |
82 | peer = call->peer; |
83 | if (peer) | |
75b54cb5 | 84 | sprintf(rbuff, "%pISpc", &peer->srx.transport); |
f4e7da8c DH |
85 | else |
86 | strcpy(rbuff, "no_connection"); | |
17926a79 | 87 | |
887763bb DH |
88 | if (call->state != RXRPC_CALL_SERVER_PREALLOC) { |
89 | timeout = READ_ONCE(call->expect_rx_by); | |
887763bb DH |
90 | timeout -= jiffies; |
91 | } | |
92 | ||
3e018daf DH |
93 | tx_hard_ack = READ_ONCE(call->tx_hard_ack); |
94 | rx_hard_ack = READ_ONCE(call->rx_hard_ack); | |
17926a79 | 95 | seq_printf(seq, |
75b54cb5 | 96 | "UDP %-47.47s %-47.47s %4x %08x %08x %s %3u" |
32f71aa4 | 97 | " %-8.8s %08x %08x %08x %02x %08x %02x %08x %06lx\n", |
17926a79 DH |
98 | lbuff, |
99 | rbuff, | |
f4e7da8c | 100 | call->service_id, |
0d12f8a4 DH |
101 | call->cid, |
102 | call->call_id, | |
dabe5a79 | 103 | rxrpc_is_service_call(call) ? "Svc" : "Clt", |
a0575429 | 104 | refcount_read(&call->ref), |
17926a79 | 105 | rxrpc_call_states[call->state], |
f5c17aae | 106 | call->abort_code, |
32f71aa4 | 107 | call->debug_id, |
3e018daf | 108 | tx_hard_ack, READ_ONCE(call->tx_top) - tx_hard_ack, |
887763bb | 109 | rx_hard_ack, READ_ONCE(call->rx_top) - rx_hard_ack, |
6b97bd7a | 110 | call->rx_serial, |
887763bb | 111 | timeout); |
17926a79 DH |
112 | |
113 | return 0; | |
114 | } | |
115 | ||
c3506372 | 116 | const struct seq_operations rxrpc_call_seq_ops = { |
17926a79 DH |
117 | .start = rxrpc_call_seq_start, |
118 | .next = rxrpc_call_seq_next, | |
119 | .stop = rxrpc_call_seq_stop, | |
120 | .show = rxrpc_call_seq_show, | |
121 | }; | |
122 | ||
17926a79 DH |
123 | /* |
124 | * generate a list of extant virtual connections in /proc/net/rxrpc_conns | |
125 | */ | |
126 | static void *rxrpc_connection_seq_start(struct seq_file *seq, loff_t *_pos) | |
88f2a825 | 127 | __acquires(rxnet->conn_lock) |
17926a79 | 128 | { |
2baec2c3 DH |
129 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); |
130 | ||
131 | read_lock(&rxnet->conn_lock); | |
132 | return seq_list_start_head(&rxnet->conn_proc_list, *_pos); | |
17926a79 DH |
133 | } |
134 | ||
135 | static void *rxrpc_connection_seq_next(struct seq_file *seq, void *v, | |
136 | loff_t *pos) | |
137 | { | |
2baec2c3 DH |
138 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); |
139 | ||
140 | return seq_list_next(v, &rxnet->conn_proc_list, pos); | |
17926a79 DH |
141 | } |
142 | ||
143 | static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v) | |
88f2a825 | 144 | __releases(rxnet->conn_lock) |
17926a79 | 145 | { |
2baec2c3 DH |
146 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); |
147 | ||
148 | read_unlock(&rxnet->conn_lock); | |
17926a79 DH |
149 | } |
150 | ||
151 | static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) | |
152 | { | |
153 | struct rxrpc_connection *conn; | |
2baec2c3 | 154 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); |
75b54cb5 | 155 | char lbuff[50], rbuff[50]; |
17926a79 | 156 | |
2baec2c3 | 157 | if (v == &rxnet->conn_proc_list) { |
17926a79 | 158 | seq_puts(seq, |
75b54cb5 DH |
159 | "Proto Local " |
160 | " Remote " | |
a1399f8b | 161 | " SvID ConnID End Use State Key " |
245500d8 | 162 | " Serial ISerial CallId0 CallId1 CallId2 CallId3\n" |
17926a79 DH |
163 | ); |
164 | return 0; | |
165 | } | |
166 | ||
4d028b2c | 167 | conn = list_entry(v, struct rxrpc_connection, proc_link); |
00e90712 DH |
168 | if (conn->state == RXRPC_CONN_SERVICE_PREALLOC) { |
169 | strcpy(lbuff, "no_local"); | |
170 | strcpy(rbuff, "no_connection"); | |
171 | goto print; | |
172 | } | |
17926a79 | 173 | |
75b54cb5 | 174 | sprintf(lbuff, "%pISpc", &conn->params.local->srx.transport); |
17926a79 | 175 | |
75b54cb5 | 176 | sprintf(rbuff, "%pISpc", &conn->params.peer->srx.transport); |
00e90712 | 177 | print: |
17926a79 | 178 | seq_printf(seq, |
75b54cb5 | 179 | "UDP %-47.47s %-47.47s %4x %08x %s %3u" |
6b97bd7a | 180 | " %s %08x %08x %08x %08x %08x %08x %08x\n", |
17926a79 DH |
181 | lbuff, |
182 | rbuff, | |
68d6d1ae | 183 | conn->service_id, |
19ffa01c | 184 | conn->proto.cid, |
19ffa01c | 185 | rxrpc_conn_is_service(conn) ? "Svc" : "Clt", |
a0575429 | 186 | refcount_read(&conn->ref), |
17926a79 | 187 | rxrpc_conn_states[conn->state], |
19ffa01c | 188 | key_serial(conn->params.key), |
17926a79 | 189 | atomic_read(&conn->serial), |
6b97bd7a DH |
190 | conn->hi_serial, |
191 | conn->channels[0].call_id, | |
192 | conn->channels[1].call_id, | |
193 | conn->channels[2].call_id, | |
194 | conn->channels[3].call_id); | |
17926a79 DH |
195 | |
196 | return 0; | |
197 | } | |
198 | ||
c3506372 | 199 | const struct seq_operations rxrpc_connection_seq_ops = { |
17926a79 DH |
200 | .start = rxrpc_connection_seq_start, |
201 | .next = rxrpc_connection_seq_next, | |
202 | .stop = rxrpc_connection_seq_stop, | |
203 | .show = rxrpc_connection_seq_show, | |
204 | }; | |
bc0e7cf4 DH |
205 | |
206 | /* | |
207 | * generate a list of extant virtual peers in /proc/net/rxrpc/peers | |
208 | */ | |
209 | static int rxrpc_peer_seq_show(struct seq_file *seq, void *v) | |
210 | { | |
211 | struct rxrpc_peer *peer; | |
212 | time64_t now; | |
213 | char lbuff[50], rbuff[50]; | |
214 | ||
215 | if (v == SEQ_START_TOKEN) { | |
216 | seq_puts(seq, | |
217 | "Proto Local " | |
218 | " Remote " | |
c410bf01 | 219 | " Use CW MTU LastUse RTT RTO\n" |
bc0e7cf4 DH |
220 | ); |
221 | return 0; | |
222 | } | |
223 | ||
224 | peer = list_entry(v, struct rxrpc_peer, hash_link); | |
225 | ||
226 | sprintf(lbuff, "%pISpc", &peer->local->srx.transport); | |
227 | ||
228 | sprintf(rbuff, "%pISpc", &peer->srx.transport); | |
229 | ||
230 | now = ktime_get_seconds(); | |
231 | seq_printf(seq, | |
232 | "UDP %-47.47s %-47.47s %3u" | |
c410bf01 | 233 | " %3u %5u %6llus %8u %8u\n", |
bc0e7cf4 DH |
234 | lbuff, |
235 | rbuff, | |
a0575429 | 236 | refcount_read(&peer->ref), |
bc0e7cf4 DH |
237 | peer->cong_cwnd, |
238 | peer->mtu, | |
239 | now - peer->last_tx_at, | |
c410bf01 DH |
240 | peer->srtt_us >> 3, |
241 | jiffies_to_usecs(peer->rto_j)); | |
bc0e7cf4 DH |
242 | |
243 | return 0; | |
244 | } | |
245 | ||
246 | static void *rxrpc_peer_seq_start(struct seq_file *seq, loff_t *_pos) | |
247 | __acquires(rcu) | |
248 | { | |
249 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); | |
250 | unsigned int bucket, n; | |
251 | unsigned int shift = 32 - HASH_BITS(rxnet->peer_hash); | |
252 | void *p; | |
253 | ||
254 | rcu_read_lock(); | |
255 | ||
256 | if (*_pos >= UINT_MAX) | |
257 | return NULL; | |
258 | ||
259 | n = *_pos & ((1U << shift) - 1); | |
260 | bucket = *_pos >> shift; | |
261 | for (;;) { | |
262 | if (bucket >= HASH_SIZE(rxnet->peer_hash)) { | |
263 | *_pos = UINT_MAX; | |
264 | return NULL; | |
265 | } | |
266 | if (n == 0) { | |
267 | if (bucket == 0) | |
268 | return SEQ_START_TOKEN; | |
269 | *_pos += 1; | |
270 | n++; | |
271 | } | |
272 | ||
273 | p = seq_hlist_start_rcu(&rxnet->peer_hash[bucket], n - 1); | |
274 | if (p) | |
275 | return p; | |
276 | bucket++; | |
277 | n = 1; | |
278 | *_pos = (bucket << shift) | n; | |
279 | } | |
280 | } | |
281 | ||
282 | static void *rxrpc_peer_seq_next(struct seq_file *seq, void *v, loff_t *_pos) | |
283 | { | |
284 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); | |
285 | unsigned int bucket, n; | |
286 | unsigned int shift = 32 - HASH_BITS(rxnet->peer_hash); | |
287 | void *p; | |
288 | ||
289 | if (*_pos >= UINT_MAX) | |
290 | return NULL; | |
291 | ||
292 | bucket = *_pos >> shift; | |
293 | ||
294 | p = seq_hlist_next_rcu(v, &rxnet->peer_hash[bucket], _pos); | |
295 | if (p) | |
296 | return p; | |
297 | ||
298 | for (;;) { | |
299 | bucket++; | |
300 | n = 1; | |
301 | *_pos = (bucket << shift) | n; | |
302 | ||
303 | if (bucket >= HASH_SIZE(rxnet->peer_hash)) { | |
304 | *_pos = UINT_MAX; | |
305 | return NULL; | |
306 | } | |
307 | if (n == 0) { | |
308 | *_pos += 1; | |
309 | n++; | |
310 | } | |
311 | ||
312 | p = seq_hlist_start_rcu(&rxnet->peer_hash[bucket], n - 1); | |
313 | if (p) | |
314 | return p; | |
315 | } | |
316 | } | |
317 | ||
318 | static void rxrpc_peer_seq_stop(struct seq_file *seq, void *v) | |
319 | __releases(rcu) | |
320 | { | |
321 | rcu_read_unlock(); | |
322 | } | |
323 | ||
324 | ||
325 | const struct seq_operations rxrpc_peer_seq_ops = { | |
326 | .start = rxrpc_peer_seq_start, | |
327 | .next = rxrpc_peer_seq_next, | |
328 | .stop = rxrpc_peer_seq_stop, | |
329 | .show = rxrpc_peer_seq_show, | |
330 | }; | |
33912c26 DH |
331 | |
332 | /* | |
333 | * Generate a list of extant virtual local endpoints in /proc/net/rxrpc/locals | |
334 | */ | |
335 | static int rxrpc_local_seq_show(struct seq_file *seq, void *v) | |
336 | { | |
337 | struct rxrpc_local *local; | |
338 | char lbuff[50]; | |
339 | ||
340 | if (v == SEQ_START_TOKEN) { | |
341 | seq_puts(seq, | |
342 | "Proto Local " | |
343 | " Use Act\n"); | |
344 | return 0; | |
345 | } | |
346 | ||
347 | local = hlist_entry(v, struct rxrpc_local, link); | |
348 | ||
349 | sprintf(lbuff, "%pISpc", &local->srx.transport); | |
350 | ||
351 | seq_printf(seq, | |
352 | "UDP %-47.47s %3u %3u\n", | |
353 | lbuff, | |
a0575429 | 354 | refcount_read(&local->ref), |
33912c26 DH |
355 | atomic_read(&local->active_users)); |
356 | ||
357 | return 0; | |
358 | } | |
359 | ||
360 | static void *rxrpc_local_seq_start(struct seq_file *seq, loff_t *_pos) | |
361 | __acquires(rcu) | |
362 | { | |
363 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); | |
364 | unsigned int n; | |
365 | ||
366 | rcu_read_lock(); | |
367 | ||
368 | if (*_pos >= UINT_MAX) | |
369 | return NULL; | |
370 | ||
371 | n = *_pos; | |
372 | if (n == 0) | |
373 | return SEQ_START_TOKEN; | |
374 | ||
375 | return seq_hlist_start_rcu(&rxnet->local_endpoints, n - 1); | |
376 | } | |
377 | ||
378 | static void *rxrpc_local_seq_next(struct seq_file *seq, void *v, loff_t *_pos) | |
379 | { | |
380 | struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); | |
381 | ||
382 | if (*_pos >= UINT_MAX) | |
383 | return NULL; | |
384 | ||
385 | return seq_hlist_next_rcu(v, &rxnet->local_endpoints, _pos); | |
386 | } | |
387 | ||
388 | static void rxrpc_local_seq_stop(struct seq_file *seq, void *v) | |
389 | __releases(rcu) | |
390 | { | |
391 | rcu_read_unlock(); | |
392 | } | |
393 | ||
394 | const struct seq_operations rxrpc_local_seq_ops = { | |
395 | .start = rxrpc_local_seq_start, | |
396 | .next = rxrpc_local_seq_next, | |
397 | .stop = rxrpc_local_seq_stop, | |
398 | .show = rxrpc_local_seq_show, | |
399 | }; |