2007-04-27 05:48:28 +07:00
|
|
|
/* /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"
|
|
|
|
|
2016-06-27 16:32:02 +07:00
|
|
|
static const char *const rxrpc_conn_states[RXRPC_CONN__NR_STATES] = {
|
|
|
|
[RXRPC_CONN_UNUSED] = "Unused ",
|
|
|
|
[RXRPC_CONN_CLIENT] = "Client ",
|
2016-09-08 17:10:12 +07:00
|
|
|
[RXRPC_CONN_SERVICE_PREALLOC] = "SvPrealc",
|
2016-06-27 16:32:02 +07:00
|
|
|
[RXRPC_CONN_SERVICE_UNSECURED] = "SvUnsec ",
|
|
|
|
[RXRPC_CONN_SERVICE_CHALLENGING] = "SvChall ",
|
|
|
|
[RXRPC_CONN_SERVICE] = "SvSecure",
|
|
|
|
[RXRPC_CONN_REMOTELY_ABORTED] = "RmtAbort",
|
|
|
|
[RXRPC_CONN_LOCALLY_ABORTED] = "LocAbort",
|
2007-04-27 05:48:28 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
2018-03-31 03:05:17 +07:00
|
|
|
__acquires(rcu)
|
|
|
|
__acquires(rxnet->call_lock)
|
2007-04-27 05:48:28 +07:00
|
|
|
{
|
2017-05-24 23:02:32 +07:00
|
|
|
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
|
|
|
|
|
2016-09-07 15:19:31 +07:00
|
|
|
rcu_read_lock();
|
2017-05-24 23:02:32 +07:00
|
|
|
read_lock(&rxnet->call_lock);
|
|
|
|
return seq_list_start_head(&rxnet->calls, *_pos);
|
2007-04-27 05:48:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *rxrpc_call_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
|
|
{
|
2017-05-24 23:02:32 +07:00
|
|
|
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
|
|
|
|
|
|
|
|
return seq_list_next(v, &rxnet->calls, pos);
|
2007-04-27 05:48:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rxrpc_call_seq_stop(struct seq_file *seq, void *v)
|
2018-03-31 03:05:17 +07:00
|
|
|
__releases(rxnet->call_lock)
|
|
|
|
__releases(rcu)
|
2007-04-27 05:48:28 +07:00
|
|
|
{
|
2017-05-24 23:02:32 +07:00
|
|
|
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
|
|
|
|
|
|
|
|
read_unlock(&rxnet->call_lock);
|
2016-09-07 15:19:31 +07:00
|
|
|
rcu_read_unlock();
|
2007-04-27 05:48:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
|
|
|
|
{
|
2016-08-24 20:31:43 +07:00
|
|
|
struct rxrpc_local *local;
|
|
|
|
struct rxrpc_sock *rx;
|
|
|
|
struct rxrpc_peer *peer;
|
2007-04-27 05:48:28 +07:00
|
|
|
struct rxrpc_call *call;
|
2017-05-24 23:02:32 +07:00
|
|
|
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
|
2018-08-02 15:13:33 +07:00
|
|
|
unsigned long timeout = 0;
|
2017-01-05 17:38:33 +07:00
|
|
|
rxrpc_seq_t tx_hard_ack, rx_hard_ack;
|
2016-09-13 14:49:05 +07:00
|
|
|
char lbuff[50], rbuff[50];
|
2007-04-27 05:48:28 +07:00
|
|
|
|
2017-05-24 23:02:32 +07:00
|
|
|
if (v == &rxnet->calls) {
|
2007-04-27 05:48:28 +07:00
|
|
|
seq_puts(seq,
|
2016-09-13 14:49:05 +07:00
|
|
|
"Proto Local "
|
|
|
|
" Remote "
|
2007-04-27 05:48:28 +07:00
|
|
|
" SvID ConnID CallID End Use State Abort "
|
2018-07-23 23:18:36 +07:00
|
|
|
" UserID TxSeq TW RxSeq RW RxSerial RxTimo\n");
|
2007-04-27 05:48:28 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
call = list_entry(v, struct rxrpc_call, link);
|
|
|
|
|
2016-09-07 15:19:31 +07:00
|
|
|
rx = rcu_dereference(call->socket);
|
2016-08-24 20:31:43 +07:00
|
|
|
if (rx) {
|
|
|
|
local = READ_ONCE(rx->local);
|
|
|
|
if (local)
|
2016-09-13 14:49:05 +07:00
|
|
|
sprintf(lbuff, "%pISpc", &local->srx.transport);
|
2016-08-24 20:31:43 +07:00
|
|
|
else
|
|
|
|
strcpy(lbuff, "no_local");
|
|
|
|
} else {
|
|
|
|
strcpy(lbuff, "no_socket");
|
|
|
|
}
|
2007-04-27 05:48:28 +07:00
|
|
|
|
2016-08-24 20:31:43 +07:00
|
|
|
peer = call->peer;
|
|
|
|
if (peer)
|
2016-09-13 14:49:05 +07:00
|
|
|
sprintf(rbuff, "%pISpc", &peer->srx.transport);
|
2016-06-17 17:07:55 +07:00
|
|
|
else
|
|
|
|
strcpy(rbuff, "no_connection");
|
2007-04-27 05:48:28 +07:00
|
|
|
|
2018-07-23 23:18:36 +07:00
|
|
|
if (call->state != RXRPC_CALL_SERVER_PREALLOC) {
|
|
|
|
timeout = READ_ONCE(call->expect_rx_by);
|
|
|
|
timeout -= jiffies;
|
|
|
|
}
|
|
|
|
|
2017-01-05 17:38:33 +07:00
|
|
|
tx_hard_ack = READ_ONCE(call->tx_hard_ack);
|
|
|
|
rx_hard_ack = READ_ONCE(call->rx_hard_ack);
|
2007-04-27 05:48:28 +07:00
|
|
|
seq_printf(seq,
|
2016-09-13 14:49:05 +07:00
|
|
|
"UDP %-47.47s %-47.47s %4x %08x %08x %s %3u"
|
2018-07-23 23:18:36 +07:00
|
|
|
" %-8.8s %08x %lx %08x %02x %08x %02x %08x %06lx\n",
|
2007-04-27 05:48:28 +07:00
|
|
|
lbuff,
|
|
|
|
rbuff,
|
2016-06-17 17:07:55 +07:00
|
|
|
call->service_id,
|
2016-03-04 22:53:46 +07:00
|
|
|
call->cid,
|
|
|
|
call->call_id,
|
2016-08-23 21:27:24 +07:00
|
|
|
rxrpc_is_service_call(call) ? "Svc" : "Clt",
|
2007-04-27 05:48:28 +07:00
|
|
|
atomic_read(&call->usage),
|
|
|
|
rxrpc_call_states[call->state],
|
2016-08-30 15:49:28 +07:00
|
|
|
call->abort_code,
|
2017-01-05 17:38:33 +07:00
|
|
|
call->user_call_ID,
|
|
|
|
tx_hard_ack, READ_ONCE(call->tx_top) - tx_hard_ack,
|
2018-07-23 23:18:36 +07:00
|
|
|
rx_hard_ack, READ_ONCE(call->rx_top) - rx_hard_ack,
|
2018-07-23 23:18:36 +07:00
|
|
|
call->rx_serial,
|
2018-07-23 23:18:36 +07:00
|
|
|
timeout);
|
2007-04-27 05:48:28 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-11 00:42:55 +07:00
|
|
|
const struct seq_operations rxrpc_call_seq_ops = {
|
2007-04-27 05:48:28 +07:00
|
|
|
.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)
|
2018-03-31 03:05:17 +07:00
|
|
|
__acquires(rxnet->conn_lock)
|
2007-04-27 05:48:28 +07:00
|
|
|
{
|
2017-05-24 23:02:32 +07:00
|
|
|
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);
|
2007-04-27 05:48:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *rxrpc_connection_seq_next(struct seq_file *seq, void *v,
|
|
|
|
loff_t *pos)
|
|
|
|
{
|
2017-05-24 23:02:32 +07:00
|
|
|
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
|
|
|
|
|
|
|
|
return seq_list_next(v, &rxnet->conn_proc_list, pos);
|
2007-04-27 05:48:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v)
|
2018-03-31 03:05:17 +07:00
|
|
|
__releases(rxnet->conn_lock)
|
2007-04-27 05:48:28 +07:00
|
|
|
{
|
2017-05-24 23:02:32 +07:00
|
|
|
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
|
|
|
|
|
|
|
|
read_unlock(&rxnet->conn_lock);
|
2007-04-27 05:48:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
|
|
|
|
{
|
|
|
|
struct rxrpc_connection *conn;
|
2017-05-24 23:02:32 +07:00
|
|
|
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
|
2016-09-13 14:49:05 +07:00
|
|
|
char lbuff[50], rbuff[50];
|
2007-04-27 05:48:28 +07:00
|
|
|
|
2017-05-24 23:02:32 +07:00
|
|
|
if (v == &rxnet->conn_proc_list) {
|
2007-04-27 05:48:28 +07:00
|
|
|
seq_puts(seq,
|
2016-09-13 14:49:05 +07:00
|
|
|
"Proto Local "
|
|
|
|
" Remote "
|
rxrpc: Call channels should have separate call number spaces
Each channel on a connection has a separate, independent number space from
which to allocate callNumber values. It is entirely possible, for example,
to have a connection with four active calls, each with call number 1.
Note that the callNumber values for any particular channel don't have to
start at 1, but they are supposed to increment monotonically for that
channel from a client's perspective and may not be reused once the call
number is transmitted (until the epoch cycles all the way back round).
Currently, however, call numbers are allocated on a per-connection basis
and, further, are held in an rb-tree. The rb-tree is redundant as the four
channel pointers in the rxrpc_connection struct are entirely capable of
pointing to all the calls currently in progress on a connection.
To this end, make the following changes:
(1) Handle call number allocation independently per channel.
(2) Get rid of the conn->calls rb-tree. This is overkill as a connection
may have a maximum of four calls in progress at any one time. Use the
pointers in the channels[] array instead, indexed by the channel
number from the packet.
(3) For each channel, save the result of the last call that was in
progress on that channel in conn->channels[] so that the final ACK or
ABORT packet can be replayed if necessary. Any call earlier than that
is just ignored. If we've seen the next call number in a packet, the
last one is most definitely defunct.
(4) When generating a RESPONSE packet for a connection, the call number
counter for each channel must be included in it.
(5) When parsing a RESPONSE packet for a connection, the call number
counters contained therein should be used to set the minimum expected
call numbers on each channel.
To do in future commits:
(1) Replay terminal packets based on the last call stored in
conn->channels[].
(2) Connections should be retired before the callNumber space on any
channel runs out.
(3) A server is expected to disregard or reject any new incoming call that
has a call number less than the current call number counter. The call
number counter for that channel must be advanced to the new call
number.
Note that the server cannot just require that the next call that it
sees on a channel be exactly the call number counter + 1 because then
there's a scenario that could cause a problem: The client transmits a
packet to initiate a connection, the network goes out, the server
sends an ACK (which gets lost), the client sends an ABORT (which also
gets lost); the network then reconnects, the client then reuses the
call number for the next call (it doesn't know the server already saw
the call number), but the server thinks it already has the first
packet of this call (it doesn't know that the client doesn't know that
it saw the call number the first time).
Signed-off-by: David Howells <dhowells@redhat.com>
2016-06-27 20:39:44 +07:00
|
|
|
" SvID ConnID End Use State Key "
|
2007-04-27 05:48:28 +07:00
|
|
|
" Serial ISerial\n"
|
|
|
|
);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-24 13:30:52 +07:00
|
|
|
conn = list_entry(v, struct rxrpc_connection, proc_link);
|
2016-09-08 17:10:12 +07:00
|
|
|
if (conn->state == RXRPC_CONN_SERVICE_PREALLOC) {
|
|
|
|
strcpy(lbuff, "no_local");
|
|
|
|
strcpy(rbuff, "no_connection");
|
|
|
|
goto print;
|
|
|
|
}
|
2007-04-27 05:48:28 +07:00
|
|
|
|
2016-09-13 14:49:05 +07:00
|
|
|
sprintf(lbuff, "%pISpc", &conn->params.local->srx.transport);
|
2007-04-27 05:48:28 +07:00
|
|
|
|
2016-09-13 14:49:05 +07:00
|
|
|
sprintf(rbuff, "%pISpc", &conn->params.peer->srx.transport);
|
2016-09-08 17:10:12 +07:00
|
|
|
print:
|
2007-04-27 05:48:28 +07:00
|
|
|
seq_printf(seq,
|
2016-09-13 14:49:05 +07:00
|
|
|
"UDP %-47.47s %-47.47s %4x %08x %s %3u"
|
2018-07-23 23:18:36 +07:00
|
|
|
" %s %08x %08x %08x %08x %08x %08x %08x\n",
|
2007-04-27 05:48:28 +07:00
|
|
|
lbuff,
|
|
|
|
rbuff,
|
2017-06-05 20:30:49 +07:00
|
|
|
conn->service_id,
|
2016-04-04 20:00:36 +07:00
|
|
|
conn->proto.cid,
|
|
|
|
rxrpc_conn_is_service(conn) ? "Svc" : "Clt",
|
2007-04-27 05:48:28 +07:00
|
|
|
atomic_read(&conn->usage),
|
|
|
|
rxrpc_conn_states[conn->state],
|
2016-04-04 20:00:36 +07:00
|
|
|
key_serial(conn->params.key),
|
2007-04-27 05:48:28 +07:00
|
|
|
atomic_read(&conn->serial),
|
2018-07-23 23:18:36 +07:00
|
|
|
conn->hi_serial,
|
|
|
|
conn->channels[0].call_id,
|
|
|
|
conn->channels[1].call_id,
|
|
|
|
conn->channels[2].call_id,
|
|
|
|
conn->channels[3].call_id);
|
2007-04-27 05:48:28 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-11 00:42:55 +07:00
|
|
|
const struct seq_operations rxrpc_connection_seq_ops = {
|
2007-04-27 05:48:28 +07:00
|
|
|
.start = rxrpc_connection_seq_start,
|
|
|
|
.next = rxrpc_connection_seq_next,
|
|
|
|
.stop = rxrpc_connection_seq_stop,
|
|
|
|
.show = rxrpc_connection_seq_show,
|
|
|
|
};
|
2018-10-15 17:31:03 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
|
|
|
};
|