Merge branch 'octeontx2-af-npc-enhancements'

Ratheesh Kannoth says:

====================
octeontx2-af: npc: Enhancements.

This series extends Marvell octeontx2-af support for CN20K NPC (MCAM
debuggability, allocation policy, default-rule lifetime, optional KPU
profiles from firmware files, X2/X4 MCAM keyword handling in flows and
defaults, and dynamic CN20K NPC private state), adds a devlink mechanism
for multi-value parameters, and moves devlink_nl_param_fill() temporaries
to the heap so stack usage stays reasonable once union devlink_param_value
grows (patch 3).

Patch 1 enforces a single RVU admin-function PCI device in the kernel.
On Octeon series SoCs, hardware resources such as NPC, NIX and related
blocks are global and coordinated by the AF driver; PFs and VFs request
them through AF mailbox messages.  Firmware exposes only one AF PCI
function at boot, so two AF driver instances cannot both own that state.
rvu_probe() rejects a second bind with -EBUSY, logs a warning, clears the
probe gate on early allocation failures, and aligns the driver model with
hardware so reviewers and automation can rely on exactly one bound AF.

Patch 2 improves CN20K MCAM visibility in debugfs: mcam_layout marks
enabled entries, dstats reports per-entry hit deltas (baseline updated in
software after each read; hardware counters are not cleared), and mismatch
lists enabled entries without a PF mapping.

Patch 3 allocates the per-configuration-mode union devlink_param_value
buffers and struct devlink_param_gset_ctx used by devlink_nl_param_fill()
with kcalloc()/kzalloc_obj() and funnels failures through a single cleanup
path so the netlink reply path stays safe as the union grows.

Patch 4 (Saeed) introduces DEVLINK_PARAM_TYPE_U64_ARRAY and nested
DEVLINK_ATTR_PARAM_VALUE_DATA attributes so drivers and user space can
exchange bounded u64 arrays; YAML, uapi, and netlink validation are
updated.

Patch 5 adds a runtime devlink parameter srch_order to reorder CN20K
subbank search during MCAM allocation (the param uses the u64 array type
from patch 4).

Patch 6 ties default MCAM entries to NIX LF alloc/free on CN20K, adds
NIX_LF_DONT_FREE_DFT_IDXS for PF teardown paths that must not drop default
NPC indexes while the driver still owns state, and tightens nix_lf_alloc
error propagation.

Patch 7 allows loading a custom KPU profile from /lib/firmware/kpu via
module parameter kpu_profile, with cam2 / ptype_mask wiring and helpers
that share firmware-sourced vs filesystem-sourced profile layouts.

Patch 8 makes default-rule allocation, AF flow install, and PF-side RSS,
defaults, and ethtool flows respect the active CN20K MCAM keyword width
(X2 vs X4), including X4 reference-index masking and -EOPNOTSUPP when a
flow needs X4 keys on an X2-only profile.

Patch 9 replaces file-scope npc_priv and static dstats with allocation
sized from discovered bank/subbank geometry, threads npc_priv_get()
through CN20K NPC paths, and allocates dstats via devm_kzalloc for the
debugfs helper.

Patch 1 is ordered first so later patches assume a single bound AF.
Heap-backed devlink_nl_param_fill() sits immediately before the U64 array
param work so incremental builds stay stack-safe as the union grows; the
CN20K patches keep srch_order ahead of NIX LF coordination, optional KPU
profile load from firmware files, X2/X4 handling, and the npc_priv refactor
that touches the same files heavily.
====================

Link: https://patch.msgid.link/20260609040453.711932-1-rkannoth@marvell.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-06-13 16:17:01 -07:00
commit 7a446c6bce
20 changed files with 1307 additions and 406 deletions

View File

@ -234,6 +234,10 @@ definitions:
value: 10
-
name: binary
-
name: u64-array
value: 129
-
name: rate-tc-index-max
type: const

View File

