Flashing updated firmware on deployed devices is cumbersome. Provide a
mechanism to load a custom KPU (Key Parse Unit) profile directly from
the filesystem at module load time.
When the rvu_af module is loaded with the kpu_profile parameter, the
specified profile is read from /lib/firmware/kpu and programmed into
the KPU registers. Add npc_kpu_profile_cam2 for the extended cam format
used by filesystem-loaded profiles and support ptype/ptype_mask in
npc_config_kpucam when profile->from_fs is set.
Usage:
1. Copy the KPU profile file to /lib/firmware/kpu.
2. Build OCTEONTX2_AF as a module.
3. Load: insmod rvu_af.ko kpu_profile=<profile_name>
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../ethernet/marvell/octeontx2/af/cn20k/npc.c | 21 +-
.../net/ethernet/marvell/octeontx2/af/npc.h | 17 ++
.../net/ethernet/marvell/octeontx2/af/rvu.h | 6 +-
.../ethernet/marvell/octeontx2/af/rvu_npc.c | 269 +++++++++++++++---
.../ethernet/marvell/octeontx2/af/rvu_npc.h | 17 ++
.../ethernet/marvell/octeontx2/af/rvu_reg.h | 1 +
6 files changed, 285 insertions(+), 46 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 8dad272fd069..c8c09a91b13f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -521,13 +521,17 @@ npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr,
int kpm, int start_entry,
const struct npc_kpu_profile *profile)
{
+ int num_cam_entries, num_action_entries;
int entry, num_entries, max_entries;
u64 idx;
- 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,
"kpm%d: CAM and action entries [%d != %d] not equal\n",
- kpm, profile->cam_entries, profile->action_entries);
+ kpm, num_cam_entries, num_action_entries);
WARN(1, "Fatal error\n");
return;
@@ -536,16 +540,18 @@ npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr,
max_entries = rvu->hw->npc_kpu_entries / 2;
entry = start_entry;
/* Program CAM match entries for previous kpm extracted data */
- num_entries = min_t(int, profile->cam_entries, max_entries);
+ num_entries = min_t(int, num_cam_entries, max_entries);
for (idx = 0; entry < num_entries + start_entry; entry++, idx++)
- npc_config_kpmcam(rvu, blkaddr, &profile->cam[idx],
+ npc_config_kpmcam(rvu, blkaddr,
+ npc_get_kpu_cam_nth_entry(rvu, profile, idx),
kpm, entry);
entry = start_entry;
/* Program this kpm's actions */
- num_entries = min_t(int, profile->action_entries, max_entries);
+ num_entries = min_t(int, num_action_entries, max_entries);
for (idx = 0; entry < num_entries + start_entry; entry++, idx++)
- npc_config_kpmaction(rvu, blkaddr, &profile->action[idx],
+ npc_config_kpmaction(rvu, blkaddr,
+ npc_get_kpu_action_nth_entry(rvu, profile, idx),
kpm, entry, false);
}
@@ -666,7 +672,8 @@ void npc_cn20k_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_kpmaction(rvu, blkaddr, &rvu->kpu.ikpu[idx],
+ npc_config_kpmaction(rvu, blkaddr,
+ npc_get_ikpu_nth_entry(rvu, idx),
0, idx, true);
/* Program KPM CAM and Action profiles */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h
index cefc5d70f3e4..c8c0cb68535c 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h
@@ -265,6 +265,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;
@@ -290,6 +303,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 */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index a466181cf908..eb2e4ccf070d 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -553,8 +553,9 @@ 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;
/* used for cn9k and cn10k */
@@ -564,6 +565,7 @@ struct npc_kpu_profile_adapter {
bool custom;
size_t pkinds;
size_t kpus;
+ bool from_fs;
};
#define RVU_SWITCH_LBK_CHAN 63
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index 352dc5f1d5b9..e348cdd375eb 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -1583,8 +1583,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;
@@ -1596,6 +1600,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,
@@ -1607,34 +1619,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)
@@ -1678,26 +1760,50 @@ static void npc_prepare_default_kpu(struct rvu *rvu,
npc_cn20k_update_action_entries_n_flags(rvu, 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(struct rvu *rvu,
- struct npc_kpu_profile_adapter *profile)
+ struct npc_kpu_profile_adapter *profile,
+ bool from_fs, int *fw_kpus)
{
size_t hdr_sz = sizeof(struct npc_kpu_profile_fwdata), offset = 0;
struct npc_kpu_profile_action *action;
+ struct npc_kpu_profile_fwdata *sfw;
struct npc_kpu_profile_fwdata *fw;
+ struct npc_kpu_profile_cam2 *cam2;
struct npc_kpu_profile_cam *cam;
struct npc_kpu_fwdata *fw_kpu;
- int entries;
+ int entries, ret;
u16 kpu, entry;
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);
@@ -1731,32 +1837,89 @@ static int npc_apply_custom_kpu(struct rvu *rvu,
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;
+
+ /* Binary blob contains ikpu actions entries at start of data[0] */
+ if (from_fs) {
+ 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 -ENOMEM;
+
+ memcpy((void *)profile->ikpu2, action, sizeof(ikpu_action_entries));
+ offset += sizeof(ikpu_action_entries);
+ }
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);
+ if (!from_fs) {
+ 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];
+ }
+ continue;
+ }
+ entries = fw_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",
+ "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].cam[entry] = cam[entry];
- profile->kpu[kpu].action[entry] = action[entry];
+ profile->kpu[kpu].cam2[entry] = cam2[entry];
+ profile->kpu[kpu].action2[entry] = action[entry];
}
}
@@ -1853,7 +2016,10 @@ void npc_load_kpu_profile(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 len, ret, fw_kpus = 0;
+ char *path;
+
+ profile->from_fs = false;
/* If user not specified profile customization */
if (!strncmp(kpu_profile, def_pfl_name, KPU_NAME_LEN))
@@ -1866,27 +2032,52 @@ void npc_load_kpu_profile(struct rvu *rvu)
* 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);
+
+#define PDIR "kpu/"
+ len = strlen(kpu_profile) + sizeof(PDIR);
+ path = kmalloc(len, GFP_KERNEL);
+ if (!path)
+ return;
+
+ strscpy(path, PDIR, len);
+ strcat(path, kpu_profile);
+ if (!request_firmware_direct(&fw, path, rvu->dev)) {
+ dev_info(rvu->dev, "Loading KPU profile from filesystem: %s\n",
+ path);
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;
+ kfree(path);
+
+ ret = npc_apply_custom_kpu(rvu, profile, true, &fw_kpus);
+ kfree(rvu->kpu_fwdata);
+ rvu->kpu_fwdata = NULL;
+
+ if (ret) {
+ rvu->kpu_fwdata_sz = 0;
+ npc_prepare_default_kpu(rvu, profile);
+ dev_err(rvu->dev,
+ "Loading KPU profile from filesystem failed\n");
+ goto load_image_fwdb;
+ }
+
+ rvu->kpu.kpus = fw_kpus;
+ profile->from_fs = true;
+ return;
}
+ kfree(path);
load_image_fwdb:
/* Loading the KPU profile using firmware database */
if (npc_load_kpu_profile_fwdb(rvu, kpu_profile))
goto revert_to_default;
-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.
*/
@@ -1900,10 +2091,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,
@@ -1927,6 +2114,7 @@ void npc_load_kpu_profile(struct rvu *rvu)
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;
@@ -1950,7 +2138,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;
@@ -1958,6 +2148,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)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.h
index 83c5e32e2afc..662f6693cfe9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.h
@@ -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 */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h
index 62cdc714ba57..ab89b8c6e490 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h
@@ -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)
--
2.43.0
This is an AI-generated review of your patch. The human sending this
email says "review on patch 3 looks legit, so sending this along
even tho not so sure about this one".
For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
octeontx2-af: npc: Support for custom KPU profile from filesystem
This commit provides a mechanism to load custom KPU (Key Parse Unit)
profiles from the filesystem at module load time, avoiding the need to
flash updated firmware on deployed devices.
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
> index 8dad272fd069..c8c09a91b13f 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
[ ... ]
> @@ -666,7 +672,8 @@ void npc_cn20k_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_kpmaction(rvu, blkaddr, &rvu->kpu.ikpu[idx],
> + npc_config_kpmaction(rvu, blkaddr,
> + npc_get_ikpu_nth_entry(rvu, idx),
> 0, idx, true);
When a filesystem-loaded profile is used on cn20k (from_fs=true),
npc_get_ikpu_nth_entry() returns &profile->ikpu2[idx]. However, ikpu2
is never allocated for the cn20k path.
The allocation of ikpu2 happens inside npc_apply_custom_kpu() in the
from_fs block, but that block is never reached for cn20k because
npc_apply_custom_kpu() returns early via npc_cn20k_apply_custom_kpu()
before reaching the ikpu2 allocation:
static int npc_apply_custom_kpu(struct rvu *rvu,
struct npc_kpu_profile_adapter *profile,
bool from_fs, int *fw_kpus)
{
...
if (is_cn20k(rvu->pdev))
return npc_cn20k_apply_custom_kpu(rvu, profile);
if (from_fs) {
profile->ikpu2 = devm_kcalloc(rvu->dev, 1,
sizeof(ikpu_action_entries),
GFP_KERNEL);
...
}
...
}
However, npc_load_kpu_profile() unconditionally sets profile->from_fs =
true after npc_apply_custom_kpu() returns success.
Does this code dereference a NULL-offset pointer? The resulting
&NULL[idx] is passed to npc_config_kpmaction() which dereferences it
(kpuaction->errlev), which would cause a kernel crash.
This same issue affects cam2 and action2 via npc_get_kpu_cam_nth_entry()
and npc_get_kpu_action_nth_entry().
>
> /* Program KPM CAM and Action profiles */
[ ... ]
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
> index 352dc5f1d5b9..e348cdd375eb 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
[ ... ]
> @@ -1731,32 +1837,89 @@ static int npc_apply_custom_kpu(struct rvu *rvu,
> 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;
> +
> + /* Binary blob contains ikpu actions entries at start of data[0] */
> + if (from_fs) {
> + 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 -ENOMEM;
> +
> + memcpy((void *)profile->ikpu2, action, sizeof(ikpu_action_entries));
> + offset += sizeof(ikpu_action_entries);
> + }
> +
> 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);
> + if (!from_fs) {
> + 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];
> + }
> + continue;
> + }
> + entries = fw_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",
> + "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].cam[entry] = cam[entry];
> - profile->kpu[kpu].action[entry] = action[entry];
> + profile->kpu[kpu].cam2[entry] = cam2[entry];
> + profile->kpu[kpu].action2[entry] = action[entry];
> }
> }
This is a concern raised by Simon Horman during review that was
acknowledged but not addressed in v4.
The loop variable 'entry' is declared as u16 in npc_apply_custom_kpu():
u16 kpu, entry;
while 'entries' is an int:
entries = fw_kpu->entries;
In the from_fs path, 'entries' is read directly from the firmware blob
without bounds checking. If a corrupt or malicious firmware file
specifies fw_kpu->entries > 65535, the u16 variable wraps around at
65536:
for (entry = 0; entry < entries; entry++)
When entry reaches 65535, incrementing it produces 0, which is still
less than entries, creating an infinite loop that hangs the kernel.
Should the code cap entries in the from_fs path or use an int-sized loop
variable? The author acknowledged this concern with 'ACK' in v3 review
discussion, and Simon emphasized that "the kernel should not rely on
filesystem data being well-formed."
>
> return 0;
[ ... ]
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
> @@ -521,13 +521,17 @@ npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr,
[ ... ]
> @@ -536,16 +540,18 @@ npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr,
> max_entries = rvu->hw->npc_kpu_entries / 2;
> entry = start_entry;
> /* Program CAM match entries for previous kpm extracted data */
> - num_entries = min_t(int, profile->cam_entries, max_entries);
> + num_entries = min_t(int, num_cam_entries, max_entries);
> for (idx = 0; entry < num_entries + start_entry; entry++, idx++)
> - npc_config_kpmcam(rvu, blkaddr, &profile->cam[idx],
> + npc_config_kpmcam(rvu, blkaddr,
> + npc_get_kpu_cam_nth_entry(rvu, profile, idx),
> kpm, entry);
>
> entry = start_entry;
> /* Program this kpm's actions */
> - num_entries = min_t(int, profile->action_entries, max_entries);
> + num_entries = min_t(int, num_action_entries, max_entries);
> for (idx = 0; entry < num_entries + start_entry; entry++, idx++)
> - npc_config_kpmaction(rvu, blkaddr, &profile->action[idx],
> + npc_config_kpmaction(rvu, blkaddr,
> + npc_get_kpu_action_nth_entry(rvu, profile, idx),
> kpm, entry, false);
> }
>
> @@ -615,6 +621,12 @@ npc_program_kpm_profile(struct rvu *rvu, int blkaddr,
> const struct npc_kpu_profile *profile1,
> const struct npc_kpu_profile *profile2)
> {
[ ... ]
> + /* Program the first KPM profile */
> + npc_program_single_kpm_profile(rvu, blkaddr, idx,
> + 0, profile1);
> +
> + /* Program the second KPM profile */
> + start_entry = profile->cam_entries;
> + npc_program_single_kpm_profile(rvu, blkaddr, idx,
> + start_entry, profile2);
> +
> + total_cam_entries = profile->cam_entries + profile2->cam_entries;
> + total_action_entries = profile->action_entries + profile2->action_entries;
> + npc_enable_kpm_entry(rvu, blkaddr, idx, 0,
> + total_cam_entries, total_action_entries);
> +
> + val = profile->cam_entries;
> + rvu_write64(rvu, blkaddr, NPC_AF_KPMX_PASS2_OFFSET(idx), val);
> }
Looking at npc_program_kpm_profile(), it uses profile->cam_entries
directly in four places instead of calling npc_get_num_kpu_cam_entries().
When profile->from_fs is true (filesystem-loaded KPU profile), the
accessor returns cam_entries2, while the raw field cam_entries retains
its default value from npc_prepare_default_kpu().
Does this cause the start_entry for the second profile to be based on
the wrong count, leading profile2 entries to overlap or be offset
incorrectly? Would total_cam_entries and total_action_entries passed to
npc_enable_kpm_entry() be wrong, enabling the wrong set of entries? And
would NPC_AF_KPMX_PASS2_OFFSET be set incorrectly?
The callee npc_program_single_kpm_profile() was correctly updated to use
accessors, but npc_program_kpm_profile() was not.
© 2016 - 2026 Red Hat, Inc.