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