mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-15 06:00:41 +00:00
Merge branch 'nfp-enhanced-debug-dump-via-ethtool'
Simon Horman says: ==================== nfp: enhanced debug dump via ethtool Add debug dump implementation to the NFP driver. This makes use of existing ethtool infrastructure. ethtool -W is used to select the dump level and ethtool -w is used to dump NFP state. The existing behaviour of dump level 0, dumping the arm.diag resource, is preserved. Dump levels greater than 0 are implemented by this patchset and optionally supported by firmware providing a _abi_dump_spec rtsym. This rtsym provides a specification, in TLV format, of the information to be dumped from the NFP at each supported dump level. Dumps are also structured using a TLVs. They consist a prolog and the data described int he corresponding dump. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
045203e90e
@ -22,6 +22,7 @@ nfp-objs := \
|
||||
nfp_hwmon.o \
|
||||
nfp_main.o \
|
||||
nfp_net_common.o \
|
||||
nfp_net_debugdump.o \
|
||||
nfp_net_ethtool.o \
|
||||
nfp_net_main.o \
|
||||
nfp_net_repr.o \
|
||||
|
@ -262,6 +262,7 @@ enum lcsr_wr_src {
|
||||
#define OP_CARB_BASE 0x0e000000000ULL
|
||||
#define OP_CARB_OR 0x00000010000ULL
|
||||
|
||||
#define NFP_CSR_CTX_PTR 0x20
|
||||
#define NFP_CSR_ACT_LM_ADDR0 0x64
|
||||
#define NFP_CSR_ACT_LM_ADDR1 0x6c
|
||||
#define NFP_CSR_ACT_LM_ADDR2 0x94
|
||||
@ -382,4 +383,13 @@ int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg,
|
||||
int nfp_ustore_check_valid_no_ecc(u64 insn);
|
||||
u64 nfp_ustore_calc_ecc_insn(u64 insn);
|
||||
|
||||
#define NFP_IND_ME_REFL_WR_SIG_INIT 3
|
||||
#define NFP_IND_ME_CTX_PTR_BASE_MASK GENMASK(9, 0)
|
||||
#define NFP_IND_NUM_CONTEXTS 8
|
||||
|
||||
static inline u32 nfp_get_ind_csr_ctx_ptr_offs(u32 read_offset)
|
||||
{
|
||||
return (read_offset & ~NFP_IND_ME_CTX_PTR_BASE_MASK) | NFP_CSR_CTX_PTR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/vermagic.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <net/devlink.h>
|
||||
|
||||
#include "nfpcore/nfp.h"
|
||||
@ -509,6 +510,9 @@ static int nfp_pci_probe(struct pci_dev *pdev,
|
||||
pf->mip = nfp_mip_open(pf->cpp);
|
||||
pf->rtbl = __nfp_rtsym_table_read(pf->cpp, pf->mip);
|
||||
|
||||
pf->dump_flag = NFP_DUMP_NSP_DIAG;
|
||||
pf->dumpspec = nfp_net_dump_load_dumpspec(pf->cpp, pf->rtbl);
|
||||
|
||||
err = nfp_pcie_sriov_read_nfd_limit(pf);
|
||||
if (err)
|
||||
goto err_fw_unload;
|
||||
@ -544,6 +548,7 @@ err_fw_unload:
|
||||
nfp_fw_unload(pf);
|
||||
kfree(pf->eth_tbl);
|
||||
kfree(pf->nspi);
|
||||
vfree(pf->dumpspec);
|
||||
err_devlink_unreg:
|
||||
devlink_unregister(devlink);
|
||||
err_hwinfo_free:
|
||||
@ -579,6 +584,7 @@ static void nfp_pci_remove(struct pci_dev *pdev)
|
||||
|
||||
devlink_unregister(devlink);
|
||||
|
||||
vfree(pf->dumpspec);
|
||||
kfree(pf->rtbl);
|
||||
nfp_mip_close(pf->mip);
|
||||
if (pf->fw_loaded)
|
||||
|
@ -39,6 +39,7 @@
|
||||
#ifndef NFP_MAIN_H
|
||||
#define NFP_MAIN_H
|
||||
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/msi.h>
|
||||
@ -61,6 +62,17 @@ struct nfp_nsp_identify;
|
||||
struct nfp_port;
|
||||
struct nfp_rtsym_table;
|
||||
|
||||
/**
|
||||
* struct nfp_dumpspec - NFP FW dump specification structure
|
||||
* @size: Size of the data
|
||||
* @data: Sequence of TLVs, each being an instruction to dump some data
|
||||
* from FW
|
||||
*/
|
||||
struct nfp_dumpspec {
|
||||
u32 size;
|
||||
u8 data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nfp_pf - NFP PF-specific device structure
|
||||
* @pdev: Backpointer to PCI device
|
||||
@ -83,6 +95,9 @@ struct nfp_rtsym_table;
|
||||
* @mip: MIP handle
|
||||
* @rtbl: RTsym table
|
||||
* @hwinfo: HWInfo table
|
||||
* @dumpspec: Debug dump specification
|
||||
* @dump_flag: Store dump flag between set_dump and get_dump_flag
|
||||
* @dump_len: Store dump length between set_dump and get_dump_flag
|
||||
* @eth_tbl: NSP ETH table
|
||||
* @nspi: NSP identification info
|
||||
* @hwmon_dev: pointer to hwmon device
|
||||
@ -124,6 +139,9 @@ struct nfp_pf {
|
||||
const struct nfp_mip *mip;
|
||||
struct nfp_rtsym_table *rtbl;
|
||||
struct nfp_hwinfo *hwinfo;
|
||||
struct nfp_dumpspec *dumpspec;
|
||||
u32 dump_flag;
|
||||
u32 dump_len;
|
||||
struct nfp_eth_table *eth_tbl;
|
||||
struct nfp_nsp_identify *nspi;
|
||||
|
||||
@ -157,4 +175,15 @@ void nfp_net_get_mac_addr(struct nfp_pf *pf, struct nfp_port *port);
|
||||
|
||||
bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);
|
||||
|
||||
enum nfp_dump_diag {
|
||||
NFP_DUMP_NSP_DIAG = 0,
|
||||
};
|
||||
|
||||
struct nfp_dumpspec *
|
||||
nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl);
|
||||
s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec,
|
||||
u32 flag);
|
||||
int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec,
|
||||
struct ethtool_dump *dump_param, void *dest);
|
||||
|
||||
#endif /* NFP_MAIN_H */
|
||||
|
787
drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c
Normal file
787
drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c
Normal file
@ -0,0 +1,787 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||
*
|
||||
* This software is dual licensed under the GNU General License Version 2,
|
||||
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||
* source tree or the BSD 2-Clause License provided below. You have the
|
||||
* option to license this software under the complete terms of either license.
|
||||
*
|
||||
* The BSD 2-Clause License:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "nfp_asm.h"
|
||||
#include "nfp_main.h"
|
||||
#include "nfpcore/nfp.h"
|
||||
#include "nfpcore/nfp_nffw.h"
|
||||
|
||||
#define NFP_DUMP_SPEC_RTSYM "_abi_dump_spec"
|
||||
|
||||
#define ALIGN8(x) ALIGN(x, 8)
|
||||
|
||||
enum nfp_dumpspec_type {
|
||||
NFP_DUMPSPEC_TYPE_CPP_CSR = 0,
|
||||
NFP_DUMPSPEC_TYPE_XPB_CSR = 1,
|
||||
NFP_DUMPSPEC_TYPE_ME_CSR = 2,
|
||||
NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR = 3,
|
||||
NFP_DUMPSPEC_TYPE_RTSYM = 4,
|
||||
NFP_DUMPSPEC_TYPE_HWINFO = 5,
|
||||
NFP_DUMPSPEC_TYPE_FWNAME = 6,
|
||||
NFP_DUMPSPEC_TYPE_HWINFO_FIELD = 7,
|
||||
NFP_DUMPSPEC_TYPE_PROLOG = 10000,
|
||||
NFP_DUMPSPEC_TYPE_ERROR = 10001,
|
||||
};
|
||||
|
||||
/* The following structs must be carefully aligned so that they can be used to
|
||||
* interpret the binary dumpspec and populate the dump data in a deterministic
|
||||
* way.
|
||||
*/
|
||||
|
||||
/* generic type plus length */
|
||||
struct nfp_dump_tl {
|
||||
__be32 type;
|
||||
__be32 length; /* chunk length to follow, aligned to 8 bytes */
|
||||
char data[0];
|
||||
};
|
||||
|
||||
/* NFP CPP parameters */
|
||||
struct nfp_dumpspec_cpp_isl_id {
|
||||
u8 target;
|
||||
u8 action;
|
||||
u8 token;
|
||||
u8 island;
|
||||
};
|
||||
|
||||
struct nfp_dump_common_cpp {
|
||||
struct nfp_dumpspec_cpp_isl_id cpp_id;
|
||||
__be32 offset; /* address to start dump */
|
||||
__be32 dump_length; /* total bytes to dump, aligned to reg size */
|
||||
};
|
||||
|
||||
/* CSR dumpables */
|
||||
struct nfp_dumpspec_csr {
|
||||
struct nfp_dump_tl tl;
|
||||
struct nfp_dump_common_cpp cpp;
|
||||
__be32 register_width; /* in bits */
|
||||
};
|
||||
|
||||
struct nfp_dumpspec_rtsym {
|
||||
struct nfp_dump_tl tl;
|
||||
char rtsym[0];
|
||||
};
|
||||
|
||||
/* header for register dumpable */
|
||||
struct nfp_dump_csr {
|
||||
struct nfp_dump_tl tl;
|
||||
struct nfp_dump_common_cpp cpp;
|
||||
__be32 register_width; /* in bits */
|
||||
__be32 error; /* error code encountered while reading */
|
||||
__be32 error_offset; /* offset being read when error occurred */
|
||||
};
|
||||
|
||||
struct nfp_dump_rtsym {
|
||||
struct nfp_dump_tl tl;
|
||||
struct nfp_dump_common_cpp cpp;
|
||||
__be32 error; /* error code encountered while reading */
|
||||
u8 padded_name_length; /* pad so data starts at 8 byte boundary */
|
||||
char rtsym[0];
|
||||
/* after padded_name_length, there is dump_length data */
|
||||
};
|
||||
|
||||
struct nfp_dump_prolog {
|
||||
struct nfp_dump_tl tl;
|
||||
__be32 dump_level;
|
||||
};
|
||||
|
||||
struct nfp_dump_error {
|
||||
struct nfp_dump_tl tl;
|
||||
__be32 error;
|
||||
char padding[4];
|
||||
char spec[0];
|
||||
};
|
||||
|
||||
/* to track state through debug size calculation TLV traversal */
|
||||
struct nfp_level_size {
|
||||
u32 requested_level; /* input */
|
||||
u32 total_size; /* output */
|
||||
};
|
||||
|
||||
/* to track state during debug dump creation TLV traversal */
|
||||
struct nfp_dump_state {
|
||||
u32 requested_level; /* input param */
|
||||
u32 dumped_size; /* adds up to size of dumped data */
|
||||
u32 buf_size; /* size of buffer pointer to by p */
|
||||
void *p; /* current point in dump buffer */
|
||||
};
|
||||
|
||||
typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl,
|
||||
void *param);
|
||||
|
||||
static int
|
||||
nfp_traverse_tlvs(struct nfp_pf *pf, void *data, u32 data_length, void *param,
|
||||
nfp_tlv_visit tlv_visit)
|
||||
{
|
||||
long long remaining = data_length;
|
||||
struct nfp_dump_tl *tl;
|
||||
u32 total_tlv_size;
|
||||
void *p = data;
|
||||
int err;
|
||||
|
||||
while (remaining >= sizeof(*tl)) {
|
||||
tl = p;
|
||||
if (!tl->type && !tl->length)
|
||||
break;
|
||||
|
||||
if (be32_to_cpu(tl->length) > remaining - sizeof(*tl))
|
||||
return -EINVAL;
|
||||
|
||||
total_tlv_size = sizeof(*tl) + be32_to_cpu(tl->length);
|
||||
|
||||
/* Spec TLVs should be aligned to 4 bytes. */
|
||||
if (total_tlv_size % 4 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
p += total_tlv_size;
|
||||
remaining -= total_tlv_size;
|
||||
err = tlv_visit(pf, tl, param);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 nfp_get_numeric_cpp_id(struct nfp_dumpspec_cpp_isl_id *cpp_id)
|
||||
{
|
||||
return NFP_CPP_ISLAND_ID(cpp_id->target, cpp_id->action, cpp_id->token,
|
||||
cpp_id->island);
|
||||
}
|
||||
|
||||
struct nfp_dumpspec *
|
||||
nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl)
|
||||
{
|
||||
const struct nfp_rtsym *specsym;
|
||||
struct nfp_dumpspec *dumpspec;
|
||||
int bytes_read;
|
||||
u32 cpp_id;
|
||||
|
||||
specsym = nfp_rtsym_lookup(rtbl, NFP_DUMP_SPEC_RTSYM);
|
||||
if (!specsym)
|
||||
return NULL;
|
||||
|
||||
/* expected size of this buffer is in the order of tens of kilobytes */
|
||||
dumpspec = vmalloc(sizeof(*dumpspec) + specsym->size);
|
||||
if (!dumpspec)
|
||||
return NULL;
|
||||
|
||||
dumpspec->size = specsym->size;
|
||||
|
||||
cpp_id = NFP_CPP_ISLAND_ID(specsym->target, NFP_CPP_ACTION_RW, 0,
|
||||
specsym->domain);
|
||||
|
||||
bytes_read = nfp_cpp_read(cpp, cpp_id, specsym->addr, dumpspec->data,
|
||||
specsym->size);
|
||||
if (bytes_read != specsym->size) {
|
||||
vfree(dumpspec);
|
||||
nfp_warn(cpp, "Debug dump specification read failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dumpspec;
|
||||
}
|
||||
|
||||
static int nfp_dump_error_tlv_size(struct nfp_dump_tl *spec)
|
||||
{
|
||||
return ALIGN8(sizeof(struct nfp_dump_error) + sizeof(*spec) +
|
||||
be32_to_cpu(spec->length));
|
||||
}
|
||||
|
||||
static int nfp_calc_fwname_tlv_size(struct nfp_pf *pf)
|
||||
{
|
||||
u32 fwname_len = strlen(nfp_mip_name(pf->mip));
|
||||
|
||||
return sizeof(struct nfp_dump_tl) + ALIGN8(fwname_len + 1);
|
||||
}
|
||||
|
||||
static int nfp_calc_hwinfo_field_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
|
||||
{
|
||||
u32 tl_len, key_len;
|
||||
const char *value;
|
||||
|
||||
tl_len = be32_to_cpu(spec->length);
|
||||
key_len = strnlen(spec->data, tl_len);
|
||||
if (key_len == tl_len)
|
||||
return nfp_dump_error_tlv_size(spec);
|
||||
|
||||
value = nfp_hwinfo_lookup(pf->hwinfo, spec->data);
|
||||
if (!value)
|
||||
return nfp_dump_error_tlv_size(spec);
|
||||
|
||||
return sizeof(struct nfp_dump_tl) + ALIGN8(key_len + strlen(value) + 2);
|
||||
}
|
||||
|
||||
static bool nfp_csr_spec_valid(struct nfp_dumpspec_csr *spec_csr)
|
||||
{
|
||||
u32 required_read_sz = sizeof(*spec_csr) - sizeof(spec_csr->tl);
|
||||
u32 available_sz = be32_to_cpu(spec_csr->tl.length);
|
||||
u32 reg_width;
|
||||
|
||||
if (available_sz < required_read_sz)
|
||||
return false;
|
||||
|
||||
reg_width = be32_to_cpu(spec_csr->register_width);
|
||||
|
||||
return reg_width == 32 || reg_width == 64;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_calc_rtsym_dump_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
|
||||
{
|
||||
struct nfp_rtsym_table *rtbl = pf->rtbl;
|
||||
struct nfp_dumpspec_rtsym *spec_rtsym;
|
||||
const struct nfp_rtsym *sym;
|
||||
u32 tl_len, key_len;
|
||||
|
||||
spec_rtsym = (struct nfp_dumpspec_rtsym *)spec;
|
||||
tl_len = be32_to_cpu(spec->length);
|
||||
key_len = strnlen(spec_rtsym->rtsym, tl_len);
|
||||
if (key_len == tl_len)
|
||||
return nfp_dump_error_tlv_size(spec);
|
||||
|
||||
sym = nfp_rtsym_lookup(rtbl, spec_rtsym->rtsym);
|
||||
if (!sym)
|
||||
return nfp_dump_error_tlv_size(spec);
|
||||
|
||||
return ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1) +
|
||||
ALIGN8(sym->size);
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_add_tlv_size(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
|
||||
{
|
||||
struct nfp_dumpspec_csr *spec_csr;
|
||||
u32 *size = param;
|
||||
u32 hwinfo_size;
|
||||
|
||||
switch (be32_to_cpu(tl->type)) {
|
||||
case NFP_DUMPSPEC_TYPE_FWNAME:
|
||||
*size += nfp_calc_fwname_tlv_size(pf);
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_CPP_CSR:
|
||||
case NFP_DUMPSPEC_TYPE_XPB_CSR:
|
||||
case NFP_DUMPSPEC_TYPE_ME_CSR:
|
||||
spec_csr = (struct nfp_dumpspec_csr *)tl;
|
||||
if (!nfp_csr_spec_valid(spec_csr))
|
||||
*size += nfp_dump_error_tlv_size(tl);
|
||||
else
|
||||
*size += ALIGN8(sizeof(struct nfp_dump_csr)) +
|
||||
ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length));
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
|
||||
spec_csr = (struct nfp_dumpspec_csr *)tl;
|
||||
if (!nfp_csr_spec_valid(spec_csr))
|
||||
*size += nfp_dump_error_tlv_size(tl);
|
||||
else
|
||||
*size += ALIGN8(sizeof(struct nfp_dump_csr)) +
|
||||
ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length) *
|
||||
NFP_IND_NUM_CONTEXTS);
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_RTSYM:
|
||||
*size += nfp_calc_rtsym_dump_sz(pf, tl);
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_HWINFO:
|
||||
hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
|
||||
*size += sizeof(struct nfp_dump_tl) + ALIGN8(hwinfo_size);
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
|
||||
*size += nfp_calc_hwinfo_field_sz(pf, tl);
|
||||
break;
|
||||
default:
|
||||
*size += nfp_dump_error_tlv_size(tl);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_calc_specific_level_size(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
|
||||
void *param)
|
||||
{
|
||||
struct nfp_level_size *lev_sz = param;
|
||||
|
||||
if (be32_to_cpu(dump_level->type) != lev_sz->requested_level)
|
||||
return 0;
|
||||
|
||||
return nfp_traverse_tlvs(pf, dump_level->data,
|
||||
be32_to_cpu(dump_level->length),
|
||||
&lev_sz->total_size, nfp_add_tlv_size);
|
||||
}
|
||||
|
||||
s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec,
|
||||
u32 flag)
|
||||
{
|
||||
struct nfp_level_size lev_sz;
|
||||
int err;
|
||||
|
||||
lev_sz.requested_level = flag;
|
||||
lev_sz.total_size = ALIGN8(sizeof(struct nfp_dump_prolog));
|
||||
|
||||
err = nfp_traverse_tlvs(pf, spec->data, spec->size, &lev_sz,
|
||||
nfp_calc_specific_level_size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return lev_sz.total_size;
|
||||
}
|
||||
|
||||
static int nfp_add_tlv(u32 type, u32 total_tlv_sz, struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_tl *tl = dump->p;
|
||||
|
||||
if (total_tlv_sz > dump->buf_size)
|
||||
return -ENOSPC;
|
||||
|
||||
if (dump->buf_size - total_tlv_sz < dump->dumped_size)
|
||||
return -ENOSPC;
|
||||
|
||||
tl->type = cpu_to_be32(type);
|
||||
tl->length = cpu_to_be32(total_tlv_sz - sizeof(*tl));
|
||||
|
||||
dump->dumped_size += total_tlv_sz;
|
||||
dump->p += total_tlv_sz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_dump_error_tlv(struct nfp_dump_tl *spec, int error,
|
||||
struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_error *dump_header = dump->p;
|
||||
u32 total_spec_size, total_size;
|
||||
int err;
|
||||
|
||||
total_spec_size = sizeof(*spec) + be32_to_cpu(spec->length);
|
||||
total_size = ALIGN8(sizeof(*dump_header) + total_spec_size);
|
||||
|
||||
err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_ERROR, total_size, dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dump_header->error = cpu_to_be32(error);
|
||||
memcpy(dump_header->spec, spec, total_spec_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfp_dump_fwname(struct nfp_pf *pf, struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_tl *dump_header = dump->p;
|
||||
u32 fwname_len, total_size;
|
||||
const char *fwname;
|
||||
int err;
|
||||
|
||||
fwname = nfp_mip_name(pf->mip);
|
||||
fwname_len = strlen(fwname);
|
||||
total_size = sizeof(*dump_header) + ALIGN8(fwname_len + 1);
|
||||
|
||||
err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_FWNAME, total_size, dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(dump_header->data, fwname, fwname_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_dump_hwinfo(struct nfp_pf *pf, struct nfp_dump_tl *spec,
|
||||
struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_tl *dump_header = dump->p;
|
||||
u32 hwinfo_size, total_size;
|
||||
char *hwinfo;
|
||||
int err;
|
||||
|
||||
hwinfo = nfp_hwinfo_get_packed_strings(pf->hwinfo);
|
||||
hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
|
||||
total_size = sizeof(*dump_header) + ALIGN8(hwinfo_size);
|
||||
|
||||
err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO, total_size, dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(dump_header->data, hwinfo, hwinfo_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfp_dump_hwinfo_field(struct nfp_pf *pf, struct nfp_dump_tl *spec,
|
||||
struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_tl *dump_header = dump->p;
|
||||
u32 tl_len, key_len, val_len;
|
||||
const char *key, *value;
|
||||
u32 total_size;
|
||||
int err;
|
||||
|
||||
tl_len = be32_to_cpu(spec->length);
|
||||
key_len = strnlen(spec->data, tl_len);
|
||||
if (key_len == tl_len)
|
||||
return nfp_dump_error_tlv(spec, -EINVAL, dump);
|
||||
|
||||
key = spec->data;
|
||||
value = nfp_hwinfo_lookup(pf->hwinfo, key);
|
||||
if (!value)
|
||||
return nfp_dump_error_tlv(spec, -ENOENT, dump);
|
||||
|
||||
val_len = strlen(value);
|
||||
total_size = sizeof(*dump_header) + ALIGN8(key_len + val_len + 2);
|
||||
err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO_FIELD, total_size, dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(dump_header->data, key, key_len + 1);
|
||||
memcpy(dump_header->data + key_len + 1, value, val_len + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_dump_csr_range(struct nfp_pf *pf, struct nfp_dumpspec_csr *spec_csr,
|
||||
struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_csr *dump_header = dump->p;
|
||||
u32 reg_sz, header_size, total_size;
|
||||
u32 cpp_rd_addr, max_rd_addr;
|
||||
int bytes_read;
|
||||
void *dest;
|
||||
u32 cpp_id;
|
||||
int err;
|
||||
|
||||
if (!nfp_csr_spec_valid(spec_csr))
|
||||
return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump);
|
||||
|
||||
reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE;
|
||||
header_size = ALIGN8(sizeof(*dump_header));
|
||||
total_size = header_size +
|
||||
ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length));
|
||||
dest = dump->p + header_size;
|
||||
|
||||
err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dump_header->cpp = spec_csr->cpp;
|
||||
dump_header->register_width = spec_csr->register_width;
|
||||
|
||||
cpp_id = nfp_get_numeric_cpp_id(&spec_csr->cpp.cpp_id);
|
||||
cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset);
|
||||
max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length);
|
||||
|
||||
while (cpp_rd_addr < max_rd_addr) {
|
||||
bytes_read = nfp_cpp_read(pf->cpp, cpp_id, cpp_rd_addr, dest,
|
||||
reg_sz);
|
||||
if (bytes_read != reg_sz) {
|
||||
if (bytes_read >= 0)
|
||||
bytes_read = -EIO;
|
||||
dump_header->error = cpu_to_be32(bytes_read);
|
||||
dump_header->error_offset = cpu_to_be32(cpp_rd_addr);
|
||||
break;
|
||||
}
|
||||
cpp_rd_addr += reg_sz;
|
||||
dest += reg_sz;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write context to CSRCtxPtr, then read from it. Then the value can be read
|
||||
* from IndCtxStatus.
|
||||
*/
|
||||
static int
|
||||
nfp_read_indirect_csr(struct nfp_cpp *cpp,
|
||||
struct nfp_dumpspec_cpp_isl_id cpp_params, u32 offset,
|
||||
u32 reg_sz, u32 context, void *dest)
|
||||
{
|
||||
u32 csr_ctx_ptr_offs;
|
||||
u32 cpp_id;
|
||||
int result;
|
||||
|
||||
csr_ctx_ptr_offs = nfp_get_ind_csr_ctx_ptr_offs(offset);
|
||||
cpp_id = NFP_CPP_ISLAND_ID(cpp_params.target,
|
||||
NFP_IND_ME_REFL_WR_SIG_INIT,
|
||||
cpp_params.token, cpp_params.island);
|
||||
result = nfp_cpp_writel(cpp, cpp_id, csr_ctx_ptr_offs, context);
|
||||
if (result != sizeof(context))
|
||||
return result < 0 ? result : -EIO;
|
||||
|
||||
cpp_id = nfp_get_numeric_cpp_id(&cpp_params);
|
||||
result = nfp_cpp_read(cpp, cpp_id, csr_ctx_ptr_offs, dest, reg_sz);
|
||||
if (result != reg_sz)
|
||||
return result < 0 ? result : -EIO;
|
||||
|
||||
result = nfp_cpp_read(cpp, cpp_id, offset, dest, reg_sz);
|
||||
if (result != reg_sz)
|
||||
return result < 0 ? result : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_read_all_indirect_csr_ctx(struct nfp_cpp *cpp,
|
||||
struct nfp_dumpspec_csr *spec_csr, u32 address,
|
||||
u32 reg_sz, void *dest)
|
||||
{
|
||||
u32 ctx;
|
||||
int err;
|
||||
|
||||
for (ctx = 0; ctx < NFP_IND_NUM_CONTEXTS; ctx++) {
|
||||
err = nfp_read_indirect_csr(cpp, spec_csr->cpp.cpp_id, address,
|
||||
reg_sz, ctx, dest + ctx * reg_sz);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_dump_indirect_csr_range(struct nfp_pf *pf,
|
||||
struct nfp_dumpspec_csr *spec_csr,
|
||||
struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_csr *dump_header = dump->p;
|
||||
u32 reg_sz, header_size, total_size;
|
||||
u32 cpp_rd_addr, max_rd_addr;
|
||||
u32 reg_data_length;
|
||||
void *dest;
|
||||
int err;
|
||||
|
||||
if (!nfp_csr_spec_valid(spec_csr))
|
||||
return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump);
|
||||
|
||||
reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE;
|
||||
header_size = ALIGN8(sizeof(*dump_header));
|
||||
reg_data_length = be32_to_cpu(spec_csr->cpp.dump_length) *
|
||||
NFP_IND_NUM_CONTEXTS;
|
||||
total_size = header_size + ALIGN8(reg_data_length);
|
||||
dest = dump->p + header_size;
|
||||
|
||||
err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dump_header->cpp = spec_csr->cpp;
|
||||
dump_header->register_width = spec_csr->register_width;
|
||||
|
||||
cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset);
|
||||
max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length);
|
||||
while (cpp_rd_addr < max_rd_addr) {
|
||||
err = nfp_read_all_indirect_csr_ctx(pf->cpp, spec_csr,
|
||||
cpp_rd_addr, reg_sz, dest);
|
||||
if (err) {
|
||||
dump_header->error = cpu_to_be32(err);
|
||||
dump_header->error_offset = cpu_to_be32(cpp_rd_addr);
|
||||
break;
|
||||
}
|
||||
cpp_rd_addr += reg_sz;
|
||||
dest += reg_sz * NFP_IND_NUM_CONTEXTS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_dump_single_rtsym(struct nfp_pf *pf, struct nfp_dumpspec_rtsym *spec,
|
||||
struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_rtsym *dump_header = dump->p;
|
||||
struct nfp_dumpspec_cpp_isl_id cpp_params;
|
||||
struct nfp_rtsym_table *rtbl = pf->rtbl;
|
||||
const struct nfp_rtsym *sym;
|
||||
u32 header_size, total_size;
|
||||
u32 tl_len, key_len;
|
||||
int bytes_read;
|
||||
u32 cpp_id;
|
||||
void *dest;
|
||||
int err;
|
||||
|
||||
tl_len = be32_to_cpu(spec->tl.length);
|
||||
key_len = strnlen(spec->rtsym, tl_len);
|
||||
if (key_len == tl_len)
|
||||
return nfp_dump_error_tlv(&spec->tl, -EINVAL, dump);
|
||||
|
||||
sym = nfp_rtsym_lookup(rtbl, spec->rtsym);
|
||||
if (!sym)
|
||||
return nfp_dump_error_tlv(&spec->tl, -ENOENT, dump);
|
||||
|
||||
header_size =
|
||||
ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1);
|
||||
total_size = header_size + ALIGN8(sym->size);
|
||||
dest = dump->p + header_size;
|
||||
|
||||
err = nfp_add_tlv(be32_to_cpu(spec->tl.type), total_size, dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dump_header->padded_name_length =
|
||||
header_size - offsetof(struct nfp_dump_rtsym, rtsym);
|
||||
memcpy(dump_header->rtsym, spec->rtsym, key_len + 1);
|
||||
|
||||
cpp_params.target = sym->target;
|
||||
cpp_params.action = NFP_CPP_ACTION_RW;
|
||||
cpp_params.token = 0;
|
||||
cpp_params.island = sym->domain;
|
||||
cpp_id = nfp_get_numeric_cpp_id(&cpp_params);
|
||||
|
||||
dump_header->cpp.cpp_id = cpp_params;
|
||||
dump_header->cpp.offset = cpu_to_be32(sym->addr);
|
||||
dump_header->cpp.dump_length = cpu_to_be32(sym->size);
|
||||
|
||||
bytes_read = nfp_cpp_read(pf->cpp, cpp_id, sym->addr, dest, sym->size);
|
||||
if (bytes_read != sym->size) {
|
||||
if (bytes_read >= 0)
|
||||
bytes_read = -EIO;
|
||||
dump_header->error = cpu_to_be32(bytes_read);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_dump_for_tlv(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
|
||||
{
|
||||
struct nfp_dumpspec_rtsym *spec_rtsym;
|
||||
struct nfp_dump_state *dump = param;
|
||||
struct nfp_dumpspec_csr *spec_csr;
|
||||
int err;
|
||||
|
||||
switch (be32_to_cpu(tl->type)) {
|
||||
case NFP_DUMPSPEC_TYPE_FWNAME:
|
||||
err = nfp_dump_fwname(pf, dump);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_CPP_CSR:
|
||||
case NFP_DUMPSPEC_TYPE_XPB_CSR:
|
||||
case NFP_DUMPSPEC_TYPE_ME_CSR:
|
||||
spec_csr = (struct nfp_dumpspec_csr *)tl;
|
||||
err = nfp_dump_csr_range(pf, spec_csr, dump);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
|
||||
spec_csr = (struct nfp_dumpspec_csr *)tl;
|
||||
err = nfp_dump_indirect_csr_range(pf, spec_csr, dump);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_RTSYM:
|
||||
spec_rtsym = (struct nfp_dumpspec_rtsym *)tl;
|
||||
err = nfp_dump_single_rtsym(pf, spec_rtsym, dump);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_HWINFO:
|
||||
err = nfp_dump_hwinfo(pf, tl, dump);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
|
||||
err = nfp_dump_hwinfo_field(pf, tl, dump);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
err = nfp_dump_error_tlv(tl, -EOPNOTSUPP, dump);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_dump_specific_level(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
|
||||
void *param)
|
||||
{
|
||||
struct nfp_dump_state *dump = param;
|
||||
|
||||
if (be32_to_cpu(dump_level->type) != dump->requested_level)
|
||||
return 0;
|
||||
|
||||
return nfp_traverse_tlvs(pf, dump_level->data,
|
||||
be32_to_cpu(dump_level->length), dump,
|
||||
nfp_dump_for_tlv);
|
||||
}
|
||||
|
||||
static int nfp_dump_populate_prolog(struct nfp_dump_state *dump)
|
||||
{
|
||||
struct nfp_dump_prolog *prolog = dump->p;
|
||||
u32 total_size;
|
||||
int err;
|
||||
|
||||
total_size = ALIGN8(sizeof(*prolog));
|
||||
|
||||
err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_PROLOG, total_size, dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
prolog->dump_level = cpu_to_be32(dump->requested_level);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec,
|
||||
struct ethtool_dump *dump_param, void *dest)
|
||||
{
|
||||
struct nfp_dump_state dump;
|
||||
int err;
|
||||
|
||||
dump.requested_level = dump_param->flag;
|
||||
dump.dumped_size = 0;
|
||||
dump.p = dest;
|
||||
dump.buf_size = dump_param->len;
|
||||
|
||||
err = nfp_dump_populate_prolog(&dump);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = nfp_traverse_tlvs(pf, spec->data, spec->size, &dump,
|
||||
nfp_dump_specific_level);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Set size of actual dump, to trigger warning if different from
|
||||
* calculated size.
|
||||
*/
|
||||
dump_param->len = dump.dumped_size;
|
||||
|
||||
return 0;
|
||||
}
|
@ -51,14 +51,11 @@
|
||||
#include "nfpcore/nfp.h"
|
||||
#include "nfpcore/nfp_nsp.h"
|
||||
#include "nfp_app.h"
|
||||
#include "nfp_main.h"
|
||||
#include "nfp_net_ctrl.h"
|
||||
#include "nfp_net.h"
|
||||
#include "nfp_port.h"
|
||||
|
||||
enum nfp_dump_diag {
|
||||
NFP_DUMP_NSP_DIAG = 0,
|
||||
};
|
||||
|
||||
struct nfp_et_stat {
|
||||
char name[ETH_GSTRING_LEN];
|
||||
int off;
|
||||
@ -1066,15 +1063,34 @@ exit_release:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set the dump flag/level. Calculate the dump length for flag > 0 only (new TLV
|
||||
* based dumps), since flag 0 (default) calculates the length in
|
||||
* nfp_app_get_dump_flag(), and we need to support triggering a level 0 dump
|
||||
* without setting the flag first, for backward compatibility.
|
||||
*/
|
||||
static int nfp_app_set_dump(struct net_device *netdev, struct ethtool_dump *val)
|
||||
{
|
||||
struct nfp_app *app = nfp_app_from_netdev(netdev);
|
||||
s64 len;
|
||||
|
||||
if (!app)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (val->flag != NFP_DUMP_NSP_DIAG)
|
||||
return -EINVAL;
|
||||
if (val->flag == NFP_DUMP_NSP_DIAG) {
|
||||
app->pf->dump_flag = val->flag;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!app->pf->dumpspec)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
len = nfp_net_dump_calculate_size(app->pf, app->pf->dumpspec,
|
||||
val->flag);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
app->pf->dump_flag = val->flag;
|
||||
app->pf->dump_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1082,14 +1098,37 @@ static int nfp_app_set_dump(struct net_device *netdev, struct ethtool_dump *val)
|
||||
static int
|
||||
nfp_app_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump)
|
||||
{
|
||||
return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, NULL);
|
||||
struct nfp_app *app = nfp_app_from_netdev(netdev);
|
||||
|
||||
if (!app)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (app->pf->dump_flag == NFP_DUMP_NSP_DIAG)
|
||||
return nfp_dump_nsp_diag(app, dump, NULL);
|
||||
|
||||
dump->flag = app->pf->dump_flag;
|
||||
dump->len = app->pf->dump_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_app_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
|
||||
void *buffer)
|
||||
{
|
||||
return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, buffer);
|
||||
struct nfp_app *app = nfp_app_from_netdev(netdev);
|
||||
|
||||
if (!app)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (app->pf->dump_flag == NFP_DUMP_NSP_DIAG)
|
||||
return nfp_dump_nsp_diag(app, dump, buffer);
|
||||
|
||||
dump->flag = app->pf->dump_flag;
|
||||
dump->len = app->pf->dump_len;
|
||||
|
||||
return nfp_net_dump_populate_buffer(app->pf, app->pf->dumpspec, dump,
|
||||
buffer);
|
||||
}
|
||||
|
||||
static int nfp_net_set_coalesce(struct net_device *netdev,
|
||||
|
@ -49,6 +49,8 @@
|
||||
struct nfp_hwinfo;
|
||||
struct nfp_hwinfo *nfp_hwinfo_read(struct nfp_cpp *cpp);
|
||||
const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup);
|
||||
char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo);
|
||||
u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo);
|
||||
|
||||
/* Implemented in nfp_nsp.c, low level functions */
|
||||
|
||||
|
@ -302,3 +302,13 @@ const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo)
|
||||
{
|
||||
return hwinfo->data;
|
||||
}
|
||||
|
||||
u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo)
|
||||
{
|
||||
return le32_to_cpu(hwinfo->size) - sizeof(u32);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user