18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2019-2020, Mellanox Technologies Ltd. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <uapi/rdma/rdma_netlink.h>
78c2ecf20Sopenharmony_ci#include <linux/mlx5/rsc_dump.h>
88c2ecf20Sopenharmony_ci#include <rdma/ib_umem_odp.h>
98c2ecf20Sopenharmony_ci#include <rdma/restrack.h>
108c2ecf20Sopenharmony_ci#include "mlx5_ib.h"
118c2ecf20Sopenharmony_ci#include "restrack.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define MAX_DUMP_SIZE 1024
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic int dump_rsc(struct mlx5_core_dev *dev, enum mlx5_sgmt_type type,
168c2ecf20Sopenharmony_ci		    int index, void *data, int *data_len)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	struct mlx5_core_dev *mdev = dev;
198c2ecf20Sopenharmony_ci	struct mlx5_rsc_dump_cmd *cmd;
208c2ecf20Sopenharmony_ci	struct mlx5_rsc_key key = {};
218c2ecf20Sopenharmony_ci	struct page *page;
228c2ecf20Sopenharmony_ci	int offset = 0;
238c2ecf20Sopenharmony_ci	int err = 0;
248c2ecf20Sopenharmony_ci	int cmd_err;
258c2ecf20Sopenharmony_ci	int size;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	page = alloc_page(GFP_KERNEL);
288c2ecf20Sopenharmony_ci	if (!page)
298c2ecf20Sopenharmony_ci		return -ENOMEM;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	key.size = PAGE_SIZE;
328c2ecf20Sopenharmony_ci	key.rsc = type;
338c2ecf20Sopenharmony_ci	key.index1 = index;
348c2ecf20Sopenharmony_ci	key.num_of_obj1 = 1;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	cmd = mlx5_rsc_dump_cmd_create(mdev, &key);
378c2ecf20Sopenharmony_ci	if (IS_ERR(cmd)) {
388c2ecf20Sopenharmony_ci		err = PTR_ERR(cmd);
398c2ecf20Sopenharmony_ci		goto free_page;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	do {
438c2ecf20Sopenharmony_ci		cmd_err = mlx5_rsc_dump_next(mdev, cmd, page, &size);
448c2ecf20Sopenharmony_ci		if (cmd_err < 0 || size + offset > MAX_DUMP_SIZE) {
458c2ecf20Sopenharmony_ci			err = cmd_err;
468c2ecf20Sopenharmony_ci			goto destroy_cmd;
478c2ecf20Sopenharmony_ci		}
488c2ecf20Sopenharmony_ci		memcpy(data + offset, page_address(page), size);
498c2ecf20Sopenharmony_ci		offset += size;
508c2ecf20Sopenharmony_ci	} while (cmd_err > 0);
518c2ecf20Sopenharmony_ci	*data_len = offset;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cidestroy_cmd:
548c2ecf20Sopenharmony_ci	mlx5_rsc_dump_cmd_destroy(cmd);
558c2ecf20Sopenharmony_cifree_page:
568c2ecf20Sopenharmony_ci	__free_page(page);
578c2ecf20Sopenharmony_ci	return err;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int fill_res_raw(struct sk_buff *msg, struct mlx5_ib_dev *dev,
618c2ecf20Sopenharmony_ci			enum mlx5_sgmt_type type, u32 key)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int len = 0;
648c2ecf20Sopenharmony_ci	void *data;
658c2ecf20Sopenharmony_ci	int err;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	data = kzalloc(MAX_DUMP_SIZE, GFP_KERNEL);
688c2ecf20Sopenharmony_ci	if (!data)
698c2ecf20Sopenharmony_ci		return -ENOMEM;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	err = dump_rsc(dev->mdev, type, key, data, &len);
728c2ecf20Sopenharmony_ci	if (err)
738c2ecf20Sopenharmony_ci		goto out;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	err = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, len, data);
768c2ecf20Sopenharmony_ciout:
778c2ecf20Sopenharmony_ci	kfree(data);
788c2ecf20Sopenharmony_ci	return err;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int fill_stat_mr_entry(struct sk_buff *msg, struct ib_mr *ibmr)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct mlx5_ib_mr *mr = to_mmr(ibmr);
848c2ecf20Sopenharmony_ci	struct nlattr *table_attr;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (!(mr->access_flags & IB_ACCESS_ON_DEMAND))
878c2ecf20Sopenharmony_ci		return 0;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	table_attr = nla_nest_start(msg,
908c2ecf20Sopenharmony_ci				    RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (!table_attr)
938c2ecf20Sopenharmony_ci		goto err;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (rdma_nl_stat_hwcounter_entry(msg, "page_faults",
968c2ecf20Sopenharmony_ci					 atomic64_read(&mr->odp_stats.faults)))
978c2ecf20Sopenharmony_ci		goto err_table;
988c2ecf20Sopenharmony_ci	if (rdma_nl_stat_hwcounter_entry(
998c2ecf20Sopenharmony_ci		    msg, "page_invalidations",
1008c2ecf20Sopenharmony_ci		    atomic64_read(&mr->odp_stats.invalidations)))
1018c2ecf20Sopenharmony_ci		goto err_table;
1028c2ecf20Sopenharmony_ci	if (rdma_nl_stat_hwcounter_entry(msg, "page_prefetch",
1038c2ecf20Sopenharmony_ci					 atomic64_read(&mr->odp_stats.prefetch)))
1048c2ecf20Sopenharmony_ci		goto err_table;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	nla_nest_end(msg, table_attr);
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cierr_table:
1108c2ecf20Sopenharmony_ci	nla_nest_cancel(msg, table_attr);
1118c2ecf20Sopenharmony_cierr:
1128c2ecf20Sopenharmony_ci	return -EMSGSIZE;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ibmr)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct mlx5_ib_mr *mr = to_mmr(ibmr);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return fill_res_raw(msg, mr->dev, MLX5_SGMT_TYPE_PRM_QUERY_MKEY,
1208c2ecf20Sopenharmony_ci			    mlx5_mkey_to_idx(mr->mmkey.key));
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int fill_res_mr_entry(struct sk_buff *msg, struct ib_mr *ibmr)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct mlx5_ib_mr *mr = to_mmr(ibmr);
1268c2ecf20Sopenharmony_ci	struct nlattr *table_attr;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (!(mr->access_flags & IB_ACCESS_ON_DEMAND))
1298c2ecf20Sopenharmony_ci		return 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_DRIVER);
1328c2ecf20Sopenharmony_ci	if (!table_attr)
1338c2ecf20Sopenharmony_ci		goto err;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (mr->is_odp_implicit) {
1368c2ecf20Sopenharmony_ci		if (rdma_nl_put_driver_string(msg, "odp", "implicit"))
1378c2ecf20Sopenharmony_ci			goto err;
1388c2ecf20Sopenharmony_ci	} else {
1398c2ecf20Sopenharmony_ci		if (rdma_nl_put_driver_string(msg, "odp", "explicit"))
1408c2ecf20Sopenharmony_ci			goto err;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	nla_nest_end(msg, table_attr);
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cierr:
1478c2ecf20Sopenharmony_ci	nla_nest_cancel(msg, table_attr);
1488c2ecf20Sopenharmony_ci	return -EMSGSIZE;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ibcq)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct mlx5_ib_dev *dev = to_mdev(ibcq->device);
1548c2ecf20Sopenharmony_ci	struct mlx5_ib_cq *cq = to_mcq(ibcq);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return fill_res_raw(msg, dev, MLX5_SGMT_TYPE_PRM_QUERY_CQ, cq->mcq.cqn);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int fill_res_qp_entry_raw(struct sk_buff *msg, struct ib_qp *ibqp)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return fill_res_raw(msg, dev, MLX5_SGMT_TYPE_PRM_QUERY_QP,
1648c2ecf20Sopenharmony_ci			    ibqp->qp_num);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic const struct ib_device_ops restrack_ops = {
1688c2ecf20Sopenharmony_ci	.fill_res_cq_entry_raw = fill_res_cq_entry_raw,
1698c2ecf20Sopenharmony_ci	.fill_res_mr_entry = fill_res_mr_entry,
1708c2ecf20Sopenharmony_ci	.fill_res_mr_entry_raw = fill_res_mr_entry_raw,
1718c2ecf20Sopenharmony_ci	.fill_res_qp_entry_raw = fill_res_qp_entry_raw,
1728c2ecf20Sopenharmony_ci	.fill_stat_mr_entry = fill_stat_mr_entry,
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ciint mlx5_ib_restrack_init(struct mlx5_ib_dev *dev)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	ib_set_device_ops(&dev->ib_dev, &restrack_ops);
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
180