@ -13,6 +13,7 @@
#include "struct.h"
#include "rvu.h"
#include "debugfs.h"
#include "cn20k/reg.h"
#include "cn20k/npc.h"
static int npc_mcam_layout_show(struct seq_file *s, void *unused)
@ -58,7 +59,8 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
"v:%u", vidx0);
}
seq_printf(s, "\t%u(%#x) %s\n", idx0, pf1,
seq_printf(s, "\t%u(%#x)%c %s\n", idx0, pf1,
test_bit(idx0, npc_priv->en_map) ? '+' : ' ',
map ? buf0 : " ");
}
goto next;
@ -101,9 +103,13 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
vidx1);
}
seq_printf(s, "%05u(%#x) %s\t\t%05u(%#x) %s\n",
idx1, pf2, v1 ? buf1 : " ",
idx0, pf1, v0 ? buf0 : " ");
seq_printf(s, "%05u(%#x)%c %s\t\t%05u(%#x)%c %s\n",
idx1, pf2,
test_bit(idx1, npc_priv->en_map) ? '+' : ' ',
v1 ? buf1 : " ",
idx0, pf1,
test_bit(idx0, npc_priv->en_map) ? '+' : ' ',
v0 ? buf0 : " ");
continue;
}
@ -120,8 +126,9 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
vidx0);
}
seq_printf(s, "\t\t \t\t%05u(%#x) %s\n", idx0,
pf1, map ? buf0 : " ");
seq_printf(s, "\t\t \t\t%05u(%#x)%c %s\n", idx0, pf1,
test_bit(idx0, npc_priv->en_map) ? '+' : ' ',
map ? buf0 : " ");
continue;
}
@ -134,7 +141,8 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
snprintf(buf1, sizeof(buf1), "v:%05u", vidx1);
}
seq_printf(s, "%05u(%#x) %s\n", idx1, pf1,
seq_printf(s, "%05u(%#x)%c %s\n", idx1, pf1,
test_bit(idx1, npc_priv->en_map) ? '+' : ' ',
map ? buf1 : " ");
}
next:
@ -145,6 +153,137 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused)
DEFINE_SHOW_ATTRIBUTE(npc_mcam_layout);
#define __OCTEONTX2_DEBUGFS_ATTRIBUTE_FOPS(__name) \
static const struct file_operations __name ## _fops = { \
.owner = THIS_MODULE, \
.open = __name ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
#define DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(__name, __size) \
static int __name ## _open(struct inode *inode, struct file *file) \
{ \
return single_open_size(file, __name ## _show, inode->i_private, \
__size); \
} \
__OCTEONTX2_DEBUGFS_ATTRIBUTE_FOPS(__name)
static DEFINE_MUTEX(stats_lock);
/* MAX_NUM_BANKS, MAX_SUBBANK_DEPTH and MAX_NUM_SUB_BANKS represent
* hard limit on all silicon variants, preventing any possibility of
* out-of-bounds access.
*/
static u64 (*dstats)[MAX_NUM_BANKS][MAX_SUBBANK_DEPTH * MAX_NUM_SUB_BANKS];
static int npc_mcam_dstats_show(struct seq_file *s, void *unused)
{
struct npc_priv_t *npc_priv;
int blkaddr, pf, mcam_idx;
u64 stats, delta;
struct rvu *rvu;
char buff[64];
u8 key_type;
void *map;
npc_priv = npc_priv_get();
rvu = s->private;
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
if (blkaddr < 0)
return 0;
mutex_lock(&stats_lock);
seq_puts(s, "idx\tpfunc\tstats\n");
for (int bank = npc_priv->num_banks - 1; bank >= 0; bank--) {
for (int idx = npc_priv->bank_depth - 1; idx >= 0; idx--) {
mcam_idx = bank * npc_priv->bank_depth + idx;
if (npc_mcam_idx_2_key_type(rvu, mcam_idx, &key_type))
continue;
if (key_type == NPC_MCAM_KEY_X4 && bank != 0)
continue;
if (!test_bit(mcam_idx, npc_priv->en_map))
continue;
stats = rvu_read64(rvu, blkaddr,
NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(idx, bank));
if (!stats)
continue;
if (stats == dstats[0][bank][idx])
continue;
if (stats < dstats[0][bank][idx])
dstats[0][bank][idx] = 0;
pf = 0xFFFF;
map = xa_load(&npc_priv->xa_idx2pf_map, mcam_idx);
if (map)
pf = xa_to_value(map);
delta = stats - dstats[0][bank][idx];
snprintf(buff, sizeof(buff), "%u\t%#04x\t%llu\n",
mcam_idx, pf, delta);
seq_puts(s, buff);
dstats[0][bank][idx] = stats;
}
}
mutex_unlock(&stats_lock);
return 0;
}
/* "%u\t%#04x\t%llu\n" needs less than 64 characters to print */
#define TOTAL_SZ (MAX_NUM_BANKS * MAX_NUM_SUB_BANKS * MAX_SUBBANK_DEPTH * 64)
DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(npc_mcam_dstats, TOTAL_SZ);
static int npc_mcam_mismatch_show(struct seq_file *s, void *unused)
{
struct npc_priv_t *npc_priv;
struct npc_subbank *sb;
int mcam_idx, sb_off;
struct rvu *rvu;
char buff[64];
void *map;
int rc;
npc_priv = npc_priv_get();
rvu = s->private;
seq_puts(s, "index\tsb idx\tkw type\n");
for (int bank = npc_priv->num_banks - 1; bank >= 0; bank--) {
for (int idx = npc_priv->bank_depth - 1; idx >= 0; idx--) {
mcam_idx = bank * npc_priv->bank_depth + idx;
if (!test_bit(mcam_idx, npc_priv->en_map))
continue;
map = xa_load(&npc_priv->xa_idx2pf_map, mcam_idx);
if (map)
continue;
rc = npc_mcam_idx_2_subbank_idx(rvu, mcam_idx,
&sb, &sb_off);
if (rc)
continue;
snprintf(buff, sizeof(buff), "%u\t%d\t%u\n",
mcam_idx, sb->idx, sb->key_type);
seq_puts(s, buff);
}
}
return 0;
}
/* "%u\t%d\t%u\n" needs less than 64 characters to print. */
DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(npc_mcam_mismatch, TOTAL_SZ);
static int npc_mcam_default_show(struct seq_file *s, void *unused)
{
struct npc_priv_t *npc_priv;
@ -259,6 +398,16 @@ int npc_cn20k_debugfs_init(struct rvu *rvu)
debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc,
npc_priv, &npc_vidx2idx_map_fops);
dstats = devm_kzalloc(rvu->dev, sizeof(*dstats), GFP_KERNEL);
if (!dstats)
return -ENOMEM;
debugfs_create_file("dstats", 0444, rvu->rvu_dbg.npc, rvu,
&npc_mcam_dstats_fops);
debugfs_create_file("mismatch", 0444, rvu->rvu_dbg.npc, rvu,
&npc_mcam_mismatch_fops);
debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc,
npc_priv, &npc_idx2vidx_map_fops);

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,10 @@
#define MKEX_CN20K_SIGN 0x19bbfdbd160
/* MAX_NUM_BANKS, MAX_SUBBANK_DEPTH and MAX_NUM_SUB_BANKS represent
* hard limit on all silicon variants, preventing any possibility of
* out-of-bounds access on matrix defined using these values.
*/
#define MAX_NUM_BANKS 2
#define MAX_NUM_SUB_BANKS 32
#define MAX_SUBBANK_DEPTH 256
@ -170,6 +174,7 @@ struct npc_defrag_show_node {
* @num_banks: Number of banks.
* @num_subbanks: Number of subbanks.
* @subbank_depth: Depth of subbank.
* @en_map: Enable/disable status.
* @kw: Kex configured key type.
* @sb: Subbank array.
* @xa_sb_used: Array of used subbanks.
@ -178,7 +183,6 @@ struct npc_defrag_show_node {
* @xa_idx2pf_map: Mcam index to PF map.
* @xa_pf_map: Pcifunc to index map.
* @pf_cnt: Number of PFs.
* @init_done: Indicates MCAM initialization is done.
* @xa_pf2dfl_rmap: PF to default rule index map.
* @xa_idx2vidx_map: Mcam index to virtual index map.
* @xa_vidx2idx_map: virtual index to mcam index map.
@ -190,9 +194,12 @@ struct npc_defrag_show_node {
*/
struct npc_priv_t {
int bank_depth;
const int num_banks;
int num_banks;
int num_subbanks;
int subbank_depth;
DECLARE_BITMAP(en_map, MAX_NUM_BANKS *
MAX_NUM_SUB_BANKS *
MAX_SUBBANK_DEPTH);
u8 kw;
struct npc_subbank *sb;
struct xarray xa_sb_used;
@ -206,7 +213,6 @@ struct npc_priv_t {
struct list_head defrag_lh;
struct mutex lock; /* protect defrag nodes */
int pf_cnt;
bool init_done;
};
struct npc_kpm_action0 {
@ -336,5 +342,11 @@ u16 npc_cn20k_vidx2idx(u16 index);
u16 npc_cn20k_idx2vidx(u16 idx);
int npc_cn20k_defrag(struct rvu *rvu);
bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc);
int npc_mcam_idx_2_subbank_idx(struct rvu *rvu, u16 mcam_idx,
struct npc_subbank **sb,
int *sb_off);
const u32 *npc_cn20k_search_order_get(bool *restricted_order, u32 *sz);
int npc_cn20k_search_order_set(struct rvu *rvu, u64 narr[MAX_NUM_SUB_BANKS],
int cnt);
#endif /* NPC_CN20K_H */

View File

@ -1009,6 +1009,7 @@ struct nix_lf_free_req {
struct mbox_msghdr hdr;
#define NIX_LF_DISABLE_FLOWS BIT_ULL(0)
#define NIX_LF_DONT_FREE_TX_VTAG BIT_ULL(1)
#define NIX_LF_DONT_FREE_DFT_IDXS BIT_ULL(2)
u64 flags;
};

View File

@ -267,6 +267,19 @@ struct npc_kpu_profile_cam {
u16 dp2_mask;
} __packed;
struct npc_kpu_profile_cam2 {
u8 state;
u8 state_mask;
u16 dp0;
u16 dp0_mask;
u16 dp1;
u16 dp1_mask;
u16 dp2;
u16 dp2_mask;
u8 ptype;
u8 ptype_mask;
} __packed;
struct npc_kpu_profile_action {
u8 errlev;
u8 errcode;
@ -292,6 +305,10 @@ struct npc_kpu_profile {
int action_entries;
struct npc_kpu_profile_cam *cam;
struct npc_kpu_profile_action *action;
int cam_entries2;
int action_entries2;
struct npc_kpu_profile_action *action2;
struct npc_kpu_profile_cam2 *cam2;
};
/* NPC KPU register formats */

View File

@ -3542,19 +3542,29 @@ static void rvu_update_module_params(struct rvu *rvu)
kpu_profile ? kpu_profile : default_pfl_name, KPU_NAME_LEN);
}
static atomic_t device_bound = ATOMIC_INIT(0);
static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct rvu *rvu;
int err;
if (atomic_cmpxchg(&device_bound, 0, 1) != 0) {
dev_warn(dev, "Only one af device is supported.\n");
return -EBUSY;
}
rvu = devm_kzalloc(dev, sizeof(*rvu), GFP_KERNEL);
if (!rvu)
if (!rvu) {
atomic_set(&device_bound, 0);
return -ENOMEM;
}
rvu->hw = devm_kzalloc(dev, sizeof(struct rvu_hwinfo), GFP_KERNEL);
if (!rvu->hw) {
devm_kfree(dev, rvu);
atomic_set(&device_bound, 0);
return -ENOMEM;
}
@ -3687,6 +3697,7 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_drvdata(pdev, NULL);
devm_kfree(&pdev->dev, rvu->hw);
devm_kfree(dev, rvu);
atomic_set(&device_bound, 0);
return err;
}
@ -3716,6 +3727,7 @@ static void rvu_remove(struct pci_dev *pdev)
cn20k_free_mbox_memory(rvu);
kfree(rvu->ng_rvu);
devm_kfree(&pdev->dev, rvu);
atomic_set(&device_bound, 0);
}
static void rvu_shutdown(struct pci_dev *pdev)

View File

@ -553,17 +553,19 @@ struct npc_kpu_profile_adapter {
const char *name;
u64 version;
const struct npc_lt_def_cfg *lt_def;
const struct npc_kpu_profile_action *ikpu; /* array[pkinds] */
const struct npc_kpu_profile *kpu; /* array[kpus] */
struct npc_kpu_profile_action *ikpu; /* array[pkinds] */
struct npc_kpu_profile_action *ikpu2; /* array[pkinds] */
struct npc_kpu_profile *kpu; /* array[kpus] */
union npc_mcam_key_prfl {
struct npc_mcam_kex *mkex;
const struct npc_mcam_kex *mkex;
/* used for cn9k and cn10k */
struct npc_mcam_kex_extr *mkex_extr; /* used for cn20k */
const struct npc_mcam_kex_extr *mkex_extr; /* used for cn20k */
} mcam_kex_prfl;
struct npc_mcam_kex_hash *mkex_hash;
bool custom;
size_t pkinds;
size_t kpus;
bool from_fs;
};
#define RVU_SWITCH_LBK_CHAN 63
@ -634,7 +636,7 @@ struct rvu {
/* Firmware data */
struct rvu_fwdata *fwdata;
void *kpu_fwdata;
const void *kpu_fwdata;
size_t kpu_fwdata_sz;
void __iomem *kpu_prfl_addr;

View File

@ -1258,6 +1258,7 @@ enum rvu_af_dl_param_id {
RVU_AF_DEVLINK_PARAM_ID_NPC_EXACT_FEATURE_DISABLE,
RVU_AF_DEVLINK_PARAM_ID_NPC_DEF_RULE_CNTR_ENABLE,
RVU_AF_DEVLINK_PARAM_ID_NPC_DEFRAG,
RVU_AF_DEVLINK_PARAM_ID_NPC_SRCH_ORDER,
RVU_AF_DEVLINK_PARAM_ID_NIX_MAXLF,
};
@ -1619,12 +1620,83 @@ static int rvu_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
return 0;
}
static int rvu_af_dl_npc_srch_order_set(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
return npc_cn20k_search_order_set(rvu,
ctx->val.u64arr.val,
ctx->val.u64arr.size);
}
static int rvu_af_dl_npc_srch_order_get(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
bool restricted_order;
const u32 *order;
u32 sz;
order = npc_cn20k_search_order_get(&restricted_order, &sz);
ctx->val.u64arr.size = sz;
for (int i = 0; i < sz; i++)
ctx->val.u64arr.val[i] = order[i];
return 0;
}
static int rvu_af_dl_npc_srch_order_validate(struct devlink *devlink, u32 id,
union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
bool restricted_order;
unsigned long w = 0;
u64 *arr;
u32 sz;
npc_cn20k_search_order_get(&restricted_order, &sz);
if (sz != val->u64arr.size) {
dev_err(rvu->dev,
"Wrong size %llu, should be %u\n",
val->u64arr.size, sz);
return -EINVAL;
}
arr = val->u64arr.val;
for (int i = 0; i < sz; i++) {
if (arr[i] >= sz)
return -EINVAL;
w |= BIT_ULL(arr[i]);
}
if (bitmap_weight(&w, sz) != sz) {
dev_err(rvu->dev,
"Duplicate or out-of-range subbank index. %lu\n",
find_first_zero_bit(&w, sz));
return -EINVAL;
}
return 0;
}
static const struct devlink_ops rvu_devlink_ops = {
.eswitch_mode_get = rvu_devlink_eswitch_mode_get,
.eswitch_mode_set = rvu_devlink_eswitch_mode_set,
};
static const struct devlink_param rvu_af_dl_param_defrag[] = {
static const struct devlink_param rvu_af_dl_cn20k_params[] = {
DEVLINK_PARAM_DRIVER(RVU_AF_DEVLINK_PARAM_ID_NPC_SRCH_ORDER,
"npc_srch_order", DEVLINK_PARAM_TYPE_U64_ARRAY,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
rvu_af_dl_npc_srch_order_get,
rvu_af_dl_npc_srch_order_set,
rvu_af_dl_npc_srch_order_validate),
DEVLINK_PARAM_DRIVER(RVU_AF_DEVLINK_PARAM_ID_NPC_DEFRAG,
"npc_defrag", DEVLINK_PARAM_TYPE_STRING,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
@ -1666,13 +1738,13 @@ int rvu_register_dl(struct rvu *rvu)
}
if (is_cn20k(rvu->pdev)) {
err = devlink_params_register(dl, rvu_af_dl_param_defrag,
ARRAY_SIZE(rvu_af_dl_param_defrag));
err = devlink_params_register(dl, rvu_af_dl_cn20k_params,
ARRAY_SIZE(rvu_af_dl_cn20k_params));
if (err) {
dev_err(rvu->dev,
"devlink defrag params register failed with error %d",
"devlink cn20k params register failed with error %d",
err);
goto err_dl_defrag;
goto err_dl_cn20k_params;
}
}
@ -1695,10 +1767,10 @@ int rvu_register_dl(struct rvu *rvu)
err_dl_exact_match:
if (is_cn20k(rvu->pdev))
devlink_params_unregister(dl, rvu_af_dl_param_defrag,
ARRAY_SIZE(rvu_af_dl_param_defrag));
devlink_params_unregister(dl, rvu_af_dl_cn20k_params,
ARRAY_SIZE(rvu_af_dl_cn20k_params));
err_dl_defrag:
err_dl_cn20k_params:
devlink_params_unregister(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params));
err_dl_health:
@ -1717,8 +1789,8 @@ void rvu_unregister_dl(struct rvu *rvu)
devlink_params_unregister(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params));
if (is_cn20k(rvu->pdev))
devlink_params_unregister(dl, rvu_af_dl_param_defrag,
ARRAY_SIZE(rvu_af_dl_param_defrag));
devlink_params_unregister(dl, rvu_af_dl_cn20k_params,
ARRAY_SIZE(rvu_af_dl_cn20k_params));
/* Unregister exact match devlink only for CN10K-B */
if (rvu_npc_exact_has_match_table(rvu))

View File

@ -16,6 +16,7 @@
#include "cgx.h"
#include "lmac_common.h"
#include "rvu_npc_hash.h"
#include "cn20k/npc.h"
static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc);
static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req,
@ -1499,9 +1500,11 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
struct nix_lf_alloc_req *req,
struct nix_lf_alloc_rsp *rsp)
{
int nixlf, qints, hwctx_size, intf, err, rc = 0;
int nixlf, qints, hwctx_size, intf, rc = 0;
u16 bcast, mcast, promisc, ucast;
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = req->hdr.pcifunc;
bool rules_created = false;
struct rvu_block *block;
struct rvu_pfvf *pfvf;
u64 cfg, ctx_cfg;
@ -1555,8 +1558,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
return NIX_AF_ERR_RSS_GRPS_INVALID;
/* Reset this NIX LF */
err = rvu_lf_reset(rvu, block, nixlf);
if (err) {
rc = rvu_lf_reset(rvu, block, nixlf);
if (rc) {
dev_err(rvu->dev, "Failed to reset NIX%d LF%d\n",
block->addr - BLKADDR_NIX0, nixlf);
return NIX_AF_ERR_LF_RESET;
@ -1566,13 +1569,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
/* Alloc NIX RQ HW context memory and config the base */
hwctx_size = 1UL << ((ctx_cfg >> 4) & 0xF);
err = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
if (err)
rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
if (rc)
goto free_mem;
pfvf->rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL);
if (!pfvf->rq_bmap)
if (!pfvf->rq_bmap) {
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_BASE(nixlf),
(u64)pfvf->rq_ctx->iova);
@ -1583,13 +1588,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
/* Alloc NIX SQ HW context memory and config the base */
hwctx_size = 1UL << (ctx_cfg & 0xF);
err = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size);
if (err)
rc = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size);
if (rc)
goto free_mem;
pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
if (!pfvf->sq_bmap)
if (!pfvf->sq_bmap) {
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_BASE(nixlf),
(u64)pfvf->sq_ctx->iova);
@ -1599,13 +1606,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
/* Alloc NIX CQ HW context memory and config the base */
hwctx_size = 1UL << ((ctx_cfg >> 8) & 0xF);
err = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size);
if (err)
rc = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size);
if (rc)
goto free_mem;
pfvf->cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL);
if (!pfvf->cq_bmap)
if (!pfvf->cq_bmap) {
rc = -ENOMEM;
goto free_mem;
}
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_BASE(nixlf),
(u64)pfvf->cq_ctx->iova);
@ -1615,18 +1624,18 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
/* Initialize receive side scaling (RSS) */
hwctx_size = 1UL << ((ctx_cfg >> 12) & 0xF);
err = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz,
req->rss_grps, hwctx_size, req->way_mask,
!!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER));
if (err)
rc = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz,
req->rss_grps, hwctx_size, req->way_mask,
!!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER));
if (rc)
goto free_mem;
/* Alloc memory for CQINT's HW contexts */
cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
qints = (cfg >> 24) & 0xFFF;
hwctx_size = 1UL << ((ctx_cfg >> 24) & 0xF);
err = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size);
if (err)
rc = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size);
if (rc)
goto free_mem;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_BASE(nixlf),
@ -1639,8 +1648,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
qints = (cfg >> 12) & 0xFFF;
hwctx_size = 1UL << ((ctx_cfg >> 20) & 0xF);
err = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size);
if (err)
rc = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size);
if (rc)
goto free_mem;
rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_BASE(nixlf),
@ -1684,10 +1693,22 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
if (is_sdp_pfvf(rvu, pcifunc))
intf = NIX_INTF_TYPE_SDP;
err = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp,
!!(req->flags & NIX_LF_LBK_BLK_SEL));
if (err)
goto free_mem;
if (is_cn20k(rvu->pdev)) {
rc = npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &bcast, &mcast,
&promisc, &ucast);
if (rc) {
rc = npc_cn20k_dft_rules_alloc(rvu, pcifunc);
if (rc)
goto free_mem;
rules_created = true;
}
}
rc = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp,
!!(req->flags & NIX_LF_LBK_BLK_SEL));
if (rc)
goto free_dft;
/* Disable NPC entries as NIXLF's contexts are not initialized yet */
rvu_npc_disable_default_entries(rvu, pcifunc, nixlf);
@ -1699,9 +1720,12 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
goto exit;
free_dft:
if (is_cn20k(rvu->pdev) && rules_created)
npc_cn20k_dft_rules_free(rvu, pcifunc);
free_mem:
nix_ctx_free(rvu, pfvf);
rc = -ENOMEM;
exit:
/* Set macaddr of this PF/VF */
@ -1775,6 +1799,9 @@ int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct nix_lf_free_req *req,
nix_ctx_free(rvu, pfvf);
if (is_cn20k(rvu->pdev) && !(req->flags & NIX_LF_DONT_FREE_DFT_IDXS))
npc_cn20k_dft_rules_free(rvu, pcifunc);
return 0;
}

