net/mlx5: Add support for resource dump

On driver load:
- Initialize resource dump data structure and memory access tools (mkey
  & pd).
- Read the resource dump's menu which contains the FW segment
  identifier. Each record is identified by the segment name (ASCII).

During the driver's course of life, users (like reporters) may request
dumps per segment. The user should create a command providing the
segment identifier (SW enumeration) and command keys. In return, the
user receives a command context. In order to receive the dump, the user
should supply the command context and a memory (aligned to a PAGE) on
which the dump content will be written. Since the dump may be larger
than the given memory, the user may resubmit the command until received
an indication of end-of-dump. It is the user's responsibility to destroy
the command.

Signed-off-by: Aya Levin <ayal@mellanox.com>
Reviewed-by: Moshe Shemesh <moshe@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
This commit is contained in:
Aya Levin 2020-02-11 14:32:43 -08:00 committed by Saeed Mahameed
parent 573ed90aa5
commit 12206b1723
5 changed files with 358 additions and 1 deletions

View File

@ -16,7 +16,7 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \
fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \
lib/devcom.o lib/pci_vsc.o lib/dm.o diag/fs_tracepoint.o \
diag/fw_tracer.o diag/crdump.o devlink.o
diag/fw_tracer.o diag/crdump.o devlink.o diag/rsc_dump.o
#
# Netdev basic

View File

@ -0,0 +1,286 @@
// 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),
};
struct mlx5_rsc_dump {
u32 pdn;
struct mlx5_core_mkey mkey;
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;
}
static void mlx5_rsc_dump_read_menu_sgmt(struct mlx5_rsc_dump *rsc_dump, struct page *page)
{
void *data = page_address(page);
enum mlx5_sgmt_type sgmt_idx;
int num_of_items;
char *sgmt_name;
void *member;
void *menu;
int i;
menu = MLX5_ADDR_OF(menu_resource_dump_response, data, menu);
num_of_items = MLX5_GET(resource_dump_menu_segment, menu, num_of_records);
for (i = 0; i < num_of_items; i++) {
member = MLX5_ADDR_OF(resource_dump_menu_segment, 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);
}
}
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 = &dev->pdev->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;
}
void mlx5_rsc_dump_cmd_destroy(struct mlx5_rsc_dump_cmd *cmd)
{
kfree(cmd);
}
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;
}
#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 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;
mlx5_rsc_dump_read_menu_sgmt(dev->rsc_dump, page);
} 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);
}

View File

@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2019 Mellanox Technologies. */
#ifndef __MLX5_RSC_DUMP_H
#define __MLX5_RSC_DUMP__H
#include <linux/mlx5/driver.h>
#include "mlx5_core.h"
enum mlx5_sgmt_type {
MLX5_SGMT_TYPE_HW_CQPC,
MLX5_SGMT_TYPE_HW_SQPC,
MLX5_SGMT_TYPE_HW_RQPC,
MLX5_SGMT_TYPE_FULL_SRQC,
MLX5_SGMT_TYPE_FULL_CQC,
MLX5_SGMT_TYPE_FULL_EQC,
MLX5_SGMT_TYPE_FULL_QPC,
MLX5_SGMT_TYPE_SND_BUFF,
MLX5_SGMT_TYPE_RCV_BUFF,
MLX5_SGMT_TYPE_SRQ_BUFF,
MLX5_SGMT_TYPE_CQ_BUFF,
MLX5_SGMT_TYPE_EQ_BUFF,
MLX5_SGMT_TYPE_SX_SLICE,
MLX5_SGMT_TYPE_SX_SLICE_ALL,
MLX5_SGMT_TYPE_RDB,
MLX5_SGMT_TYPE_RX_SLICE_ALL,
MLX5_SGMT_TYPE_MENU,
MLX5_SGMT_TYPE_TERMINATE,
MLX5_SGMT_TYPE_NUM, /* Keep last */
};
struct mlx5_rsc_key {
enum mlx5_sgmt_type rsc;
int index1;
int index2;
int num_of_obj1;
int num_of_obj2;
int size;
};
#define MLX5_RSC_DUMP_ALL 0xFFFF
struct mlx5_rsc_dump_cmd;
struct mlx5_rsc_dump;
struct mlx5_rsc_dump *mlx5_rsc_dump_create(struct mlx5_core_dev *dev);
void mlx5_rsc_dump_destroy(struct mlx5_core_dev *dev);
int mlx5_rsc_dump_init(struct mlx5_core_dev *dev);
void mlx5_rsc_dump_cleanup(struct mlx5_core_dev *dev);
struct mlx5_rsc_dump_cmd *mlx5_rsc_dump_cmd_create(struct mlx5_core_dev *dev,
struct mlx5_rsc_key *key);
void mlx5_rsc_dump_cmd_destroy(struct mlx5_rsc_dump_cmd *cmd);
int mlx5_rsc_dump_next(struct mlx5_core_dev *dev, struct mlx5_rsc_dump_cmd *cmd,
struct page *page, int *size);
#endif

View File

@ -70,6 +70,7 @@
#include "diag/fw_tracer.h"
#include "ecpf.h"
#include "lib/hv_vhca.h"
#include "diag/rsc_dump.h"
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) core driver");
@ -880,6 +881,7 @@ static int mlx5_init_once(struct mlx5_core_dev *dev)
dev->tracer = mlx5_fw_tracer_create(dev);
dev->hv_vhca = mlx5_hv_vhca_create(dev);
dev->rsc_dump = mlx5_rsc_dump_create(dev);
return 0;
@ -909,6 +911,7 @@ err_devcom:
static void mlx5_cleanup_once(struct mlx5_core_dev *dev)
{
mlx5_rsc_dump_destroy(dev);
mlx5_hv_vhca_destroy(dev->hv_vhca);
mlx5_fw_tracer_destroy(dev->tracer);
mlx5_dm_cleanup(dev);
@ -1079,6 +1082,12 @@ static int mlx5_load(struct mlx5_core_dev *dev)
mlx5_hv_vhca_init(dev->hv_vhca);
err = mlx5_rsc_dump_init(dev);
if (err) {
mlx5_core_err(dev, "Failed to init Resource dump\n");
goto err_rsc_dump;
}
err = mlx5_fpga_device_start(dev);
if (err) {
mlx5_core_err(dev, "fpga device start failed %d\n", err);
@ -1134,6 +1143,8 @@ err_tls_start:
err_ipsec_start:
mlx5_fpga_device_stop(dev);
err_fpga_start:
mlx5_rsc_dump_cleanup(dev);
err_rsc_dump:
mlx5_hv_vhca_cleanup(dev->hv_vhca);
mlx5_fw_tracer_cleanup(dev->tracer);
err_fw_tracer:
@ -1155,6 +1166,7 @@ static void mlx5_unload(struct mlx5_core_dev *dev)
mlx5_accel_ipsec_cleanup(dev);
mlx5_accel_tls_cleanup(dev);
mlx5_fpga_device_stop(dev);
mlx5_rsc_dump_cleanup(dev);
mlx5_hv_vhca_cleanup(dev->hv_vhca);
mlx5_fw_tracer_cleanup(dev->tracer);
mlx5_eq_table_destroy(dev);

View File

@ -722,6 +722,7 @@ struct mlx5_core_dev {
struct mlx5_clock clock;
struct mlx5_ib_clock_info *clock_info;
struct mlx5_fw_tracer *tracer;
struct mlx5_rsc_dump *rsc_dump;
u32 vsc_addr;
struct mlx5_hv_vhca *hv_vhca;
};