Skip to content

Commit

Permalink
mptcp: listen diag dump support
Browse files Browse the repository at this point in the history
makes 'ss -Ml' show mptcp listen sockets.

Iterate over the tcp listen sockets and pick those that have mptcp ulp
info attached.

mptcp_diag_get_info() is modified to prefer msk->first for mptcp sockets
in listen state.  This reports accurate number for recv and send queue
(pending / max connection backlog counters).

Sample output:
ss -Mil
State        Recv-Q Send-Q Local Address:Port  Peer Address:Port
LISTEN       0      20     127.0.0.1:12000     0.0.0.0:*
         subflows_max:2

Signed-off-by: Florian Westphal <fw@strlen.de>
  • Loading branch information
Florian Westphal authored and intel-lab-lkp committed Mar 28, 2022
1 parent 9ac7ded commit 6ef1c53
Showing 1 changed file with 88 additions and 0 deletions.
88 changes: 88 additions & 0 deletions net/mptcp/mptcp_diag.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,80 @@ static int mptcp_diag_dump_one(struct netlink_callback *cb,
struct mptcp_diag_ctx {
long s_slot;
long s_num;
unsigned int l_slot;
unsigned int l_num;
};

static void mptcp_diag_dump_listeners(struct sk_buff *skb, struct netlink_callback *cb,
const struct inet_diag_req_v2 *r,
bool net_admin)
{
struct mptcp_diag_ctx *diag_ctx = (void *)cb->ctx;
struct net *net = sock_net(skb->sk);
struct nlattr *bc;
int i;

for (i = diag_ctx->l_slot; i < INET_LHTABLE_SIZE; i++) {
struct inet_listen_hashbucket *ilb;
struct hlist_nulls_node *node;
struct sock *sk;
int num = 0;

ilb = &tcp_hashinfo.listening_hash[i];

rcu_read_lock();
spin_lock(&ilb->lock);
sk_nulls_for_each(sk, node, &ilb->nulls_head) {
const struct mptcp_subflow_context *ctx = mptcp_subflow_ctx(sk);
struct inet_sock *inet = inet_sk(sk);
int ret;

if (num < diag_ctx->l_num)
goto next_listen;

if (!ctx || strcmp(inet_csk(sk)->icsk_ulp_ops->name, "mptcp"))
goto next_listen;

sk = ctx->conn;
if (!sk || !net_eq(sock_net(sk), net))
goto next_listen;

if (r->sdiag_family != AF_UNSPEC &&
sk->sk_family != r->sdiag_family)
goto next_listen;

if (r->id.idiag_sport != inet->inet_sport &&
r->id.idiag_sport)
goto next_listen;

if (!refcount_inc_not_zero(&sk->sk_refcnt))
goto next_listen;

ret = sk_diag_dump(sk, skb, cb, r, bc, net_admin);
sock_put(sk);
if (ret < 0) {
spin_unlock(&ilb->lock);
rcu_read_unlock();
diag_ctx->l_slot = i;
diag_ctx->l_num = num;
return;
}
diag_ctx->l_num = num + 1;
num = 0;
next_listen:
++num;
}
spin_unlock(&ilb->lock);
rcu_read_unlock();

cond_resched();
diag_ctx->l_num = 0;
}

diag_ctx->l_num = 0;
diag_ctx->l_slot = i;
}

static void mptcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
const struct inet_diag_req_v2 *r)
{
Expand Down Expand Up @@ -114,6 +186,9 @@ static void mptcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
}
cond_resched();
}

if ((r->idiag_states & TCPF_LISTEN) && r->id.idiag_dport == 0)
mptcp_diag_dump_listeners(skb, cb, r, net_admin);
}

static void mptcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
Expand All @@ -124,6 +199,19 @@ static void mptcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,

r->idiag_rqueue = sk_rmem_alloc_get(sk);
r->idiag_wqueue = sk_wmem_alloc_get(sk);

if (inet_sk_state_load(sk) == TCP_LISTEN) {
struct sock *lsk = READ_ONCE(msk->first);

if (lsk) {
/* override with settings from tcp listener,
* so Send-Q will show accept queue.
*/
r->idiag_rqueue = READ_ONCE(lsk->sk_ack_backlog);
r->idiag_wqueue = READ_ONCE(lsk->sk_max_ack_backlog);
}
}

if (!info)
return;

Expand Down

0 comments on commit 6ef1c53

Please sign in to comment.