// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
|
/* Copyright (c) 2019 Mellanox Technologies. */
|
|
#include "rsc_dump.h"
|
#include "lib/mlx5.h"
|
|
#define MLX5_SGMT_TYPE(SGMT) MLX5_SGMT_TYPE_##SGMT
|
#define MLX5_SGMT_STR_ASSING(SGMT)[MLX5_SGMT_TYPE(SGMT)] = #SGMT
|
static const char *const mlx5_rsc_sgmt_name[] = {
|
MLX5_SGMT_STR_ASSING(HW_CQPC),
|
MLX5_SGMT_STR_ASSING(HW_SQPC),
|
MLX5_SGMT_STR_ASSING(HW_RQPC),
|
MLX5_SGMT_STR_ASSING(FULL_SRQC),
|
MLX5_SGMT_STR_ASSING(FULL_CQC),
|
MLX5_SGMT_STR_ASSING(FULL_EQC),
|
MLX5_SGMT_STR_ASSING(FULL_QPC),
|
MLX5_SGMT_STR_ASSING(SND_BUFF),
|
MLX5_SGMT_STR_ASSING(RCV_BUFF),
|
MLX5_SGMT_STR_ASSING(SRQ_BUFF),
|
MLX5_SGMT_STR_ASSING(CQ_BUFF),
|
MLX5_SGMT_STR_ASSING(EQ_BUFF),
|
MLX5_SGMT_STR_ASSING(SX_SLICE),
|
MLX5_SGMT_STR_ASSING(SX_SLICE_ALL),
|
MLX5_SGMT_STR_ASSING(RDB),
|
MLX5_SGMT_STR_ASSING(RX_SLICE_ALL),
|
MLX5_SGMT_STR_ASSING(PRM_QUERY_QP),
|
MLX5_SGMT_STR_ASSING(PRM_QUERY_CQ),
|
MLX5_SGMT_STR_ASSING(PRM_QUERY_MKEY),
|
};
|
|
struct mlx5_rsc_dump {
|
u32 pdn;
|
struct mlx5_core_mkey mkey;
|
u32 number_of_menu_items;
|
u16 fw_segment_type[MLX5_SGMT_TYPE_NUM];
|
};
|
|
struct mlx5_rsc_dump_cmd {
|
u64 mem_size;
|
u8 cmd[MLX5_ST_SZ_BYTES(resource_dump)];
|
};
|
|
static int mlx5_rsc_dump_sgmt_get_by_name(char *name)
|
{
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(mlx5_rsc_sgmt_name); i++)
|
if (!strcmp(name, mlx5_rsc_sgmt_name[i]))
|
return i;
|
|
return -EINVAL;
|
}
|
|
#define MLX5_RSC_DUMP_MENU_HEADER_SIZE (MLX5_ST_SZ_BYTES(resource_dump_info_segment) + \
|
MLX5_ST_SZ_BYTES(resource_dump_command_segment) + \
|
MLX5_ST_SZ_BYTES(resource_dump_menu_segment))
|
|
static int mlx5_rsc_dump_read_menu_sgmt(struct mlx5_rsc_dump *rsc_dump, struct page *page,
|
int read_size, int start_idx)
|
{
|
void *data = page_address(page);
|
enum mlx5_sgmt_type sgmt_idx;
|
int num_of_items;
|
char *sgmt_name;
|
void *member;
|
int size = 0;
|
void *menu;
|
int i;
|
|
if (!start_idx) {
|
menu = MLX5_ADDR_OF(menu_resource_dump_response, data, menu);
|
rsc_dump->number_of_menu_items = MLX5_GET(resource_dump_menu_segment, menu,
|
num_of_records);
|
size = MLX5_RSC_DUMP_MENU_HEADER_SIZE;
|
data += size;
|
}
|
num_of_items = rsc_dump->number_of_menu_items;
|
|
for (i = 0; start_idx + i < num_of_items; i++) {
|
size += MLX5_ST_SZ_BYTES(resource_dump_menu_record);
|
if (size >= read_size)
|
return start_idx + i;
|
|
member = data + MLX5_ST_SZ_BYTES(resource_dump_menu_record) * i;
|
sgmt_name = MLX5_ADDR_OF(resource_dump_menu_record, member, segment_name);
|
sgmt_idx = mlx5_rsc_dump_sgmt_get_by_name(sgmt_name);
|
if (sgmt_idx == -EINVAL)
|
continue;
|
rsc_dump->fw_segment_type[sgmt_idx] = MLX5_GET(resource_dump_menu_record,
|
member, segment_type);
|
}
|
return 0;
|
}
|
|
static int mlx5_rsc_dump_trigger(struct mlx5_core_dev *dev, struct mlx5_rsc_dump_cmd *cmd,
|
struct page *page)
|
{
|
struct mlx5_rsc_dump *rsc_dump = dev->rsc_dump;
|
struct device *ddev = mlx5_core_dma_dev(dev);
|
u32 out_seq_num;
|
u32 in_seq_num;
|
dma_addr_t dma;
|
int err;
|
|
dma = dma_map_page(ddev, page, 0, cmd->mem_size, DMA_FROM_DEVICE);
|
if (unlikely(dma_mapping_error(ddev, dma)))
|
return -ENOMEM;
|
|
in_seq_num = MLX5_GET(resource_dump, cmd->cmd, seq_num);
|
MLX5_SET(resource_dump, cmd->cmd, mkey, rsc_dump->mkey.key);
|
MLX5_SET64(resource_dump, cmd->cmd, address, dma);
|
|
err = mlx5_core_access_reg(dev, cmd->cmd, sizeof(cmd->cmd), cmd->cmd,
|
sizeof(cmd->cmd), MLX5_REG_RESOURCE_DUMP, 0, 1);
|
if (err) {
|
mlx5_core_err(dev, "Resource dump: Failed to access err %d\n", err);
|
goto out;
|
}
|
out_seq_num = MLX5_GET(resource_dump, cmd->cmd, seq_num);
|
if (out_seq_num && (in_seq_num + 1 != out_seq_num))
|
err = -EIO;
|
out:
|
dma_unmap_page(ddev, dma, cmd->mem_size, DMA_FROM_DEVICE);
|
return err;
|
}
|
|
struct mlx5_rsc_dump_cmd *mlx5_rsc_dump_cmd_create(struct mlx5_core_dev *dev,
|
struct mlx5_rsc_key *key)
|
{
|
struct mlx5_rsc_dump_cmd *cmd;
|
int sgmt_type;
|
|
if (IS_ERR_OR_NULL(dev->rsc_dump))
|
return ERR_PTR(-EOPNOTSUPP);
|
|
sgmt_type = dev->rsc_dump->fw_segment_type[key->rsc];
|
if (!sgmt_type && key->rsc != MLX5_SGMT_TYPE_MENU)
|
return ERR_PTR(-EOPNOTSUPP);
|
|
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
if (!cmd) {
|
mlx5_core_err(dev, "Resource dump: Failed to allocate command\n");
|
return ERR_PTR(-ENOMEM);
|
}
|
MLX5_SET(resource_dump, cmd->cmd, segment_type, sgmt_type);
|
MLX5_SET(resource_dump, cmd->cmd, index1, key->index1);
|
MLX5_SET(resource_dump, cmd->cmd, index2, key->index2);
|
MLX5_SET(resource_dump, cmd->cmd, num_of_obj1, key->num_of_obj1);
|
MLX5_SET(resource_dump, cmd->cmd, num_of_obj2, key->num_of_obj2);
|
MLX5_SET(resource_dump, cmd->cmd, size, key->size);
|
cmd->mem_size = key->size;
|
return cmd;
|
}
|
EXPORT_SYMBOL(mlx5_rsc_dump_cmd_create);
|
|
void mlx5_rsc_dump_cmd_destroy(struct mlx5_rsc_dump_cmd *cmd)
|
{
|
kfree(cmd);
|
}
|
EXPORT_SYMBOL(mlx5_rsc_dump_cmd_destroy);
|
|
int mlx5_rsc_dump_next(struct mlx5_core_dev *dev, struct mlx5_rsc_dump_cmd *cmd,
|
struct page *page, int *size)
|
{
|
bool more_dump;
|
int err;
|
|
if (IS_ERR_OR_NULL(dev->rsc_dump))
|
return -EOPNOTSUPP;
|
|
err = mlx5_rsc_dump_trigger(dev, cmd, page);
|
if (err) {
|
mlx5_core_err(dev, "Resource dump: Failed to trigger dump, %d\n", err);
|
return err;
|
}
|
*size = MLX5_GET(resource_dump, cmd->cmd, size);
|
more_dump = MLX5_GET(resource_dump, cmd->cmd, more_dump);
|
|
return more_dump;
|
}
|
EXPORT_SYMBOL(mlx5_rsc_dump_next);
|
|
#define MLX5_RSC_DUMP_MENU_SEGMENT 0xffff
|
static int mlx5_rsc_dump_menu(struct mlx5_core_dev *dev)
|
{
|
struct mlx5_rsc_dump_cmd *cmd = NULL;
|
struct mlx5_rsc_key key = {};
|
struct page *page;
|
int start_idx = 0;
|
int size;
|
int err;
|
|
page = alloc_page(GFP_KERNEL);
|
if (!page)
|
return -ENOMEM;
|
|
key.rsc = MLX5_SGMT_TYPE_MENU;
|
key.size = PAGE_SIZE;
|
cmd = mlx5_rsc_dump_cmd_create(dev, &key);
|
if (IS_ERR(cmd)) {
|
err = PTR_ERR(cmd);
|
goto free_page;
|
}
|
MLX5_SET(resource_dump, cmd->cmd, segment_type, MLX5_RSC_DUMP_MENU_SEGMENT);
|
|
do {
|
err = mlx5_rsc_dump_next(dev, cmd, page, &size);
|
if (err < 0)
|
goto destroy_cmd;
|
|
start_idx = mlx5_rsc_dump_read_menu_sgmt(dev->rsc_dump, page, size, start_idx);
|
|
} while (err > 0);
|
|
destroy_cmd:
|
mlx5_rsc_dump_cmd_destroy(cmd);
|
free_page:
|
__free_page(page);
|
|
return err;
|
}
|
|
static int mlx5_rsc_dump_create_mkey(struct mlx5_core_dev *mdev, u32 pdn,
|
struct mlx5_core_mkey *mkey)
|
{
|
int inlen = MLX5_ST_SZ_BYTES(create_mkey_in);
|
void *mkc;
|
u32 *in;
|
int err;
|
|
in = kvzalloc(inlen, GFP_KERNEL);
|
if (!in)
|
return -ENOMEM;
|
|
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
|
MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_PA);
|
MLX5_SET(mkc, mkc, lw, 1);
|
MLX5_SET(mkc, mkc, lr, 1);
|
|
MLX5_SET(mkc, mkc, pd, pdn);
|
MLX5_SET(mkc, mkc, length64, 1);
|
MLX5_SET(mkc, mkc, qpn, 0xffffff);
|
|
err = mlx5_core_create_mkey(mdev, mkey, in, inlen);
|
|
kvfree(in);
|
return err;
|
}
|
|
struct mlx5_rsc_dump *mlx5_rsc_dump_create(struct mlx5_core_dev *dev)
|
{
|
struct mlx5_rsc_dump *rsc_dump;
|
|
if (!MLX5_CAP_DEBUG(dev, resource_dump)) {
|
mlx5_core_dbg(dev, "Resource dump: capability not present\n");
|
return NULL;
|
}
|
rsc_dump = kzalloc(sizeof(*rsc_dump), GFP_KERNEL);
|
if (!rsc_dump)
|
return ERR_PTR(-ENOMEM);
|
|
return rsc_dump;
|
}
|
|
void mlx5_rsc_dump_destroy(struct mlx5_core_dev *dev)
|
{
|
if (IS_ERR_OR_NULL(dev->rsc_dump))
|
return;
|
kfree(dev->rsc_dump);
|
}
|
|
int mlx5_rsc_dump_init(struct mlx5_core_dev *dev)
|
{
|
struct mlx5_rsc_dump *rsc_dump = dev->rsc_dump;
|
int err;
|
|
if (IS_ERR_OR_NULL(dev->rsc_dump))
|
return 0;
|
|
err = mlx5_core_alloc_pd(dev, &rsc_dump->pdn);
|
if (err) {
|
mlx5_core_warn(dev, "Resource dump: Failed to allocate PD %d\n", err);
|
return err;
|
}
|
err = mlx5_rsc_dump_create_mkey(dev, rsc_dump->pdn, &rsc_dump->mkey);
|
if (err) {
|
mlx5_core_err(dev, "Resource dump: Failed to create mkey, %d\n", err);
|
goto free_pd;
|
}
|
err = mlx5_rsc_dump_menu(dev);
|
if (err) {
|
mlx5_core_err(dev, "Resource dump: Failed to read menu, %d\n", err);
|
goto destroy_mkey;
|
}
|
return err;
|
|
destroy_mkey:
|
mlx5_core_destroy_mkey(dev, &rsc_dump->mkey);
|
free_pd:
|
mlx5_core_dealloc_pd(dev, rsc_dump->pdn);
|
return err;
|
}
|
|
void mlx5_rsc_dump_cleanup(struct mlx5_core_dev *dev)
|
{
|
if (IS_ERR_OR_NULL(dev->rsc_dump))
|
return;
|
|
mlx5_core_destroy_mkey(dev, &dev->rsc_dump->mkey);
|
mlx5_core_dealloc_pd(dev, dev->rsc_dump->pdn);
|
}
|