linux/net/smc/smc_stats.c
Wen Gu e0d103542b net/smc: introduce statistics for ringbufs usage of net namespace
The buffer size histograms in smc_stats, namely rx/tx_rmbsize, record
the sizes of ringbufs for all connections that have ever appeared in
the net namespace. They are incremental and we cannot know the actual
ringbufs usage from these. So here introduces statistics for current
ringbufs usage of existing smc connections in the net namespace into
smc_stats, it will be incremented when new connection uses a ringbuf
and decremented when the ringbuf is unused.

Signed-off-by: Wen Gu <guwen@linux.alibaba.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2024-08-20 11:38:23 +02:00

420 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Shared Memory Communications over RDMA (SMC-R) and RoCE
*
* SMC statistics netlink routines
*
* Copyright IBM Corp. 2021
*
* Author(s): Guvenc Gulce
*/
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/percpu.h>
#include <linux/ctype.h>
#include <linux/smc.h>
#include <net/genetlink.h>
#include <net/sock.h>
#include "smc_netlink.h"
#include "smc_stats.h"
int smc_stats_init(struct net *net)
{
net->smc.fback_rsn = kzalloc(sizeof(*net->smc.fback_rsn), GFP_KERNEL);
if (!net->smc.fback_rsn)
goto err_fback;
net->smc.smc_stats = alloc_percpu(struct smc_stats);
if (!net->smc.smc_stats)
goto err_stats;
mutex_init(&net->smc.mutex_fback_rsn);
return 0;
err_stats:
kfree(net->smc.fback_rsn);
err_fback:
return -ENOMEM;
}
void smc_stats_exit(struct net *net)
{
kfree(net->smc.fback_rsn);
if (net->smc.smc_stats)
free_percpu(net->smc.smc_stats);
}
static int smc_nl_fill_stats_rmb_data(struct sk_buff *skb,
struct smc_stats *stats, int tech,
int type)
{
struct smc_stats_rmbcnt *stats_rmb_cnt;
struct nlattr *attrs;
if (type == SMC_NLA_STATS_T_TX_RMB_STATS)
stats_rmb_cnt = &stats->smc[tech].rmb_tx;
else
stats_rmb_cnt = &stats->smc[tech].rmb_rx;
attrs = nla_nest_start(skb, type);
if (!attrs)
goto errout;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_RMB_REUSE_CNT,
stats_rmb_cnt->reuse_cnt,
SMC_NLA_STATS_RMB_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_RMB_SIZE_SM_PEER_CNT,
stats_rmb_cnt->buf_size_small_peer_cnt,
SMC_NLA_STATS_RMB_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_RMB_SIZE_SM_CNT,
stats_rmb_cnt->buf_size_small_cnt,
SMC_NLA_STATS_RMB_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_RMB_FULL_PEER_CNT,
stats_rmb_cnt->buf_full_peer_cnt,
SMC_NLA_STATS_RMB_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_RMB_FULL_CNT,
stats_rmb_cnt->buf_full_cnt,
SMC_NLA_STATS_RMB_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_RMB_ALLOC_CNT,
stats_rmb_cnt->alloc_cnt,
SMC_NLA_STATS_RMB_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_RMB_DGRADE_CNT,
stats_rmb_cnt->dgrade_cnt,
SMC_NLA_STATS_RMB_PAD))
goto errattr;
nla_nest_end(skb, attrs);
return 0;
errattr:
nla_nest_cancel(skb, attrs);
errout:
return -EMSGSIZE;
}
static int smc_nl_fill_stats_bufsize_data(struct sk_buff *skb,
struct smc_stats *stats, int tech,
int type)
{
struct smc_stats_memsize *stats_pload;
struct nlattr *attrs;
if (type == SMC_NLA_STATS_T_TXPLOAD_SIZE)
stats_pload = &stats->smc[tech].tx_pd;
else if (type == SMC_NLA_STATS_T_RXPLOAD_SIZE)
stats_pload = &stats->smc[tech].rx_pd;
else if (type == SMC_NLA_STATS_T_TX_RMB_SIZE)
stats_pload = &stats->smc[tech].tx_rmbsize;
else if (type == SMC_NLA_STATS_T_RX_RMB_SIZE)
stats_pload = &stats->smc[tech].rx_rmbsize;
else
goto errout;
attrs = nla_nest_start(skb, type);
if (!attrs)
goto errout;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_8K,
stats_pload->buf[SMC_BUF_8K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_16K,
stats_pload->buf[SMC_BUF_16K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_32K,
stats_pload->buf[SMC_BUF_32K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_64K,
stats_pload->buf[SMC_BUF_64K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_128K,
stats_pload->buf[SMC_BUF_128K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_256K,
stats_pload->buf[SMC_BUF_256K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_512K,
stats_pload->buf[SMC_BUF_512K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_1024K,
stats_pload->buf[SMC_BUF_1024K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_PLOAD_G_1024K,
stats_pload->buf[SMC_BUF_G_1024K],
SMC_NLA_STATS_PLOAD_PAD))
goto errattr;
nla_nest_end(skb, attrs);
return 0;
errattr:
nla_nest_cancel(skb, attrs);
errout:
return -EMSGSIZE;
}
static int smc_nl_fill_stats_tech_data(struct sk_buff *skb,
struct smc_stats *stats, int tech)
{
struct smc_stats_tech *smc_tech;
struct nlattr *attrs;
smc_tech = &stats->smc[tech];
if (tech == SMC_TYPE_D)
attrs = nla_nest_start(skb, SMC_NLA_STATS_SMCD_TECH);
else
attrs = nla_nest_start(skb, SMC_NLA_STATS_SMCR_TECH);
if (!attrs)
goto errout;
if (smc_nl_fill_stats_rmb_data(skb, stats, tech,
SMC_NLA_STATS_T_TX_RMB_STATS))
goto errattr;
if (smc_nl_fill_stats_rmb_data(skb, stats, tech,
SMC_NLA_STATS_T_RX_RMB_STATS))
goto errattr;
if (smc_nl_fill_stats_bufsize_data(skb, stats, tech,
SMC_NLA_STATS_T_TXPLOAD_SIZE))
goto errattr;
if (smc_nl_fill_stats_bufsize_data(skb, stats, tech,
SMC_NLA_STATS_T_RXPLOAD_SIZE))
goto errattr;
if (smc_nl_fill_stats_bufsize_data(skb, stats, tech,
SMC_NLA_STATS_T_TX_RMB_SIZE))
goto errattr;
if (smc_nl_fill_stats_bufsize_data(skb, stats, tech,
SMC_NLA_STATS_T_RX_RMB_SIZE))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_CLNT_V1_SUCC,
smc_tech->clnt_v1_succ_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_CLNT_V2_SUCC,
smc_tech->clnt_v2_succ_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_SRV_V1_SUCC,
smc_tech->srv_v1_succ_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_SRV_V2_SUCC,
smc_tech->srv_v2_succ_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_RX_BYTES,
smc_tech->rx_bytes,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_TX_BYTES,
smc_tech->tx_bytes,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_uint(skb, SMC_NLA_STATS_T_RX_RMB_USAGE,
smc_tech->rx_rmbuse))
goto errattr;
if (nla_put_uint(skb, SMC_NLA_STATS_T_TX_RMB_USAGE,
smc_tech->tx_rmbuse))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_RX_CNT,
smc_tech->rx_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_TX_CNT,
smc_tech->tx_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_SENDPAGE_CNT,
0,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_CORK_CNT,
smc_tech->cork_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_NDLY_CNT,
smc_tech->ndly_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_SPLICE_CNT,
smc_tech->splice_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_T_URG_DATA_CNT,
smc_tech->urg_data_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
nla_nest_end(skb, attrs);
return 0;
errattr:
nla_nest_cancel(skb, attrs);
errout:
return -EMSGSIZE;
}
int smc_nl_get_stats(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb);
struct net *net = sock_net(skb->sk);
struct smc_stats *stats;
struct nlattr *attrs;
int cpu, i, size;
void *nlh;
u64 *src;
u64 *sum;
if (cb_ctx->pos[0])
goto errmsg;
nlh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&smc_gen_nl_family, NLM_F_MULTI,
SMC_NETLINK_GET_STATS);
if (!nlh)
goto errmsg;
attrs = nla_nest_start(skb, SMC_GEN_STATS);
if (!attrs)
goto errnest;
stats = kzalloc(sizeof(*stats), GFP_KERNEL);
if (!stats)
goto erralloc;
size = sizeof(*stats) / sizeof(u64);
for_each_possible_cpu(cpu) {
src = (u64 *)per_cpu_ptr(net->smc.smc_stats, cpu);
sum = (u64 *)stats;
for (i = 0; i < size; i++)
*(sum++) += *(src++);
}
if (smc_nl_fill_stats_tech_data(skb, stats, SMC_TYPE_D))
goto errattr;
if (smc_nl_fill_stats_tech_data(skb, stats, SMC_TYPE_R))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_CLNT_HS_ERR_CNT,
stats->clnt_hshake_err_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_STATS_SRV_HS_ERR_CNT,
stats->srv_hshake_err_cnt,
SMC_NLA_STATS_PAD))
goto errattr;
nla_nest_end(skb, attrs);
genlmsg_end(skb, nlh);
cb_ctx->pos[0] = 1;
kfree(stats);
return skb->len;
errattr:
kfree(stats);
erralloc:
nla_nest_cancel(skb, attrs);
errnest:
genlmsg_cancel(skb, nlh);
errmsg:
return skb->len;
}
static int smc_nl_get_fback_details(struct sk_buff *skb,
struct netlink_callback *cb, int pos,
bool is_srv)
{
struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb);
struct net *net = sock_net(skb->sk);
int cnt_reported = cb_ctx->pos[2];
struct smc_stats_fback *trgt_arr;
struct nlattr *attrs;
int rc = 0;
void *nlh;
if (is_srv)
trgt_arr = &net->smc.fback_rsn->srv[0];
else
trgt_arr = &net->smc.fback_rsn->clnt[0];
if (!trgt_arr[pos].fback_code)
return -ENODATA;
nlh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&smc_gen_nl_family, NLM_F_MULTI,
SMC_NETLINK_GET_FBACK_STATS);
if (!nlh)
goto errmsg;
attrs = nla_nest_start(skb, SMC_GEN_FBACK_STATS);
if (!attrs)
goto errout;
if (nla_put_u8(skb, SMC_NLA_FBACK_STATS_TYPE, is_srv))
goto errattr;
if (!cnt_reported) {
if (nla_put_u64_64bit(skb, SMC_NLA_FBACK_STATS_SRV_CNT,
net->smc.fback_rsn->srv_fback_cnt,
SMC_NLA_FBACK_STATS_PAD))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_FBACK_STATS_CLNT_CNT,
net->smc.fback_rsn->clnt_fback_cnt,
SMC_NLA_FBACK_STATS_PAD))
goto errattr;
cnt_reported = 1;
}
if (nla_put_u32(skb, SMC_NLA_FBACK_STATS_RSN_CODE,
trgt_arr[pos].fback_code))
goto errattr;
if (nla_put_u16(skb, SMC_NLA_FBACK_STATS_RSN_CNT,
trgt_arr[pos].count))
goto errattr;
cb_ctx->pos[2] = cnt_reported;
nla_nest_end(skb, attrs);
genlmsg_end(skb, nlh);
return rc;
errattr:
nla_nest_cancel(skb, attrs);
errout:
genlmsg_cancel(skb, nlh);
errmsg:
return -EMSGSIZE;
}
int smc_nl_get_fback_stats(struct sk_buff *skb, struct netlink_callback *cb)
{
struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb);
struct net *net = sock_net(skb->sk);
int rc_srv = 0, rc_clnt = 0, k;
int skip_serv = cb_ctx->pos[1];
int snum = cb_ctx->pos[0];
bool is_srv = true;
mutex_lock(&net->smc.mutex_fback_rsn);
for (k = 0; k < SMC_MAX_FBACK_RSN_CNT; k++) {
if (k < snum)
continue;
if (!skip_serv) {
rc_srv = smc_nl_get_fback_details(skb, cb, k, is_srv);
if (rc_srv && rc_srv != -ENODATA)
break;
} else {
skip_serv = 0;
}
rc_clnt = smc_nl_get_fback_details(skb, cb, k, !is_srv);
if (rc_clnt && rc_clnt != -ENODATA) {
skip_serv = 1;
break;
}
if (rc_clnt == -ENODATA && rc_srv == -ENODATA)
break;
}
mutex_unlock(&net->smc.mutex_fback_rsn);
cb_ctx->pos[1] = skip_serv;
cb_ctx->pos[0] = k;
return skb->len;
}