Commit 640cb088 authored by Chengchang Tang's avatar Chengchang Tang Committed by Juan Zhou
Browse files

RDMA/hns: Add debugfs support for DCA

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I9CK0O



--------------------------------------------------------------------------

This patch synchonize DCA code from CI and is based on RFC v2 from the
community including DCA kernel suuport and debugfs support.

Add a group of debugfs files for DCA memory pool statistics.

The debugfs entries for DCA memory statistics include:
hns_roce/<ibdev_name>/dca/qp : show all DCA QPs for each device.
hns_roce/<ibdev_name>/dca/pool : show all DCA mem for each device.
hns_roce/<ibdev_name>/<pid>/qp : show all active DCA QPs for one process.
hns_roce/<ibdev_name>/<pid>/mstats : show DCA mem info for one process.

Signed-off-by: default avatarChengchang Tang <tangchengchang@huawei.com>
Reviewed-by: default avatarYangyang Li <liyangyang20@huawei.com>
Reviewed-by: default avatarYueHaibing <yuehaibing@huawei.com>
Signed-off-by: default avatarJuan Zhou <zhoujuan51@h-partners.com>
parent ef35d79d
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -1620,6 +1620,32 @@ const struct uapi_definition hns_roce_dca_uapi_defs[] = {
	{}
};

/* enum DCA pool */
struct dca_mem_enum_attr {
	void *param;
	hns_dca_enum_callback enum_fn;
};

static int enum_dca_pool_proc(struct dca_mem *mem, int index, void *param)
{
	struct dca_mem_enum_attr *attr = param;
	int ret;

	ret = attr->enum_fn(mem->states, mem->page_count, attr->param);

	return ret ? DCA_MEM_STOP_ITERATE : DCA_MEM_NEXT_ITERATE;
}

void hns_roce_enum_dca_pool(struct hns_roce_dca_ctx *dca_ctx, void *param,
			    hns_dca_enum_callback cb)
{
	struct dca_mem_enum_attr attr;

	attr.enum_fn = cb;
	attr.param = param;
	travel_dca_pages(dca_ctx, &attr, enum_dca_pool_proc);
}

module_param(dca_unit_size, uint, 0444);
module_param(dca_max_size, ulong, 0444);
module_param(dca_min_size, ulong, 0444);
+2 −0
Original line number Diff line number Diff line
@@ -73,4 +73,6 @@ void hns_roce_dca_detach(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp,
void hns_roce_dca_kick(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp,
		       struct ib_udata *udata);

void hns_roce_enum_dca_pool(struct hns_roce_dca_ctx *dca_ctx, void *param,
			    hns_dca_enum_callback cb);
#endif
+405 −0
Original line number Diff line number Diff line
@@ -7,9 +7,13 @@
#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;
@@ -81,6 +85,404 @@ static void create_sw_stat_debugfs(struct hns_roce_dev *hr_dev,
			     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_DCA) || !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)
{
@@ -89,6 +491,9 @@ void hns_roce_register_debugfs(struct hns_roce_dev *hr_dev)
	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);
}

+19 −0
Original line number Diff line number Diff line
@@ -17,17 +17,36 @@ struct hns_sw_stat_debugfs {
	struct hns_debugfs_seqfile sw_stat;
};

/* DCA debugfs */
struct hns_dca_ctx_debugfs {
	struct dentry *root; /* pool debugfs entry */
	struct hns_debugfs_seqfile mem; /* mems in pool */
	struct hns_debugfs_seqfile qp; /* QPs stats in pool */
};

struct hns_dca_debugfs {
	struct dentry *root; /* dev debugfs entry */
	struct hns_debugfs_seqfile pool; /* pools stats on device */
	struct hns_debugfs_seqfile qp; /* QPs stats on device */
	struct hns_dca_ctx_debugfs kctx; /* kDCA context */
};

/* Debugfs for device */
struct hns_roce_dev_debugfs {
	struct dentry *root;
	struct hns_sw_stat_debugfs sw_stat_root;
	struct hns_dca_debugfs dca_root;
};

struct hns_roce_dev;
struct hns_roce_ucontext;

void hns_roce_init_debugfs(void);
void hns_roce_cleanup_debugfs(void);
void hns_roce_register_debugfs(struct hns_roce_dev *hr_dev);
void hns_roce_unregister_debugfs(struct hns_roce_dev *hr_dev);
void hns_roce_register_uctx_debugfs(struct hns_roce_dev *hr_dev,
				    struct hns_roce_ucontext *uctx);
void hns_roce_unregister_uctx_debugfs(struct hns_roce_ucontext *uctx);

#endif
+4 −1
Original line number Diff line number Diff line
@@ -237,6 +237,9 @@ struct hns_roce_ucontext {
	struct hns_user_mmap_entry *reset_mmap_entry;
	u32			config;
	struct hns_roce_dca_ctx	dca_ctx;
	struct list_head list; /* link all uctx to uctx_list on hr_dev */
	pid_t pid; /* process id to which the uctx belongs */
	struct hns_dca_ctx_debugfs dca_dbgfs;
};

struct hns_roce_pd {
@@ -1037,7 +1040,7 @@ struct hns_roce_dev {
	struct device		*dev;

	struct list_head	uctx_list; /* list of all uctx on this dev */
	spinlock_t		uctx_list_lock; /* protect @uctx_list */
	struct mutex		uctx_list_mutex; /* protect @uctx_list */

	struct hns_roce_uar     priv_uar;
	const char		*irq_names[HNS_ROCE_MAX_IRQ_NUM];
Loading