proc.c 8.26 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* /proc/net/ support for AF_RXRPC
 *
 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/module.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include "ar-internal.h"

17 18 19
static const char *const rxrpc_conn_states[RXRPC_CONN__NR_STATES] = {
	[RXRPC_CONN_UNUSED]			= "Unused  ",
	[RXRPC_CONN_CLIENT]			= "Client  ",
20
	[RXRPC_CONN_SERVICE_PREALLOC]		= "SvPrealc",
21 22 23 24 25
	[RXRPC_CONN_SERVICE_UNSECURED]		= "SvUnsec ",
	[RXRPC_CONN_SERVICE_CHALLENGING]	= "SvChall ",
	[RXRPC_CONN_SERVICE]			= "SvSecure",
	[RXRPC_CONN_REMOTELY_ABORTED]		= "RmtAbort",
	[RXRPC_CONN_LOCALLY_ABORTED]		= "LocAbort",
26 27 28 29 30 31
};

/*
 * generate a list of extant and dead calls in /proc/net/rxrpc_calls
 */
static void *rxrpc_call_seq_start(struct seq_file *seq, loff_t *_pos)
32 33
	__acquires(rcu)
	__acquires(rxnet->call_lock)
34
{
35 36
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));

37
	rcu_read_lock();
38 39
	read_lock(&rxnet->call_lock);
	return seq_list_start_head(&rxnet->calls, *_pos);
40 41 42 43
}

static void *rxrpc_call_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
44 45 46
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));

	return seq_list_next(v, &rxnet->calls, pos);
47 48 49
}

static void rxrpc_call_seq_stop(struct seq_file *seq, void *v)
50 51
	__releases(rxnet->call_lock)
	__releases(rcu)
52
{
53 54 55
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));

	read_unlock(&rxnet->call_lock);
56
	rcu_read_unlock();
57 58 59 60
}

static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
{
61 62 63
	struct rxrpc_local *local;
	struct rxrpc_sock *rx;
	struct rxrpc_peer *peer;
64
	struct rxrpc_call *call;
65
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
66
	unsigned long timeout = 0;
67
	rxrpc_seq_t tx_hard_ack, rx_hard_ack;
David Howells's avatar
David Howells committed
68
	char lbuff[50], rbuff[50];
69

70
	if (v == &rxnet->calls) {
71
		seq_puts(seq,
David Howells's avatar
David Howells committed
72 73
			 "Proto Local                                          "
			 " Remote                                         "
74
			 " SvID ConnID   CallID   End Use State    Abort   "
75
			 " UserID           TxSeq    TW RxSeq    RW RxSerial RxTimo\n");
76 77 78 79 80
		return 0;
	}

	call = list_entry(v, struct rxrpc_call, link);

81
	rx = rcu_dereference(call->socket);
82 83 84
	if (rx) {
		local = READ_ONCE(rx->local);
		if (local)
David Howells's avatar
David Howells committed
85
			sprintf(lbuff, "%pISpc", &local->srx.transport);
86 87 88 89 90
		else
			strcpy(lbuff, "no_local");
	} else {
		strcpy(lbuff, "no_socket");
	}
91

92 93
	peer = call->peer;
	if (peer)
David Howells's avatar
David Howells committed
94
		sprintf(rbuff, "%pISpc", &peer->srx.transport);
95 96
	else
		strcpy(rbuff, "no_connection");
97

98 99 100 101 102
	if (call->state != RXRPC_CALL_SERVER_PREALLOC) {
		timeout = READ_ONCE(call->expect_rx_by);
		timeout -= jiffies;
	}

103 104
	tx_hard_ack = READ_ONCE(call->tx_hard_ack);
	rx_hard_ack = READ_ONCE(call->rx_hard_ack);
105
	seq_printf(seq,
David Howells's avatar
David Howells committed
106
		   "UDP   %-47.47s %-47.47s %4x %08x %08x %s %3u"
107
		   " %-8.8s %08x %lx %08x %02x %08x %02x %08x %06lx\n",
108 109
		   lbuff,
		   rbuff,
110
		   call->service_id,
111 112
		   call->cid,
		   call->call_id,
113
		   rxrpc_is_service_call(call) ? "Svc" : "Clt",
114 115
		   atomic_read(&call->usage),
		   rxrpc_call_states[call->state],
116
		   call->abort_code,
117 118
		   call->user_call_ID,
		   tx_hard_ack, READ_ONCE(call->tx_top) - tx_hard_ack,
119
		   rx_hard_ack, READ_ONCE(call->rx_top) - rx_hard_ack,
120
		   call->rx_serial,
121
		   timeout);
122 123 124 125

	return 0;
}

126
const struct seq_operations rxrpc_call_seq_ops = {
127 128 129 130 131 132 133 134 135 136
	.start  = rxrpc_call_seq_start,
	.next   = rxrpc_call_seq_next,
	.stop   = rxrpc_call_seq_stop,
	.show   = rxrpc_call_seq_show,
};

/*
 * generate a list of extant virtual connections in /proc/net/rxrpc_conns
 */
static void *rxrpc_connection_seq_start(struct seq_file *seq, loff_t *_pos)
137
	__acquires(rxnet->conn_lock)
138
{
139 140 141 142
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));

	read_lock(&rxnet->conn_lock);
	return seq_list_start_head(&rxnet->conn_proc_list, *_pos);
143 144 145 146 147
}

static void *rxrpc_connection_seq_next(struct seq_file *seq, void *v,
				       loff_t *pos)
{
148 149 150
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));

	return seq_list_next(v, &rxnet->conn_proc_list, pos);
151 152 153
}

static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v)
154
	__releases(rxnet->conn_lock)
155
{
156 157 158
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));

	read_unlock(&rxnet->conn_lock);