View File

@ -1285,11 +1285,18 @@ void npc_enadis_default_mce_entry(struct rvu *rvu, u16 pcifunc,
struct nix_mce_list *mce_list;
int index, blkaddr, mce_idx;
struct rvu_pfvf *pfvf;
u16 ptr[4];
/* multicast pkt replication is not enabled for AF's VFs & SDP links */
if (is_lbk_vf(rvu, pcifunc) || is_sdp_pfvf(rvu, pcifunc))
return;
/* In cn20k, only CGX mapped devices have default MCAST entry */
if (is_cn20k(rvu->pdev) &&
npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1],
&ptr[2], &ptr[3]))
return;
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
if (blkaddr < 0)
return;
@ -1329,9 +1336,12 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc,
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
struct npc_mcam *mcam = &rvu->hw->mcam;
int index, blkaddr;
u16 ptr[4];
/* only CGX or LBK interfaces have default entries */
if (is_cn20k(rvu->pdev) && !npc_is_cgx_or_lbk(rvu, pcifunc))
if (is_cn20k(rvu->pdev) &&
npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1],
&ptr[2], &ptr[3]))
return;
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
@ -1485,7 +1495,8 @@ void rvu_npc_free_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
}
static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr,
struct npc_mcam_kex *mkex, u8 intf)
const struct npc_mcam_kex *mkex,
u8 intf)
{
int lid, lt, ld, fl;
@ -1514,7 +1525,8 @@ static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr,
}
static void npc_program_mkex_tx(struct rvu *rvu, int blkaddr,
struct npc_mcam_kex *mkex, u8 intf)
const struct npc_mcam_kex *mkex,
u8 intf)
{
int lid, lt, ld, fl;
@ -1543,7 +1555,7 @@ static void npc_program_mkex_tx(struct rvu *rvu, int blkaddr,
}
static void npc_program_mkex_profile(struct rvu *rvu, int blkaddr,
struct npc_mcam_kex *mkex)
const struct npc_mcam_kex *mkex)
{
struct rvu_hwinfo *hw = rvu->hw;
u8 intf;
@ -1683,8 +1695,12 @@ static void npc_config_kpucam(struct rvu *rvu, int blkaddr,
const struct npc_kpu_profile_cam *kpucam,
int kpu, int entry)
{
const struct npc_kpu_profile_cam2 *kpucam2 = (void *)kpucam;
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
struct npc_kpu_cam cam0 = {0};
struct npc_kpu_cam cam1 = {0};
u64 *val = (u64 *)&cam1;
u64 *mask = (u64 *)&cam0;
cam1.state = kpucam->state & kpucam->state_mask;
cam1.dp0_data = kpucam->dp0 & kpucam->dp0_mask;
@ -1696,6 +1712,14 @@ static void npc_config_kpucam(struct rvu *rvu, int blkaddr,
cam0.dp1_data = ~kpucam->dp1 & kpucam->dp1_mask;
cam0.dp2_data = ~kpucam->dp2 & kpucam->dp2_mask;
if (profile->from_fs) {
u8 ptype = kpucam2->ptype;
u8 pmask = kpucam2->ptype_mask;
*val |= FIELD_PREP(GENMASK_ULL(57, 56), ptype & pmask);
*mask |= FIELD_PREP(GENMASK_ULL(57, 56), ~ptype & pmask);
}
rvu_write64(rvu, blkaddr,
NPC_AF_KPUX_ENTRYX_CAMX(kpu, entry, 0), *(u64 *)&cam0);
rvu_write64(rvu, blkaddr,
@ -1707,34 +1731,104 @@ u64 npc_enable_mask(int count)
return (((count) < 64) ? ~(BIT_ULL(count) - 1) : (0x00ULL));
}
struct npc_kpu_profile_action *
npc_get_ikpu_nth_entry(struct rvu *rvu, int n)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
if (profile->from_fs)
return &profile->ikpu2[n];
return &profile->ikpu[n];
}
int
npc_get_num_kpu_cam_entries(struct rvu *rvu,
const struct npc_kpu_profile *kpu_pfl)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
if (profile->from_fs)
return kpu_pfl->cam_entries2;
return kpu_pfl->cam_entries;
}
struct npc_kpu_profile_cam *
npc_get_kpu_cam_nth_entry(struct rvu *rvu,
const struct npc_kpu_profile *kpu_pfl, int n)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
if (profile->from_fs)
return (void *)&kpu_pfl->cam2[n];
return (void *)&kpu_pfl->cam[n];
}
int
npc_get_num_kpu_action_entries(struct rvu *rvu,
const struct npc_kpu_profile *kpu_pfl)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
if (profile->from_fs)
return kpu_pfl->action_entries2;
return kpu_pfl->action_entries;
}
struct npc_kpu_profile_action *
npc_get_kpu_action_nth_entry(struct rvu *rvu,
const struct npc_kpu_profile *kpu_pfl,
int n)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
if (profile->from_fs)
return (void *)&kpu_pfl->action2[n];
return (void *)&kpu_pfl->action[n];
}
static void npc_program_kpu_profile(struct rvu *rvu, int blkaddr, int kpu,
const struct npc_kpu_profile *profile)
{
int num_cam_entries, num_action_entries;
int entry, num_entries, max_entries;
u64 entry_mask;
if (profile->cam_entries != profile->action_entries) {
num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile);
num_action_entries = npc_get_num_kpu_action_entries(rvu, profile);
if (num_cam_entries != num_action_entries) {
dev_err(rvu->dev,
"KPU%d: CAM and action entries [%d != %d] not equal\n",
kpu, profile->cam_entries, profile->action_entries);
kpu, num_cam_entries, num_action_entries);
}
max_entries = rvu->hw->npc_kpu_entries;
WARN(num_cam_entries > max_entries,
"KPU%u: err: hw max entries=%u, input entries=%u\n",
kpu, rvu->hw->npc_kpu_entries, num_cam_entries);
/* Program CAM match entries for previous KPU extracted data */
num_entries = min_t(int, profile->cam_entries, max_entries);
num_entries = min_t(int, num_cam_entries, max_entries);
for (entry = 0; entry < num_entries; entry++)
npc_config_kpucam(rvu, blkaddr,
&profile->cam[entry], kpu, entry);
(void *)npc_get_kpu_cam_nth_entry(rvu, profile, entry),
kpu, entry);
/* Program this KPU's actions */
num_entries = min_t(int, profile->action_entries, max_entries);
num_entries = min_t(int, num_action_entries, max_entries);
for (entry = 0; entry < num_entries; entry++)
npc_config_kpuaction(rvu, blkaddr, &profile->action[entry],
npc_config_kpuaction(rvu, blkaddr,
(void *)npc_get_kpu_action_nth_entry(rvu, profile, entry),
kpu, entry, false);
/* Enable all programmed entries */
num_entries = min_t(int, profile->action_entries, profile->cam_entries);
num_entries = min_t(int, num_action_entries, num_cam_entries);
entry_mask = npc_enable_mask(num_entries);
/* Disable first KPU_MAX_CST_ENT entries for built-in profile */
if (!rvu->kpu.custom)
@ -1778,26 +1872,175 @@ static void npc_prepare_default_kpu(struct rvu *rvu,
npc_cn20k_update_action_entries_n_flags(rvu, profile);
}
static int npc_apply_custom_kpu(struct rvu *rvu,
struct npc_kpu_profile_adapter *profile)
static int npc_alloc_kpu_cam2_n_action2(struct rvu *rvu, int kpu_num,
int num_entries)
{
struct npc_kpu_profile_adapter *adapter = &rvu->kpu;
struct npc_kpu_profile *kpu;
kpu = &adapter->kpu[kpu_num];
kpu->cam2 = devm_kcalloc(rvu->dev, num_entries,
sizeof(*kpu->cam2), GFP_KERNEL);
if (!kpu->cam2)
return -ENOMEM;
kpu->action2 = devm_kcalloc(rvu->dev, num_entries,
sizeof(*kpu->action2), GFP_KERNEL);
if (!kpu->action2)
return -ENOMEM;
return 0;
}
static int npc_apply_custom_kpu_from_fw(struct rvu *rvu,
struct npc_kpu_profile_adapter *profile)
{
size_t hdr_sz = sizeof(struct npc_kpu_profile_fwdata), offset = 0;
const struct npc_kpu_profile_fwdata *fw;
struct npc_kpu_profile_action *action;
struct npc_kpu_profile_fwdata *fw;
struct npc_kpu_profile_cam *cam;
struct npc_kpu_fwdata *fw_kpu;
int entries;
u16 kpu, entry;
int entries, entry, kpu;
fw = rvu->kpu_fwdata;
for (kpu = 0; kpu < fw->kpus; kpu++) {
if (rvu->kpu_fwdata_sz < hdr_sz + offset) {
dev_warn(rvu->dev,
"Profile size mismatch on KPU%i parsing\n",
kpu + 1);
return -EINVAL;
}
fw_kpu = (struct npc_kpu_fwdata *)(fw->data + offset);
if (fw_kpu->entries < 0) {
dev_warn(rvu->dev,
"Profile entries is negative on KPU%i parsing\n",
kpu + 1);
return -EINVAL;
}
if (fw_kpu->entries > KPU_MAX_CST_ENT)
dev_warn(rvu->dev,
"Too many custom entries on KPU%d: %d > %d\n",
kpu, fw_kpu->entries, KPU_MAX_CST_ENT);
entries = min_t(int, fw_kpu->entries, KPU_MAX_CST_ENT);
cam = (struct npc_kpu_profile_cam *)fw_kpu->data;
offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam);
action = (struct npc_kpu_profile_action *)(fw->data + offset);
offset += fw_kpu->entries * sizeof(*action);
if (rvu->kpu_fwdata_sz < hdr_sz + offset) {
dev_warn(rvu->dev,
"Profile size mismatch on KPU%i parsing.\n",
kpu + 1);
return -EINVAL;
}
for (entry = 0; entry < entries; entry++) {
profile->kpu[kpu].cam[entry] = cam[entry];
profile->kpu[kpu].action[entry] = action[entry];
}
}
return 0;
}
static int npc_apply_custom_kpu_from_fs(struct rvu *rvu,
struct npc_kpu_profile_adapter *profile)
{
size_t hdr_sz = sizeof(struct npc_kpu_profile_fwdata), offset = 0;
const struct npc_kpu_profile_fwdata *fw;
struct npc_kpu_profile_action *action;
struct npc_kpu_profile_cam2 *cam2;
struct npc_kpu_fwdata *fw_kpu;
int entries, ret, entry, kpu;
fw = rvu->kpu_fwdata;
/* Binary blob contains ikpu actions entries at start of data[0] */
profile->ikpu2 = devm_kcalloc(rvu->dev, 1,
sizeof(ikpu_action_entries),
GFP_KERNEL);
if (!profile->ikpu2)
return -ENOMEM;
action = (struct npc_kpu_profile_action *)(fw->data + offset);
if (rvu->kpu_fwdata_sz < hdr_sz + sizeof(ikpu_action_entries))
return -EINVAL;
/* The firmware layout does dependent on the internal size of
* ikpu_action_entries.
*/
memcpy((void *)profile->ikpu2, action, sizeof(ikpu_action_entries));
offset += sizeof(ikpu_action_entries);
for (kpu = 0; kpu < fw->kpus; kpu++) {
if (rvu->kpu_fwdata_sz < hdr_sz + offset + sizeof(*fw_kpu)) {
dev_warn(rvu->dev,
"profile size mismatch on kpu%i parsing\n",
kpu + 1);
return -EINVAL;
}
fw_kpu = (struct npc_kpu_fwdata *)(fw->data + offset);
if (fw_kpu->entries <= 0) {
dev_warn(rvu->dev,
"Invalid kpu entries on KPU%d\n", kpu);
return -EINVAL;
}
entries = min_t(int, fw_kpu->entries, rvu->hw->npc_kpu_entries);
dev_info(rvu->dev,
"Loading %u entries on KPU%d\n", entries, kpu);
cam2 = (struct npc_kpu_profile_cam2 *)fw_kpu->data;
offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam2);
action = (struct npc_kpu_profile_action *)(fw->data + offset);
offset += fw_kpu->entries * sizeof(*action);
if (rvu->kpu_fwdata_sz < hdr_sz + offset) {
dev_warn(rvu->dev,
"profile size mismatch on kpu%i parsing.\n",
kpu + 1);
return -EINVAL;
}
profile->kpu[kpu].cam_entries2 = entries;
profile->kpu[kpu].action_entries2 = entries;
ret = npc_alloc_kpu_cam2_n_action2(rvu, kpu, entries);
if (ret) {
dev_warn(rvu->dev,
"profile entry allocation failed for kpu=%d for %d entries\n",
kpu, entries);
return -EINVAL;
}
for (entry = 0; entry < entries; entry++) {
profile->kpu[kpu].cam2[entry] = cam2[entry];
profile->kpu[kpu].action2[entry] = action[entry];
}
}
return 0;
}
static int npc_apply_custom_kpu(struct rvu *rvu,
struct npc_kpu_profile_adapter *profile,
bool from_fs, int *fw_kpus)
{
size_t hdr_sz = sizeof(struct npc_kpu_profile_fwdata);
const struct npc_kpu_profile_fwdata *fw;
struct npc_kpu_profile_fwdata *sfw;
if (is_cn20k(rvu->pdev))
return npc_cn20k_apply_custom_kpu(rvu, profile);
fw = rvu->kpu_fwdata;
if (rvu->kpu_fwdata_sz < hdr_sz) {
dev_warn(rvu->dev, "Invalid KPU profile size\n");
return -EINVAL;
}
fw = rvu->kpu_fwdata;
if (le64_to_cpu(fw->signature) != KPU_SIGN) {
dev_warn(rvu->dev, "Invalid KPU profile signature %llx\n",
fw->signature);
@ -1825,42 +2068,38 @@ static int npc_apply_custom_kpu(struct rvu *rvu,
return -EINVAL;
}
/* Verify if profile fits the HW */
if (fw->kpus > rvu->hw->npc_kpus) {
dev_warn(rvu->dev, "Not enough KPUs: %d > %d\n", fw->kpus,
rvu->hw->npc_kpus);
return -EINVAL;
}
/* Check if there is enough memory for fw loading.
* Check if there is enough entries for profile->kpu[] to
* set cam_entries2 and action_entries2
*/
if (fw->kpus > profile->kpus) {
dev_warn(rvu->dev, "Not enough KPUs: %d > %ld\n", fw->kpus,
dev_warn(rvu->dev, "Not enough KPUs: %d > %zu\n", fw->kpus,
profile->kpus);
return -EINVAL;
}
*fw_kpus = fw->kpus;
sfw = devm_kcalloc(rvu->dev, 1, sizeof(*sfw), GFP_KERNEL);
if (!sfw)
return -ENOMEM;
memcpy(sfw, fw, sizeof(*sfw));
profile->custom = 1;
profile->name = fw->name;
profile->name = sfw->name;
profile->version = le64_to_cpu(fw->version);
profile->mcam_kex_prfl.mkex = &fw->mkex;
profile->lt_def = &fw->lt_def;
profile->mcam_kex_prfl.mkex = &sfw->mkex;
profile->lt_def = &sfw->lt_def;
for (kpu = 0; kpu < fw->kpus; kpu++) {
fw_kpu = (struct npc_kpu_fwdata *)(fw->data + offset);
if (fw_kpu->entries > KPU_MAX_CST_ENT)
dev_warn(rvu->dev,
"Too many custom entries on KPU%d: %d > %d\n",
kpu, fw_kpu->entries, KPU_MAX_CST_ENT);
entries = min(fw_kpu->entries, KPU_MAX_CST_ENT);
cam = (struct npc_kpu_profile_cam *)fw_kpu->data;
offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam);
action = (struct npc_kpu_profile_action *)(fw->data + offset);
offset += fw_kpu->entries * sizeof(*action);
if (rvu->kpu_fwdata_sz < hdr_sz + offset) {
dev_warn(rvu->dev,
"Profile size mismatch on KPU%i parsing.\n",
kpu + 1);
return -EINVAL;
}
for (entry = 0; entry < entries; entry++) {
profile->kpu[kpu].cam[entry] = cam[entry];
profile->kpu[kpu].action[entry] = action[entry];
}
}
return 0;
return from_fs ? npc_apply_custom_kpu_from_fs(rvu, profile) :
npc_apply_custom_kpu_from_fw(rvu, profile);
}
static int npc_load_kpu_prfl_img(struct rvu *rvu, void __iomem *prfl_addr,
@ -1948,45 +2187,19 @@ static int npc_load_kpu_profile_fwdb(struct rvu *rvu, const char *kpu_profile)
return ret;
}
void npc_load_kpu_profile(struct rvu *rvu)
static int npc_load_kpu_profile_from_fw(struct rvu *rvu)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
const char *kpu_profile = rvu->kpu_pfl_name;
const struct firmware *fw = NULL;
bool retry_fwdb = false;
int fw_kpus = 0;
/* If user not specified profile customization */
if (!strncmp(kpu_profile, def_pfl_name, KPU_NAME_LEN))
goto revert_to_default;
/* First prepare default KPU, then we'll customize top entries. */
npc_prepare_default_kpu(rvu, profile);
/* Order of preceedence for load loading NPC profile (high to low)
* Firmware binary in filesystem.
* Firmware database method.
* Default KPU profile.
*/
if (!request_firmware_direct(&fw, kpu_profile, rvu->dev)) {
dev_info(rvu->dev, "Loading KPU profile from firmware: %s\n",
kpu_profile);
rvu->kpu_fwdata = kzalloc(fw->size, GFP_KERNEL);
if (rvu->kpu_fwdata) {
memcpy(rvu->kpu_fwdata, fw->data, fw->size);
rvu->kpu_fwdata_sz = fw->size;
}
release_firmware(fw);
retry_fwdb = true;
goto program_kpu;
}
load_image_fwdb:
/* Loading the KPU profile using firmware database */
if (npc_load_kpu_profile_fwdb(rvu, kpu_profile))
goto revert_to_default;
return -EFAULT;
program_kpu:
/* Apply profile customization if firmware was loaded. */
if (!rvu->kpu_fwdata_sz || npc_apply_custom_kpu(rvu, profile)) {
if (!rvu->kpu_fwdata_sz ||
npc_apply_custom_kpu(rvu, profile, false, &fw_kpus)) {
/* If image from firmware filesystem fails to load or invalid
* retry with firmware database method.
*/
@ -2000,10 +2213,6 @@ void npc_load_kpu_profile(struct rvu *rvu)
}
rvu->kpu_fwdata = NULL;
rvu->kpu_fwdata_sz = 0;
if (retry_fwdb) {
retry_fwdb = false;
goto load_image_fwdb;
}
}
dev_warn(rvu->dev,
@ -2011,22 +2220,101 @@ void npc_load_kpu_profile(struct rvu *rvu)
kpu_profile);
kfree(rvu->kpu_fwdata);
rvu->kpu_fwdata = NULL;
goto revert_to_default;
return -EFAULT;
}
dev_info(rvu->dev, "Using custom profile '%s', version %d.%d.%d\n",
dev_info(rvu->dev, "Using custom profile '%.32s', version %d.%d.%d\n",
profile->name, NPC_KPU_VER_MAJ(profile->version),
NPC_KPU_VER_MIN(profile->version),
NPC_KPU_VER_PATCH(profile->version));
return;
return 0;
}
static int npc_load_kpu_profile_from_fs(struct rvu *rvu)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
const char *kpu_profile = rvu->kpu_pfl_name;
const struct firmware *fw = NULL;
int ret, fw_kpus = 0;
char path[512] = "kpu/";
if (strlen(kpu_profile) > sizeof(path) - strlen("kpu/") - 1) {
dev_err(rvu->dev, "kpu profile name is too big\n");
return -ENOSPC;
}
strcat(path, kpu_profile);
if (request_firmware_direct(&fw, path, rvu->dev))
return -ENOENT;
dev_info(rvu->dev, "Loading KPU profile from filesystem: %s\n",
path);
rvu->kpu_fwdata = fw->data;
rvu->kpu_fwdata_sz = fw->size;
ret = npc_apply_custom_kpu(rvu, profile, true, &fw_kpus);
release_firmware(fw);
rvu->kpu_fwdata = NULL;
if (ret) {
rvu->kpu_fwdata_sz = 0;
dev_err(rvu->dev,
"Loading KPU profile from filesystem failed\n");
return ret;
}
/* In firmware loading from filesystem method, all entries are from
* same binary blob.
*/
rvu->kpu.kpus = fw_kpus;
profile->kpus = fw_kpus;
profile->from_fs = true;
return 0;
}
void npc_load_kpu_profile(struct rvu *rvu)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
const char *kpu_profile = rvu->kpu_pfl_name;
profile->from_fs = false;
npc_prepare_default_kpu(rvu, profile);
/* If user not specified profile customization */
if (!strncmp(kpu_profile, def_pfl_name, KPU_NAME_LEN))
return;
/* Order of preceedence for load loading NPC profile (high to low)
* Firmware binary in filesystem.
* Firmware database method.
* Default KPU profile.
*/
/* Filesystem-based KPU loading is not supported on cn20k.
* npc_prepare_default_kpu() was invoked earlier, but control
* reached this point because the default profile was not selected.
* No need to call it again.
*/
if (!is_cn20k(rvu->pdev)) {
if (!npc_load_kpu_profile_from_fs(rvu))
return;
}
/* First prepare default KPU, then we'll customize top entries. */
npc_prepare_default_kpu(rvu, profile);
if (!npc_load_kpu_profile_from_fw(rvu))
return;
revert_to_default:
npc_prepare_default_kpu(rvu, profile);
}
static void npc_parser_profile_init(struct rvu *rvu, int blkaddr)
{
struct npc_kpu_profile_adapter *profile = &rvu->kpu;
struct rvu_hwinfo *hw = rvu->hw;
int num_pkinds, num_kpus, idx;
@ -2050,7 +2338,9 @@ static void npc_parser_profile_init(struct rvu *rvu, int blkaddr)
num_pkinds = min_t(int, hw->npc_pkinds, num_pkinds);
for (idx = 0; idx < num_pkinds; idx++)
npc_config_kpuaction(rvu, blkaddr, &rvu->kpu.ikpu[idx], 0, idx, true);
npc_config_kpuaction(rvu, blkaddr,
npc_get_ikpu_nth_entry(rvu, idx),
0, idx, true);
/* Program KPU CAM and Action profiles */
num_kpus = rvu->kpu.kpus;
@ -2058,6 +2348,11 @@ static void npc_parser_profile_init(struct rvu *rvu, int blkaddr)
for (idx = 0; idx < num_kpus; idx++)
npc_program_kpu_profile(rvu, blkaddr, idx, &rvu->kpu.kpu[idx]);
if (profile->from_fs) {
rvu_write64(rvu, blkaddr, NPC_AF_PKINDX_TYPE(54), 0x03);
rvu_write64(rvu, blkaddr, NPC_AF_PKINDX_TYPE(58), 0x03);
}
}
void npc_mcam_rsrcs_deinit(struct rvu *rvu)
@ -2287,18 +2582,21 @@ static void rvu_npc_hw_init(struct rvu *rvu, int blkaddr)
static void rvu_npc_setup_interfaces(struct rvu *rvu, int blkaddr)
{
struct npc_mcam_kex_extr *mkex_extr = rvu->kpu.mcam_kex_prfl.mkex_extr;
struct npc_mcam_kex *mkex = rvu->kpu.mcam_kex_prfl.mkex;
const struct npc_mcam_kex_extr *mkex_extr;
struct npc_mcam *mcam = &rvu->hw->mcam;
struct rvu_hwinfo *hw = rvu->hw;
const struct npc_mcam_kex *mkex;
u64 nibble_ena, rx_kex, tx_kex;
u64 *keyx_cfg, reg;
u8 intf;
mkex_extr = rvu->kpu.mcam_kex_prfl.mkex_extr;
mkex = rvu->kpu.mcam_kex_prfl.mkex;
if (is_cn20k(rvu->pdev)) {
keyx_cfg = mkex_extr->keyx_cfg;
keyx_cfg = (u64 *)mkex_extr->keyx_cfg;
} else {
keyx_cfg = mkex->keyx_cfg;
keyx_cfg = (u64 *)mkex->keyx_cfg;
/* Reserve last counter for MCAM RX miss action which is set to
* drop packet. This way we will know how many pkts didn't
* match any MCAM entry.
@ -4085,12 +4383,10 @@ void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf)
ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_UCAST_ENTRY);
if (ucast_idx < 0) {
dev_err(rvu->dev,
"%s: Error to get ucast entry for pcifunc=%#x\n",
__func__, pcifunc);
/* In cn20k, default rules are freed before detach rsrc */
if (ucast_idx < 0)
return;
}
npc_enable_mcam_entry(rvu, mcam, blkaddr, ucast_idx, false);

View File

@ -18,4 +18,21 @@ int npc_fwdb_prfl_img_map(struct rvu *rvu, void __iomem **prfl_img_addr,
void npc_mcam_clear_bit(struct npc_mcam *mcam, u16 index);
void npc_mcam_set_bit(struct npc_mcam *mcam, u16 index);
struct npc_kpu_profile_action *
npc_get_ikpu_nth_entry(struct rvu *rvu, int n);
int
npc_get_num_kpu_cam_entries(struct rvu *rvu,
const struct npc_kpu_profile *kpu_pfl);
struct npc_kpu_profile_cam *
npc_get_kpu_cam_nth_entry(struct rvu *rvu,
const struct npc_kpu_profile *kpu_pfl, int n);
int
npc_get_num_kpu_action_entries(struct rvu *rvu,
const struct npc_kpu_profile *kpu_pfl);
struct npc_kpu_profile_action *
npc_get_kpu_action_nth_entry(struct rvu *rvu,
const struct npc_kpu_profile *kpu_pfl, int n);
#endif /* RVU_NPC_H */

View File

@ -1671,9 +1671,11 @@ rvu_npc_alloc_entry_for_flow_install(struct rvu *rvu,
{
struct npc_mcam_alloc_entry_req entry_req;
struct npc_mcam_alloc_entry_rsp entry_rsp;
struct npc_get_pfl_info_rsp rsp = { 0 };
struct npc_get_num_kws_req kws_req;
struct npc_get_num_kws_rsp kws_rsp;
int off, kw_bits, rc;
struct msg_req req;
u8 *src, *dst;
if (!is_cn20k(rvu->pdev)) {
@ -1697,8 +1699,16 @@ rvu_npc_alloc_entry_for_flow_install(struct rvu *rvu,
kw_bits = kws_rsp.kws * 64;
*kw_type = NPC_MCAM_KEY_X2;
if (kw_bits > 256)
if (kw_bits > 256) {
rvu_mbox_handler_npc_get_pfl_info(rvu, &req, &rsp);
if (rsp.kw_type == NPC_MCAM_KEY_X2) {
dev_err(rvu->dev,
"Only X2 entries are supported in X2 profile\n");
return -EOPNOTSUPP;
}
*kw_type = NPC_MCAM_KEY_X4;
}
memset(&entry_req, 0, sizeof(entry_req));
memset(&entry_rsp, 0, sizeof(entry_rsp));

View File

@ -596,6 +596,7 @@
#define NPC_AF_INTFX_KEX_CFG(a) (0x01010 | (a) << 8)
#define NPC_AF_PKINDX_ACTION0(a) (0x80000ull | (a) << 6)
#define NPC_AF_PKINDX_ACTION1(a) (0x80008ull | (a) << 6)
#define NPC_AF_PKINDX_TYPE(a) (0x80010ull | (a) << 6)
#define NPC_AF_PKINDX_CPI_DEFX(a, b) (0x80020ull | (a) << 6 | (b) << 3)
#define NPC_AF_KPUX_ENTRYX_CAMX(a, b, c) \
(0x100000 | (a) << 14 | (b) << 6 | (c) << 3)

View File

@ -37,14 +37,13 @@ static void otx2_clear_ntuple_flow_info(struct otx2_nic *pfvf, struct otx2_flow_
flow_cfg->max_flows = 0;
}
static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, bool *is_x2,
u16 *x4_slots)
static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, u16 *x4_slots, u8 *kw_type)
{
struct npc_get_pfl_info_rsp *rsp;
struct msg_req *req;
static struct {
bool is_set;
bool is_x2;
u8 kw_type;
u16 x4_slots;
} pfl_info;
@ -53,8 +52,8 @@ static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, bool *is_x2,
*/
mutex_lock(&pfvf->mbox.lock);
if (pfl_info.is_set) {
*is_x2 = pfl_info.is_x2;
*x4_slots = pfl_info.x4_slots;
*kw_type = pfl_info.kw_type;
mutex_unlock(&pfvf->mbox.lock);
return 0;
}
@ -79,16 +78,16 @@ static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, bool *is_x2,
return -EFAULT;
}
*is_x2 = (rsp->kw_type == NPC_MCAM_KEY_X2);
if (*is_x2)
*x4_slots = 0;
pfl_info.kw_type = rsp->kw_type;
if (rsp->kw_type == NPC_MCAM_KEY_X2)
pfl_info.x4_slots = 0;
else
*x4_slots = rsp->x4_slots;
pfl_info.is_x2 = *is_x2;
pfl_info.x4_slots = *x4_slots;
pfl_info.x4_slots = rsp->x4_slots;
pfl_info.is_set = true;
*x4_slots = pfl_info.x4_slots;
*kw_type = pfl_info.kw_type;
mutex_unlock(&pfvf->mbox.lock);
return 0;
}
@ -164,6 +163,7 @@ int otx2_alloc_mcam_entries(struct otx2_nic *pfvf, u16 count)
u16 dft_idx = 0, x4_slots = 0;
int ent, allocated = 0, ref;
bool is_x2 = false;
u8 kw_type = 0;
int rc;
/* Free current ones and allocate new ones with requested count */
@ -182,12 +182,14 @@ int otx2_alloc_mcam_entries(struct otx2_nic *pfvf, u16 count)
}
if (is_cn20k(pfvf->pdev)) {
rc = otx2_mcam_pfl_info_get(pfvf, &is_x2, &x4_slots);
rc = otx2_mcam_pfl_info_get(pfvf, &x4_slots, &kw_type);
if (rc) {
netdev_err(pfvf->netdev, "Error to retrieve profile info\n");
return rc;
}
is_x2 = kw_type == NPC_MCAM_KEY_X2;
rc = otx2_get_dft_rl_idx(pfvf, &dft_idx);
if (rc) {
netdev_err(pfvf->netdev,
@ -289,6 +291,8 @@ int otx2_mcam_entry_init(struct otx2_nic *pfvf)
struct npc_mcam_alloc_entry_rsp *rsp;
int vf_vlan_max_flows, count;
int rc, ref, prio, ent;
u8 kw_type = 0;
u16 x4_slots;
u16 dft_idx;
ref = 0;
@ -315,6 +319,16 @@ int otx2_mcam_entry_init(struct otx2_nic *pfvf)
if (!flow_cfg->def_ent)
return -ENOMEM;
kw_type = NPC_MCAM_KEY_X2;
if (is_cn20k(pfvf->pdev)) {
rc = otx2_mcam_pfl_info_get(pfvf, &x4_slots, &kw_type);
if (rc) {
netdev_err(pfvf->netdev,
"Error to get pfl info\n");
return rc;
}
}
mutex_lock(&pfvf->mbox.lock);
req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(&pfvf->mbox);
@ -324,6 +338,10 @@ int otx2_mcam_entry_init(struct otx2_nic *pfvf)
}
req->kw_type = NPC_MCAM_KEY_X2;
if (is_cn20k(pfvf->pdev) && kw_type == NPC_MCAM_KEY_X4) {
req->kw_type = NPC_MCAM_KEY_X4;
ref &= (x4_slots - 1);
}
req->contig = false;
req->count = count;
req->ref_prio = prio;
@ -1174,15 +1192,14 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow)
#ifdef CONFIG_DCB
int vlan_prio, qidx, pfc_rule = 0;
#endif
bool modify = false, is_x2;
int err, vf = 0, off, sz;
bool modify = false;
u8 kw_type = 0;
u8 *src, *dst;
u16 x4_slots;
bool is_x2;
if (is_cn20k(pfvf->pdev)) {
err = otx2_mcam_pfl_info_get(pfvf, &is_x2, &x4_slots);
err = otx2_mcam_pfl_info_get(pfvf, &x4_slots, &kw_type);
if (err) {
netdev_err(pfvf->netdev,
"Error to retrieve NPC profile info, pcifunc=%#x\n",
@ -1190,6 +1207,7 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow)
return -EFAULT;
}
is_x2 = kw_type == NPC_MCAM_KEY_X2;
if (!is_x2) {
err = otx2_prepare_flow_request(&flow->flow_spec,
&treq);

View File

@ -1053,7 +1053,6 @@ irqreturn_t otx2_pfaf_mbox_intr_handler(int irq, void *pf_irq)
/* Clear the IRQ */
otx2_write64(pf, RVU_PF_INT, BIT_ULL(0));
mbox_data = otx2_read64(pf, RVU_PF_PFAF_MBOX0);
if (mbox_data & MBOX_UP_MSG) {
@ -1729,7 +1728,7 @@ int otx2_init_hw_resources(struct otx2_nic *pf)
mutex_lock(&mbox->lock);
free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox);
if (free_req) {
free_req->flags = NIX_LF_DISABLE_FLOWS;
free_req->flags = NIX_LF_DISABLE_FLOWS | NIX_LF_DONT_FREE_DFT_IDXS;
if (otx2_sync_mbox_msg(mbox))
dev_err(pf->dev, "%s failed to free nixlf\n", __func__);
}
@ -1803,7 +1802,7 @@ void otx2_free_hw_resources(struct otx2_nic *pf)
/* Reset NIX LF */
free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox);
if (free_req) {
free_req->flags = NIX_LF_DISABLE_FLOWS;
free_req->flags = NIX_LF_DISABLE_FLOWS | NIX_LF_DONT_FREE_DFT_IDXS;
if (!(pf->flags & OTX2_FLAG_PF_SHUTDOWN))
free_req->flags |= NIX_LF_DONT_FREE_TX_VTAG;
if (otx2_sync_mbox_msg(mbox))
@ -1926,7 +1925,6 @@ int otx2_alloc_queue_mem(struct otx2_nic *pf)
struct otx2_qset *qset = &pf->qset;
struct otx2_cq_poll *cq_poll;
/* RQ and SQs are mapped to different CQs,
* so find out max CQ IRQs (i.e CINTs) needed.
*/

View File

@ -433,6 +433,13 @@ enum devlink_param_type {
DEVLINK_PARAM_TYPE_U64 = DEVLINK_VAR_ATTR_TYPE_U64,
DEVLINK_PARAM_TYPE_STRING = DEVLINK_VAR_ATTR_TYPE_STRING,
DEVLINK_PARAM_TYPE_BOOL = DEVLINK_VAR_ATTR_TYPE_FLAG,
DEVLINK_PARAM_TYPE_U64_ARRAY = DEVLINK_VAR_ATTR_TYPE_U64_ARRAY,
};
#define __DEVLINK_PARAM_MAX_ARRAY_SIZE 32
struct devlink_param_u64_array {
u64 size;
u64 val[__DEVLINK_PARAM_MAX_ARRAY_SIZE];
};
union devlink_param_value {
@ -442,6 +449,7 @@ union devlink_param_value {
u64 vu64;
char vstr[__DEVLINK_PARAM_MAX_STRING_VALUE];
bool vbool;
struct devlink_param_u64_array u64arr;
};
struct devlink_param_gset_ctx {

View File

@ -406,6 +406,7 @@ enum devlink_var_attr_type {
DEVLINK_VAR_ATTR_TYPE_BINARY,
__DEVLINK_VAR_ATTR_TYPE_CUSTOM_BASE = 0x80,
/* Any possible custom types, unrelated to NLA_* values go below */
DEVLINK_VAR_ATTR_TYPE_U64_ARRAY,
};
enum devlink_attr {

View File

@ -37,6 +37,8 @@ devlink_attr_param_type_validate(const struct nlattr *attr,
case DEVLINK_VAR_ATTR_TYPE_NUL_STRING:
fallthrough;
case DEVLINK_VAR_ATTR_TYPE_BINARY:
fallthrough;
case DEVLINK_VAR_ATTR_TYPE_U64_ARRAY:
return 0;
}
NL_SET_ERR_MSG_ATTR(extack, attr, "invalid enum value");

View File

@ -252,6 +252,15 @@ devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type,
return -EMSGSIZE;
}
break;
case DEVLINK_PARAM_TYPE_U64_ARRAY:
if (val->u64arr.size > __DEVLINK_PARAM_MAX_ARRAY_SIZE)
return -EMSGSIZE;
for (int i = 0; i < val->u64arr.size; i++) {
if (nla_put_uint(msg, nla_type, val->u64arr.val[i]))
return -EMSGSIZE;
}
break;
}
return 0;
}
@ -304,56 +313,79 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
u32 portid, u32 seq, int flags,
struct netlink_ext_ack *extack)
{
union devlink_param_value default_value[DEVLINK_PARAM_CMODE_MAX + 1];
union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1];
bool default_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
const struct devlink_param *param = param_item->param;
struct devlink_param_gset_ctx ctx;
union devlink_param_value *default_value;
union devlink_param_value *param_value;
struct devlink_param_gset_ctx *ctx;
struct nlattr *param_values_list;
struct nlattr *param_attr;
void *hdr;
int err;
int i;
default_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1,
sizeof(*default_value), GFP_KERNEL);
if (!default_value)
return -ENOMEM;
param_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1,
sizeof(*param_value), GFP_KERNEL);
if (!param_value) {
kfree(default_value);
return -ENOMEM;
}
ctx = kzalloc_obj(*ctx);
if (!ctx) {
kfree(param_value);
kfree(default_value);
return -ENOMEM;
}
/* Get value from driver part to driverinit configuration mode */
for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
if (!devlink_param_cmode_is_supported(param, i))
continue;
if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) {
if (param_item->driverinit_value_new_valid)
if (param_item->driverinit_value_new_valid) {
param_value[i] = param_item->driverinit_value_new;
else if (param_item->driverinit_value_valid)
} else if (param_item->driverinit_value_valid) {
param_value[i] = param_item->driverinit_value;
else
return -EOPNOTSUPP;
} else {
err = -EOPNOTSUPP;
goto get_put_fail;
}
if (param_item->driverinit_value_valid) {
default_value[i] = param_item->driverinit_default;
default_value_set[i] = true;
}
} else {
ctx.cmode = i;
err = devlink_param_get(devlink, param, &ctx, extack);
ctx->cmode = i;
err = devlink_param_get(devlink, param, ctx, extack);
if (err)
return err;
param_value[i] = ctx.val;
goto get_put_fail;
err = devlink_param_get_default(devlink, param, &ctx,
param_value[i] = ctx->val;
err = devlink_param_get_default(devlink, param, ctx,
extack);
if (!err) {
default_value[i] = ctx.val;
default_value[i] = ctx->val;
default_value_set[i] = true;
} else if (err != -EOPNOTSUPP) {
return err;
goto get_put_fail;
}
}
param_value_set[i] = true;
}
err = -EMSGSIZE;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
goto get_put_fail;
if (devlink_nl_put_handle(msg, devlink))
goto genlmsg_cancel;
@ -393,6 +425,9 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
nla_nest_end(msg, param_values_list);
nla_nest_end(msg, param_attr);
genlmsg_end(msg, hdr);
kfree(default_value);
kfree(param_value);
kfree(ctx);
return 0;
values_list_nest_cancel:
@ -401,7 +436,11 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
nla_nest_cancel(msg, param_attr);
genlmsg_cancel:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
get_put_fail:
kfree(default_value);
kfree(param_value);
kfree(ctx);
return err;
}
static void devlink_param_notify(struct devlink *devlink,
@ -507,7 +546,7 @@ devlink_param_value_get_from_info(const struct devlink_param *param,
union devlink_param_value *value)
{
struct nlattr *param_data;
int len;
int len, cnt, rem;
param_data = info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA];
@ -547,6 +586,28 @@ devlink_param_value_get_from_info(const struct devlink_param *param,
return -EINVAL;
value->vbool = nla_get_flag(param_data);
break;
case DEVLINK_PARAM_TYPE_U64_ARRAY:
cnt = 0;
nla_for_each_attr_type(param_data,
DEVLINK_ATTR_PARAM_VALUE_DATA,
genlmsg_data(info->genlhdr),
genlmsg_len(info->genlhdr), rem) {
if (cnt >= __DEVLINK_PARAM_MAX_ARRAY_SIZE)
return -EMSGSIZE;
if ((nla_len(param_data) != sizeof(u64)) &&
(nla_len(param_data) != sizeof(u32))) {
NL_SET_BAD_ATTR(info->extack, param_data);
return -EINVAL;
}
value->u64arr.val[cnt] = nla_get_uint(param_data);
cnt++;
}
value->u64arr.size = cnt;
break;
}
return 0;
}