rxrpc: Add transmitter stats procfile
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 7c3c897..29adaf2 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -1019,6 +1019,8 @@ int rxrpc_send_abort_packet(struct rxrpc_call *);
void rxrpc_reject_packets(struct rxrpc_local *);
void rxrpc_send_keepalive(struct rxrpc_peer *);
int rxrpc_transmitter(void *data);
+int rxrpc_transmitter_stats_show(struct seq_file *seq, void *v);
+
static inline void rxrpc_wake_up_transmitter(struct rxrpc_local *local)
{
wake_up_process(local->transmitter);
diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c
index bb4c25d..4501960 100644
--- a/net/rxrpc/net_ns.c
+++ b/net/rxrpc/net_ns.c
@@ -101,6 +101,8 @@ static __net_init int rxrpc_init_net(struct net *net)
proc_create_net("locals", 0444, rxnet->proc_net,
&rxrpc_local_seq_ops,
sizeof(struct seq_net_private));
+ proc_create_net_single("transmitter", S_IFREG | 0444, rxnet->proc_net,
+ rxrpc_transmitter_stats_show, NULL);
return 0;
err_proc:
diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c
index 767ac93..f89b60e 100644
--- a/net/rxrpc/output.c
+++ b/net/rxrpc/output.c
@@ -16,6 +16,33 @@
#include <net/udp.h>
#include "ar-internal.h"
+static atomic_t rxrpc_stat_tx_ack_dequeue;
+static atomic_t rxrpc_stat_tx_ack_fill;
+static atomic_t rxrpc_stat_tx_ack_fill_retry;
+static atomic_t rxrpc_stat_tx_ack_fill_weird;
+static atomic_t rxrpc_stat_tx_ack_send;
+static atomic_t rxrpc_stat_tx_ack_skip;
+static atomic_t rxrpc_stat_tx_ack_transmitter;
+static atomic_t rxrpc_stat_tx_data_dequeue;
+static atomic_t rxrpc_stat_tx_data_send;
+static atomic_t rxrpc_stat_tx_data_send_frag;
+static atomic_t rxrpc_stat_tx_loop;
+static atomic_t rxrpc_stat_tx_sendmsg;
+static atomic_t rxrpc_stat_tx_sendmsg_fail;
+static atomic_t rxrpc_stat_tx_sleep;
+static atomic_t rxrpc_stat_tx_zcopy_clean_queued;
+static atomic_t rxrpc_stat_tx_zcopy_clean_dequeued;
+
+static void rxrpc_inc_stat(atomic_t *s)
+{
+ atomic_inc(s);
+}
+
+static void rxrpc_dec_stat(atomic_t *s)
+{
+ atomic_dec(s);
+}
+
static const char rxrpc_keepalive_string[] = "";
extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
@@ -50,8 +77,10 @@ static int rxrpc_zcopy_sendmsg(struct rxrpc_local *local, struct rxrpc_txbuf *tx
if (list_empty(&txb->cleanup_link))
*_queued = true;
list_move_tail(&txb->cleanup_link, &local->zcopy_cleanup_queue);
+ rxrpc_inc_stat(&rxrpc_stat_tx_zcopy_clean_queued);
spin_unlock(&local->tx_lock);
+ rxrpc_inc_stat(&rxrpc_stat_tx_sendmsg);
ret = do_udp_sendmsg(local->socket, msg, sizeof(txb->wire) + txb->len);
if (ret < 0) {
/* We got an error. Leave the buffer on the queue, but with a
@@ -60,6 +89,7 @@ static int rxrpc_zcopy_sendmsg(struct rxrpc_local *local, struct rxrpc_txbuf *tx
* can't just dequeue it as it may be pending cleanup from a
* previous transmission.
*/
+ rxrpc_inc_stat(&rxrpc_stat_tx_sendmsg_fail);
txb->zcopy_seq--;
return ret;
}
@@ -92,6 +122,7 @@ bool rxrpc_zcopy_cleanup(struct rxrpc_local *local)
if (!txb)
return again;
+ rxrpc_inc_stat(&rxrpc_stat_tx_zcopy_clean_dequeued);
seq = txb->seq;
call = txb->call;
rxrpc_put_txbuf(txb, rxrpc_txbuf_put_zcopy);
@@ -157,8 +188,10 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn,
tmp = atomic_xchg(&call->ackr_nr_unacked, 0);
tmp |= atomic_xchg(&call->ackr_nr_consumed, 0);
if (!tmp && (txb->ack.reason == RXRPC_ACK_DELAY ||
- txb->ack.reason == RXRPC_ACK_IDLE))
+ txb->ack.reason == RXRPC_ACK_IDLE)) {
+ rxrpc_inc_stat(&rxrpc_stat_tx_ack_skip);
return 0;
+ }
/* Barrier against rxrpc_input_data(). */
retry:
@@ -168,6 +201,11 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn,
txb->ack.previousPacket = htonl(whigh);
if (before(whigh, hard_ack)) {
cond_resched();
+ if (hard_ack == READ_ONCE(call->rx_hard_ack)) {
+ rxrpc_inc_stat(&rxrpc_stat_tx_ack_fill_weird);
+ return 0;
+ }
+ rxrpc_inc_stat(&rxrpc_stat_tx_ack_fill_retry);
goto retry;
}
txb->ack.firstPacket = htonl(window);
@@ -179,6 +217,7 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn,
tmp = READ_ONCE(call->rx_hard_ack);
if (tmp != hard_ack) {
cond_resched();
+ rxrpc_inc_stat(&rxrpc_stat_tx_ack_fill_retry);
goto retry;
}
@@ -309,6 +348,7 @@ static int rxrpc_send_ack_packet(struct rxrpc_local *local, struct rxrpc_txbuf *
if (txb->ack.reason == RXRPC_ACK_PING)
rtt_slot = rxrpc_begin_rtt_probe(call, serial, rxrpc_rtt_tx_ping);
+ rxrpc_inc_stat(&rxrpc_stat_tx_ack_send);
ret = rxrpc_zcopy_sendmsg(conn->params.local, txb, &msg, _queued);
call->peer->last_tx_at = ktime_get_seconds();
if (ret < 0)
@@ -341,6 +381,7 @@ static void rxrpc_ack_transmitter(struct rxrpc_local *local)
trace_rxrpc_local(local->debug_id, rxrpc_local_tx_ack,
refcount_read(&local->ref), NULL);
+ rxrpc_inc_stat(&rxrpc_stat_tx_ack_transmitter);
spin_lock_bh(&local->ack_tx_lock);
list_splice_tail_init(&local->ack_tx_queue, &queue);
@@ -350,6 +391,8 @@ static void rxrpc_ack_transmitter(struct rxrpc_local *local)
struct rxrpc_txbuf *txb =
list_entry(queue.next, struct rxrpc_txbuf, tx_link);
+ rxrpc_inc_stat(&rxrpc_stat_tx_ack_dequeue);
+
queued = false;
ret = rxrpc_send_ack_packet(local, txb, &queued);
if (ret < 0 && ret != -ECONNRESET) {
@@ -536,6 +579,7 @@ static int rxrpc_send_data_packet(struct rxrpc_call *call, struct rxrpc_txbuf *t
* - in which case, we'll have processed the ICMP error
* message and update the peer record
*/
+ rxrpc_inc_stat(&rxrpc_stat_tx_data_send);
ret = rxrpc_zcopy_sendmsg(conn->params.local, txb, &msg, _queued);
conn->params.peer->last_tx_at = ktime_get_seconds();
@@ -609,6 +653,7 @@ static int rxrpc_send_data_packet(struct rxrpc_call *call, struct rxrpc_txbuf *t
case AF_INET:
ip_sock_set_mtu_discover(conn->params.local->socket->sk,
IP_PMTUDISC_DONT);
+ rxrpc_inc_stat(&rxrpc_stat_tx_data_send_frag);
ret = rxrpc_zcopy_sendmsg(conn->params.local, txb, &msg, _queued);
conn->params.peer->last_tx_at = ktime_get_seconds();
@@ -831,6 +876,7 @@ int rxrpc_transmitter(void *data)
set_user_nice(current, MIN_NICE);
for (;;) {
+ rxrpc_inc_stat(&rxrpc_stat_tx_loop);
if (!list_empty(&local->ack_tx_queue))
rxrpc_ack_transmitter(local);
@@ -838,6 +884,7 @@ int rxrpc_transmitter(void *data)
txb = list_first_entry_or_null(&local->tx_queue,
struct rxrpc_txbuf, tx_link);
if (txb) {
+ rxrpc_inc_stat(&rxrpc_stat_tx_data_dequeue);
call = txb->call;
list_del_init(&txb->tx_link);
spin_unlock(&local->tx_lock);
@@ -861,9 +908,48 @@ int rxrpc_transmitter(void *data)
__set_current_state(TASK_RUNNING);
continue;
}
+ rxrpc_inc_stat(&rxrpc_stat_tx_sleep);
schedule();
}
__set_current_state(TASK_RUNNING);
local->transmitter = NULL;
return 0;
}
+
+/*
+ * Generate transmitted stats in /proc/net/rxrpc/transmitter
+ */
+int rxrpc_transmitter_stats_show(struct seq_file *seq, void *v)
+{
+ seq_printf(seq,
+ "Trans : loop=%u sleep=%u\n",
+ atomic_read(&rxrpc_stat_tx_loop),
+ atomic_read(&rxrpc_stat_tx_sleep));
+ seq_printf(seq,
+ "Zcopy : clnq=%u/%u\n",
+ atomic_read(&rxrpc_stat_tx_zcopy_clean_queued),
+ atomic_read(&rxrpc_stat_tx_zcopy_clean_queued));
+ seq_printf(seq,
+ "Sendmsg: send=%u fail=%u\n",
+ atomic_read(&rxrpc_stat_tx_sendmsg),
+ atomic_read(&rxrpc_stat_tx_sendmsg_fail));
+ seq_printf(seq,
+ "Data : deq=%u send=%u sendf=%u\n",
+ atomic_read(&rxrpc_stat_tx_data_dequeue),
+ atomic_read(&rxrpc_stat_tx_data_send),
+ atomic_read(&rxrpc_stat_tx_data_send_frag));
+ seq_printf(seq,
+ "Ack : deq=%u fill=%u frtr=%u wrd=%u send=%u skip=%u tr=%u\n",
+ atomic_read(&rxrpc_stat_tx_ack_dequeue),
+ atomic_read(&rxrpc_stat_tx_ack_fill),
+ atomic_read(&rxrpc_stat_tx_ack_fill_retry),
+ atomic_read(&rxrpc_stat_tx_ack_fill_weird),
+ atomic_read(&rxrpc_stat_tx_ack_send),
+ atomic_read(&rxrpc_stat_tx_ack_skip),
+ atomic_read(&rxrpc_stat_tx_ack_transmitter));
+ seq_printf(seq,
+ "Buffers: txb=%u skb=%u\n",
+ atomic_read(&rxrpc_nr_txbuf),
+ atomic_read(&rxrpc_n_rx_skbs));
+ return 0;
+}