159 160 161 162 163
}

static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
{
	struct rxrpc_connection *conn;
164
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
David Howells's avatar
David Howells committed
165
	char lbuff[50], rbuff[50];
166

167
	if (v == &rxnet->conn_proc_list) {
168
		seq_puts(seq,
David Howells's avatar
David Howells committed
169 170
			 "Proto Local                                          "
			 " Remote                                         "
171
			 " SvID ConnID   End Use State    Key     "
172 173 174 175 176
			 " Serial   ISerial\n"
			 );
		return 0;
	}

177
	conn = list_entry(v, struct rxrpc_connection, proc_link);
178 179 180 181 182
	if (conn->state == RXRPC_CONN_SERVICE_PREALLOC) {
		strcpy(lbuff, "no_local");
		strcpy(rbuff, "no_connection");
		goto print;
	}
183

David Howells's avatar
David Howells committed
184
	sprintf(lbuff, "%pISpc", &conn->params.local->srx.transport);
185

David Howells's avatar
David Howells committed
186
	sprintf(rbuff, "%pISpc", &conn->params.peer->srx.transport);
187
print:
188
	seq_printf(seq,
David Howells's avatar
David Howells committed
189
		   "UDP   %-47.47s %-47.47s %4x %08x %s %3u"
190
		   " %s %08x %08x %08x %08x %08x %08x %08x\n",
191 192
		   lbuff,
		   rbuff,
193
		   conn->service_id,
194 195
		   conn->proto.cid,
		   rxrpc_conn_is_service(conn) ? "Svc" : "Clt",
196 197
		   atomic_read(&conn->usage),
		   rxrpc_conn_states[conn->state],
198
		   key_serial(conn->params.key),
199
		   atomic_read(&conn->serial),
200 201 202 203 204
		   conn->hi_serial,
		   conn->channels[0].call_id,
		   conn->channels[1].call_id,
		   conn->channels[2].call_id,
		   conn->channels[3].call_id);
205 206 207 208

	return 0;
}

209
const struct seq_operations rxrpc_connection_seq_ops = {
210 211 212 213 214
	.start  = rxrpc_connection_seq_start,
	.next   = rxrpc_connection_seq_next,
	.stop   = rxrpc_connection_seq_stop,
	.show   = rxrpc_connection_seq_show,
};
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340

/*
 * generate a list of extant virtual peers in /proc/net/rxrpc/peers
 */
static int rxrpc_peer_seq_show(struct seq_file *seq, void *v)
{
	struct rxrpc_peer *peer;
	time64_t now;
	char lbuff[50], rbuff[50];

	if (v == SEQ_START_TOKEN) {
		seq_puts(seq,
			 "Proto Local                                          "
			 " Remote                                         "
			 " Use CW  MTU   LastUse          RTT Rc\n"
			 );
		return 0;
	}

	peer = list_entry(v, struct rxrpc_peer, hash_link);

	sprintf(lbuff, "%pISpc", &peer->local->srx.transport);

	sprintf(rbuff, "%pISpc", &peer->srx.transport);

	now = ktime_get_seconds();
	seq_printf(seq,
		   "UDP   %-47.47s %-47.47s %3u"
		   " %3u %5u %6llus %12llu %2u\n",
		   lbuff,
		   rbuff,
		   atomic_read(&peer->usage),
		   peer->cong_cwnd,
		   peer->mtu,
		   now - peer->last_tx_at,
		   peer->rtt,
		   peer->rtt_cursor);

	return 0;
}

static void *rxrpc_peer_seq_start(struct seq_file *seq, loff_t *_pos)
	__acquires(rcu)
{
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
	unsigned int bucket, n;
	unsigned int shift = 32 - HASH_BITS(rxnet->peer_hash);
	void *p;

	rcu_read_lock();

	if (*_pos >= UINT_MAX)
		return NULL;

	n = *_pos & ((1U << shift) - 1);
	bucket = *_pos >> shift;
	for (;;) {
		if (bucket >= HASH_SIZE(rxnet->peer_hash)) {
			*_pos = UINT_MAX;
			return NULL;
		}
		if (n == 0) {
			if (bucket == 0)
				return SEQ_START_TOKEN;
			*_pos += 1;
			n++;
		}

		p = seq_hlist_start_rcu(&rxnet->peer_hash[bucket], n - 1);
		if (p)
			return p;
		bucket++;
		n = 1;
		*_pos = (bucket << shift) | n;
	}
}

static void *rxrpc_peer_seq_next(struct seq_file *seq, void *v, loff_t *_pos)
{
	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
	unsigned int bucket, n;
	unsigned int shift = 32 - HASH_BITS(rxnet->peer_hash);
	void *p;

	if (*_pos >= UINT_MAX)
		return NULL;

	bucket = *_pos >> shift;

	p = seq_hlist_next_rcu(v, &rxnet->peer_hash[bucket], _pos);
	if (p)
		return p;

	for (;;) {
		bucket++;
		n = 1;
		*_pos = (bucket << shift) | n;

		if (bucket >= HASH_SIZE(rxnet->peer_hash)) {
			*_pos = UINT_MAX;
			return NULL;
		}
		if (n == 0) {
			*_pos += 1;
			n++;
		}

		p = seq_hlist_start_rcu(&rxnet->peer_hash[bucket], n - 1);
		if (p)
			return p;
	}
}

static void rxrpc_peer_seq_stop(struct seq_file *seq, void *v)
	__releases(rcu)
{
	rcu_read_unlock();
}


const struct seq_operations rxrpc_peer_seq_ops = {
	.start  = rxrpc_peer_seq_start,
	.next   = rxrpc_peer_seq_next,
	.stop   = rxrpc_peer_seq_stop,
	.show   = rxrpc_peer_seq_show,
};