517 lines
14 KiB
C
517 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2023 Hisilicon Limited.
|
|
*/
|
|
|
|
#include <linux/debugfs.h>
|
|
#include <linux/device.h>
|
|
|
|
#include "hns_roce_device.h"
|
|
#include "hns_roce_common.h"
|
|
#include "hns_roce_dca.h"
|
|
|
|
static struct dentry *hns_roce_dbgfs_root;
|
|
|
|
#define KB 1024
|
|
|
|
static int hns_debugfs_seqfile_open(struct inode *inode, struct file *f)
|
|
{
|
|
struct hns_debugfs_seqfile *seqfile = inode->i_private;
|
|
|
|
return single_open(f, seqfile->read, seqfile->data);
|
|
}
|
|
|
|
static const struct file_operations hns_debugfs_seqfile_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = hns_debugfs_seqfile_open,
|
|
.release = single_release,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek
|
|
};
|
|
|
|
static void init_debugfs_seqfile(struct hns_debugfs_seqfile *seq,
|
|
const char *name, struct dentry *parent,
|
|
int (*read_fn)(struct seq_file *, void *),
|
|
void *data)
|
|
{
|
|
debugfs_create_file(name, 0400, parent, seq, &hns_debugfs_seqfile_fops);
|
|
|
|
seq->read = read_fn;
|
|
seq->data = data;
|
|
}
|
|
|
|
static const char * const sw_stat_info[] = {
|
|
[HNS_ROCE_DFX_AEQE_CNT] = "aeqe",
|
|
[HNS_ROCE_DFX_CEQE_CNT] = "ceqe",
|
|
[HNS_ROCE_DFX_CMDS_CNT] = "cmds",
|
|
[HNS_ROCE_DFX_CMDS_ERR_CNT] = "cmds_err",
|
|
[HNS_ROCE_DFX_MBX_POSTED_CNT] = "posted_mbx",
|
|
[HNS_ROCE_DFX_MBX_POLLED_CNT] = "polled_mbx",
|
|
[HNS_ROCE_DFX_MBX_EVENT_CNT] = "mbx_event",
|
|
[HNS_ROCE_DFX_QP_CREATE_ERR_CNT] = "qp_create_err",
|
|
[HNS_ROCE_DFX_QP_MODIFY_ERR_CNT] = "qp_modify_err",
|
|
[HNS_ROCE_DFX_CQ_CREATE_ERR_CNT] = "cq_create_err",
|
|
[HNS_ROCE_DFX_CQ_MODIFY_ERR_CNT] = "cq_modify_err",
|
|
[HNS_ROCE_DFX_SRQ_CREATE_ERR_CNT] = "srq_create_err",
|
|
[HNS_ROCE_DFX_SRQ_MODIFY_ERR_CNT] = "srq_modify_err",
|
|
[HNS_ROCE_DFX_XRCD_ALLOC_ERR_CNT] = "xrcd_alloc_err",
|
|
[HNS_ROCE_DFX_MR_REG_ERR_CNT] = "mr_reg_err",
|
|
[HNS_ROCE_DFX_MR_REREG_ERR_CNT] = "mr_rereg_err",
|
|
[HNS_ROCE_DFX_AH_CREATE_ERR_CNT] = "ah_create_err",
|
|
[HNS_ROCE_DFX_MMAP_ERR_CNT] = "mmap_err",
|
|
[HNS_ROCE_DFX_UCTX_ALLOC_ERR_CNT] = "uctx_alloc_err",
|
|
};
|
|
|
|
static int sw_stat_debugfs_show(struct seq_file *file, void *offset)
|
|
{
|
|
struct hns_roce_dev *hr_dev = file->private;
|
|
int i;
|
|
|
|
for (i = 0; i < HNS_ROCE_DFX_CNT_TOTAL; i++)
|
|
seq_printf(file, "%-20s --- %lld\n", sw_stat_info[i],
|
|
atomic64_read(&hr_dev->dfx_cnt[i]));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void create_sw_stat_debugfs(struct hns_roce_dev *hr_dev,
|
|
struct dentry *parent)
|
|
{
|
|
struct hns_sw_stat_debugfs *dbgfs = &hr_dev->dbgfs.sw_stat_root;
|
|
|
|
dbgfs->root = debugfs_create_dir("sw_stat", parent);
|
|
|
|
init_debugfs_seqfile(&dbgfs->sw_stat, "sw_stat", dbgfs->root,
|
|
sw_stat_debugfs_show, hr_dev);
|
|
}
|
|
|
|
struct dca_mem_stats {
|
|
unsigned int total_mems;
|
|
unsigned int clean_mems;
|
|
size_t free_size;
|
|
size_t total_size;
|
|
size_t active_size;
|
|
size_t locked_size;
|
|
};
|
|
|
|
#define DCA_CTX_PID_LEN 10
|
|
|
|
#define DCA_CTX_STATE_LEN 22
|
|
|
|
#define LOADING_PERCENT_SCALE 100
|
|
|
|
#define LOADING_PERCENT_SHIFT 2
|
|
|
|
static int stats_dca_pool_proc(struct hns_dca_page_state *states, u32 count,
|
|
void *param)
|
|
{
|
|
struct dca_mem_stats *stats = param;
|
|
struct hns_dca_page_state *s;
|
|
int i, free_pages;
|
|
|
|
free_pages = 0;
|
|
for (i = 0; i < count; i++) {
|
|
s = &states[i];
|
|
if (s->buf_id == HNS_DCA_INVALID_BUF_ID) {
|
|
free_pages++;
|
|
stats->free_size += HNS_HW_PAGE_SIZE;
|
|
} else {
|
|
if (s->lock)
|
|
stats->locked_size += HNS_HW_PAGE_SIZE;
|
|
|
|
if (s->active)
|
|
stats->active_size += HNS_HW_PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
stats->total_size += (count * HNS_HW_PAGE_SIZE);
|
|
stats->total_mems++;
|
|
if (free_pages == count)
|
|
stats->clean_mems++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* stats QPs in DCA pool */
|
|
struct dca_stats_qp_attr {
|
|
unsigned long *qpn_bitmap;
|
|
unsigned int qpn_max;
|
|
};
|
|
|
|
static int stats_dca_qp_proc(struct hns_dca_page_state *states, u32 count,
|
|
void *param)
|
|
{
|
|
struct dca_stats_qp_attr *attr = param;
|
|
struct hns_dca_page_state *s;
|
|
u32 qpn;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
s = &states[i];
|
|
if (s->buf_id == HNS_DCA_INVALID_BUF_ID || s->lock ||
|
|
!s->active)
|
|
continue;
|
|
|
|
qpn = HNS_DCA_BUF_ID_TO_QPN(s->buf_id);
|
|
if (qpn < attr->qpn_max)
|
|
set_bit(qpn, attr->qpn_bitmap);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dca_ctx_stats_qp(struct hns_roce_dca_ctx *ctx,
|
|
unsigned long *qpn_bitmap, unsigned int qpn_max)
|
|
{
|
|
struct dca_stats_qp_attr attr;
|
|
|
|
attr.qpn_bitmap = qpn_bitmap;
|
|
attr.qpn_max = qpn_max;
|
|
hns_roce_enum_dca_pool(ctx, &attr, stats_dca_qp_proc);
|
|
}
|
|
|
|
static void dca_ctx_stats_mem(struct hns_roce_dca_ctx *ctx,
|
|
struct dca_mem_stats *stats)
|
|
{
|
|
hns_roce_enum_dca_pool(ctx, stats, stats_dca_pool_proc);
|
|
}
|
|
|
|
static void dca_setup_pool_name(pid_t pid, bool is_kdca, char *name, int size)
|
|
{
|
|
if (is_kdca)
|
|
snprintf(name, size, "kernel");
|
|
else
|
|
snprintf(name, size, "%d", pid);
|
|
}
|
|
|
|
static u64 calc_loading_percent(size_t total, size_t free, u32 *out_rem)
|
|
{
|
|
u32 all_pages, used_pages, free_pages, scale;
|
|
u64 percent = 0;
|
|
u32 rem = 0;
|
|
|
|
all_pages = total >> HNS_HW_PAGE_SHIFT;
|
|
free_pages = free >> HNS_HW_PAGE_SHIFT;
|
|
if (all_pages >= free_pages) {
|
|
used_pages = all_pages - free_pages;
|
|
scale = LOADING_PERCENT_SCALE * LOADING_PERCENT_SCALE;
|
|
percent = (used_pages * scale) / all_pages;
|
|
percent = div_u64_rem(percent, LOADING_PERCENT_SCALE, &rem);
|
|
}
|
|
|
|
if (out_rem)
|
|
*out_rem = rem;
|
|
|
|
return percent;
|
|
}
|
|
|
|
static void dca_print_pool_stats(struct hns_roce_dca_ctx *ctx, pid_t pid,
|
|
bool is_kdca, struct seq_file *file)
|
|
{
|
|
char name[DCA_CTX_PID_LEN];
|
|
u64 percent;
|
|
u32 rem = 0;
|
|
|
|
percent = calc_loading_percent(ctx->total_size, ctx->free_size, &rem);
|
|
dca_setup_pool_name(pid, is_kdca, name, sizeof(name));
|
|
seq_printf(file, "%-10s %-16ld %-16ld %-16u %llu.%0*u\n", name,
|
|
ctx->total_size / KB, ctx->free_size / KB, ctx->free_mems,
|
|
percent, LOADING_PERCENT_SHIFT, rem);
|
|
}
|
|
|
|
static void dca_stats_dev_pool_in_seqfile(struct hns_roce_dev *hr_dev,
|
|
struct seq_file *file)
|
|
{
|
|
struct hns_roce_ucontext *uctx, *tmp;
|
|
|
|
seq_printf(file, "%-10s %-16s %-16s %-16s %-s\n", "PID", "Total(kB)",
|
|
"Free(kB)", "Clean(BLK)", "Loading");
|
|
|
|
/* Write kernel DCA pool stats */
|
|
dca_print_pool_stats(&hr_dev->dca_ctx, 0, true, file);
|
|
/* Write user DCA pool stats */
|
|
mutex_lock(&hr_dev->uctx_list_mutex);
|
|
list_for_each_entry_safe(uctx, tmp, &hr_dev->uctx_list, list) {
|
|
dca_print_pool_stats(&uctx->dca_ctx, uctx->pid, false, file);
|
|
}
|
|
mutex_unlock(&hr_dev->uctx_list_mutex);
|
|
}
|
|
|
|
struct dca_qp_stats {
|
|
char name[DCA_CTX_PID_LEN];
|
|
char state[DCA_CTX_STATE_LEN];
|
|
u32 qpn;
|
|
u32 total_size;
|
|
u32 sq_size;
|
|
u32 rq_size;
|
|
u32 sge_size;
|
|
};
|
|
|
|
static void dca_setup_qp_state(struct hns_roce_qp *hr_qp, char *buf, int size)
|
|
{
|
|
struct hns_roce_dca_cfg *cfg = &hr_qp->dca_cfg;
|
|
|
|
if (cfg->buf_id == HNS_DCA_INVALID_BUF_ID)
|
|
snprintf(buf, size, "detached");
|
|
else if (hr_qp->rq.wqe_cnt > 0)
|
|
snprintf(buf, size, "stable");
|
|
else
|
|
snprintf(buf, size, "attached-%-u", cfg->attach_count);
|
|
}
|
|
|
|
static void dca_setup_qp_stats(struct hns_roce_qp *hr_qp,
|
|
struct dca_qp_stats *stats)
|
|
{
|
|
struct hns_roce_ucontext *uctx = NULL;
|
|
|
|
if (!(hr_qp->en_flags & HNS_ROCE_QP_CAP_DYNAMIC_CTX_ATTACH) ||
|
|
!hr_qp->ibqp.pd)
|
|
return;
|
|
|
|
if (hr_qp->ibqp.pd->uobject)
|
|
uctx = to_hr_ucontext(hr_qp->ibqp.pd->uobject->context);
|
|
|
|
dca_setup_pool_name(uctx ? uctx->pid : 0, !uctx, stats->name,
|
|
sizeof(stats->name));
|
|
stats->qpn = (u32)hr_qp->qpn;
|
|
stats->total_size = hr_qp->buff_size;
|
|
|
|
stats->sq_size = to_hr_hem_entries_size(hr_qp->sq.wqe_cnt,
|
|
hr_qp->sq.wqe_shift);
|
|
stats->sge_size = to_hr_hem_entries_size(hr_qp->sge.sge_cnt,
|
|
hr_qp->sge.sge_shift);
|
|
stats->rq_size = to_hr_hem_entries_size(hr_qp->rq.wqe_cnt,
|
|
hr_qp->rq.wqe_shift);
|
|
|
|
dca_setup_qp_state(hr_qp, stats->state, sizeof(stats->state));
|
|
}
|
|
|
|
static void dca_stats_dev_qp_in_seqfile(struct hns_roce_dev *hr_dev,
|
|
struct seq_file *file)
|
|
{
|
|
struct dca_qp_stats stats;
|
|
struct hns_roce_qp *hr_qp;
|
|
unsigned long id;
|
|
|
|
seq_printf(file, "%-10s %-10s %-10s %s\n", "QPN", "Size(kB)", "PID",
|
|
"State");
|
|
|
|
xa_lock_irq(&hr_dev->qp_table_xa);
|
|
xa_for_each(&hr_dev->qp_table_xa, id, hr_qp) {
|
|
stats.total_size = 0;
|
|
dca_setup_qp_stats(hr_qp, &stats);
|
|
if (!stats.total_size)
|
|
continue;
|
|
|
|
xa_unlock_irq(&hr_dev->qp_table_xa);
|
|
seq_printf(file, "%-10u %-10u %-10s %-s\n", stats.qpn,
|
|
stats.total_size / KB, stats.name, stats.state);
|
|
xa_lock_irq(&hr_dev->qp_table_xa);
|
|
}
|
|
xa_unlock_irq(&hr_dev->qp_table_xa);
|
|
}
|
|
|
|
static void dca_stats_ctx_qp_in_seqfile(struct hns_roce_dev *hr_dev,
|
|
struct hns_roce_dca_ctx *ctx,
|
|
struct seq_file *file)
|
|
{
|
|
struct dca_qp_stats stats;
|
|
struct hns_roce_qp *hr_qp;
|
|
unsigned int qpn, nbits;
|
|
unsigned long *bitmap;
|
|
|
|
nbits = hr_dev->caps.num_qps;
|
|
if (nbits < 1)
|
|
return;
|
|
|
|
bitmap = bitmap_zalloc(nbits, GFP_ATOMIC);
|
|
if (!bitmap)
|
|
return;
|
|
|
|
seq_printf(file, "%-10s %-10s %-10s %-10s %-10s\n", "QPN", "Total(kB)",
|
|
"SQ(kB)", "SGE(kB)", "RQ(kB)");
|
|
|
|
dca_ctx_stats_qp(ctx, bitmap, nbits);
|
|
for_each_set_bit(qpn, bitmap, nbits) {
|
|
stats.total_size = 0;
|
|
xa_lock_irq(&hr_dev->qp_table_xa);
|
|
hr_qp = __hns_roce_qp_lookup(hr_dev, qpn);
|
|
if (hr_qp)
|
|
dca_setup_qp_stats(hr_qp, &stats);
|
|
xa_unlock_irq(&hr_dev->qp_table_xa);
|
|
if (!stats.total_size)
|
|
continue;
|
|
|
|
seq_printf(file, "%-10u %-10u %-10u %-10u %-10u\n",
|
|
stats.qpn, stats.total_size / KB, stats.sq_size / KB,
|
|
stats.sge_size / KB, stats.rq_size / KB);
|
|
}
|
|
bitmap_free(bitmap);
|
|
}
|
|
|
|
static void dca_stats_ctx_mem_in_seqfile(struct hns_roce_dca_ctx *ctx,
|
|
bool is_kdca, struct seq_file *file)
|
|
{
|
|
struct dca_mem_stats stats = {};
|
|
u64 percent;
|
|
u32 rem = 0;
|
|
|
|
#define DCA_STAT_NAME_FMT "%-22s "
|
|
#define dca_ctx_print_mem_size(f, n, fmt, v) \
|
|
seq_printf(f, DCA_STAT_NAME_FMT fmt "\n", n, v)
|
|
|
|
#define dca_ctx_print_mem_kb(f, n, v) \
|
|
dca_ctx_print_mem_size(f, n, "%-u kB", (u32)((v) / KB))
|
|
|
|
dca_ctx_stats_mem(ctx, &stats);
|
|
percent = calc_loading_percent(stats.total_size, stats.free_size, &rem);
|
|
seq_printf(file, DCA_STAT_NAME_FMT "%llu.%0*u\n", "Loading:", percent,
|
|
LOADING_PERCENT_SHIFT, rem);
|
|
dca_ctx_print_mem_kb(file, "Total:", stats.total_size);
|
|
dca_ctx_print_mem_kb(file, "Free:", stats.free_size);
|
|
dca_ctx_print_mem_kb(file, "Active:", stats.active_size);
|
|
dca_ctx_print_mem_kb(file, "Locked:", stats.locked_size);
|
|
dca_ctx_print_mem_size(file, "Dirty:", "%-u Blocks",
|
|
stats.total_mems - stats.clean_mems);
|
|
dca_ctx_print_mem_size(file, "Clean:", "%-u Blocks", stats.clean_mems);
|
|
if (is_kdca) {
|
|
dca_ctx_print_mem_size(file, "Unit:", "%-u", ctx->unit_size);
|
|
dca_ctx_print_mem_size(file, "Max:", "%-zu", ctx->max_size);
|
|
dca_ctx_print_mem_size(file, "Min:", "%-zu", ctx->min_size);
|
|
}
|
|
}
|
|
|
|
static int dca_debugfs_pool_show(struct seq_file *file, void *offset)
|
|
{
|
|
struct hns_roce_dev *hr_dev = file->private;
|
|
|
|
dca_stats_dev_pool_in_seqfile(hr_dev, file);
|
|
return 0;
|
|
}
|
|
|
|
static int dca_debugfs_qp_show(struct seq_file *file, void *offset)
|
|
{
|
|
struct hns_roce_dev *hr_dev = file->private;
|
|
|
|
dca_stats_dev_qp_in_seqfile(hr_dev, file);
|
|
return 0;
|
|
}
|
|
|
|
static int dca_debugfs_kctx_qp_stats_show(struct seq_file *file, void *offset)
|
|
{
|
|
struct hns_roce_dev *hr_dev = file->private;
|
|
|
|
dca_stats_ctx_qp_in_seqfile(hr_dev, &hr_dev->dca_ctx, file);
|
|
return 0;
|
|
}
|
|
|
|
static int dca_debugfs_uctx_qp_stats_show(struct seq_file *file, void *offset)
|
|
{
|
|
struct hns_roce_ucontext *uctx = file->private;
|
|
|
|
dca_stats_ctx_qp_in_seqfile(to_hr_dev(uctx->ibucontext.device),
|
|
&uctx->dca_ctx, file);
|
|
return 0;
|
|
}
|
|
|
|
static int dca_debugfs_kctx_mem_stats_show(struct seq_file *file, void *offset)
|
|
{
|
|
struct hns_roce_dev *hr_dev = file->private;
|
|
|
|
dca_stats_ctx_mem_in_seqfile(&hr_dev->dca_ctx, true, file);
|
|
return 0;
|
|
}
|
|
|
|
static int dca_debugfs_uctx_mem_stats_show(struct seq_file *file, void *offset)
|
|
{
|
|
struct hns_roce_ucontext *uctx = file->private;
|
|
|
|
dca_stats_ctx_mem_in_seqfile(&uctx->dca_ctx, false, file);
|
|
return 0;
|
|
}
|
|
|
|
static void init_dca_ctx_debugfs(struct hns_dca_ctx_debugfs *dbgfs,
|
|
struct dentry *parent,
|
|
struct hns_roce_dev *hr_dev,
|
|
struct hns_roce_ucontext *uctx)
|
|
{
|
|
char name[DCA_CTX_PID_LEN];
|
|
|
|
dca_setup_pool_name(uctx ? uctx->pid : 0, !uctx, name, sizeof(name));
|
|
dbgfs->root = debugfs_create_dir(name, parent);
|
|
|
|
if (uctx) {
|
|
init_debugfs_seqfile(&dbgfs->mem, "mstats", dbgfs->root,
|
|
dca_debugfs_uctx_mem_stats_show, uctx);
|
|
init_debugfs_seqfile(&dbgfs->qp, "qp", dbgfs->root,
|
|
dca_debugfs_uctx_qp_stats_show, uctx);
|
|
} else {
|
|
init_debugfs_seqfile(&dbgfs->mem, "mstats", dbgfs->root,
|
|
dca_debugfs_kctx_mem_stats_show, hr_dev);
|
|
init_debugfs_seqfile(&dbgfs->qp, "qp", dbgfs->root,
|
|
dca_debugfs_kctx_qp_stats_show, hr_dev);
|
|
}
|
|
}
|
|
|
|
static void create_dca_debugfs(struct hns_roce_dev *hr_dev,
|
|
struct dentry *parent)
|
|
{
|
|
struct hns_dca_debugfs *dbgfs = &hr_dev->dbgfs.dca_root;
|
|
|
|
dbgfs->root = debugfs_create_dir("dca", parent);
|
|
|
|
init_debugfs_seqfile(&dbgfs->pool, "pool", dbgfs->root,
|
|
dca_debugfs_pool_show, hr_dev);
|
|
init_debugfs_seqfile(&dbgfs->qp, "qp", dbgfs->root,
|
|
dca_debugfs_qp_show, hr_dev);
|
|
|
|
init_dca_ctx_debugfs(&dbgfs->kctx, dbgfs->root, hr_dev, NULL);
|
|
}
|
|
|
|
/* debugfs for ucontext */
|
|
void hns_roce_register_uctx_debugfs(struct hns_roce_dev *hr_dev,
|
|
struct hns_roce_ucontext *uctx)
|
|
{
|
|
struct hns_dca_debugfs *dca_dbgfs = &hr_dev->dbgfs.dca_root;
|
|
|
|
if (uctx->config & HNS_ROCE_UCTX_CONFIG_DCA)
|
|
init_dca_ctx_debugfs(&uctx->dca_dbgfs, dca_dbgfs->root,
|
|
hr_dev, uctx);
|
|
}
|
|
|
|
void hns_roce_unregister_uctx_debugfs(struct hns_roce_ucontext *uctx)
|
|
{
|
|
debugfs_remove_recursive(uctx->dca_dbgfs.root);
|
|
}
|
|
|
|
/* debugfs for device */
|
|
void hns_roce_register_debugfs(struct hns_roce_dev *hr_dev)
|
|
{
|
|
struct hns_roce_dev_debugfs *dbgfs = &hr_dev->dbgfs;
|
|
|
|
dbgfs->root = debugfs_create_dir(dev_name(&hr_dev->ib_dev.dev),
|
|
hns_roce_dbgfs_root);
|
|
|
|
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_DCA_MODE)
|
|
create_dca_debugfs(hr_dev, dbgfs->root);
|
|
|
|
create_sw_stat_debugfs(hr_dev, dbgfs->root);
|
|
}
|
|
|
|
void hns_roce_unregister_debugfs(struct hns_roce_dev *hr_dev)
|
|
{
|
|
debugfs_remove_recursive(hr_dev->dbgfs.root);
|
|
}
|
|
|
|
/* debugfs for hns module */
|
|
void hns_roce_init_debugfs(void)
|
|
{
|
|
hns_roce_dbgfs_root = debugfs_create_dir("hns_roce", NULL);
|
|
}
|
|
|
|
void hns_roce_cleanup_debugfs(void)
|
|
{
|
|
debugfs_remove_recursive(hns_roce_dbgfs_root);
|
|
hns_roce_dbgfs_root = NULL;
|
|
}
|