* [PATCH v12 net-next 7/9] octeontx2-af: npc: Support for custom KPU profile from filesystem
From: Ratheesh Kannoth @ 2026-05-08 3:49 UTC (permalink / raw)
To: intel-wired-lan, linux-kernel, linux-rdma, netdev, oss-drivers
Cc: akiyano, andrew+netdev, anthony.l.nguyen, arkadiusz.kubalewski,
brett.creeley, darinzon, davem, donald.hunter, edumazet, horms,
idosch, ivecera, jiri, kuba, leon, mbloch, michael.chan, pabeni,
pavan.chebbi, petrm, Prathosh.Satish, przemyslaw.kitszel, saeedm,
sgoutham, tariqt, vadim.fedorenko, Ratheesh Kannoth
In-Reply-To: <20260508034912.4082520-1-rkannoth@marvell.com>
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 | 57 ++-
.../net/ethernet/marvell/octeontx2/af/npc.h | 17 +
.../net/ethernet/marvell/octeontx2/af/rvu.h | 12 +-
.../ethernet/marvell/octeontx2/af/rvu_npc.c | 456 ++++++++++++++----
.../ethernet/marvell/octeontx2/af/rvu_npc.h | 17 +
.../ethernet/marvell/octeontx2/af/rvu_reg.h | 1 +
6 files changed, 449 insertions(+), 111 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 6f8f42234b06..67dfbe5ca903 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);
}
@@ -611,20 +617,23 @@ npc_enable_kpm_entry(struct rvu *rvu, int blkaddr, int kpm, int num_entries)
static void npc_program_kpm_profile(struct rvu *rvu, int blkaddr, int num_kpms)
{
const struct npc_kpu_profile *profile1, *profile2;
+ int pfl1_num_cam_entries, pfl2_num_cam_entries;
int idx, total_cam_entries;
for (idx = 0; idx < num_kpms; idx++) {
profile1 = &rvu->kpu.kpu[idx];
+ pfl1_num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile1);
npc_program_single_kpm_profile(rvu, blkaddr, idx, 0, profile1);
profile2 = &rvu->kpu.kpu[idx + KPU_OFFSET];
+ pfl2_num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile2);
+
npc_program_single_kpm_profile(rvu, blkaddr, idx,
- profile1->cam_entries,
+ pfl1_num_cam_entries,
profile2);
- total_cam_entries = profile1->cam_entries +
- profile2->cam_entries;
+ total_cam_entries = pfl1_num_cam_entries + pfl2_num_cam_entries;
npc_enable_kpm_entry(rvu, blkaddr, idx, total_cam_entries);
rvu_write64(rvu, blkaddr, NPC_AF_KPMX_PASS2_OFFSET(idx),
- profile1->cam_entries);
+ pfl1_num_cam_entries);
/* Enable the KPUs associated with this KPM */
rvu_write64(rvu, blkaddr, NPC_AF_KPUX_CFG(idx), 0x01);
rvu_write64(rvu, blkaddr, NPC_AF_KPUX_CFG(idx + KPU_OFFSET),
@@ -634,6 +643,7 @@ static void npc_program_kpm_profile(struct rvu *rvu, int blkaddr, int num_kpms)
void npc_cn20k_parser_profile_init(struct rvu *rvu, int blkaddr)
{
+ struct npc_kpu_profile_action *act;
struct rvu_hwinfo *hw = rvu->hw;
int num_pkinds, idx;
@@ -665,9 +675,15 @@ void npc_cn20k_parser_profile_init(struct rvu *rvu, int blkaddr)
num_pkinds = rvu->kpu.pkinds;
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],
+ /* Cn20k does not support Custom profile from filesystem */
+ for (idx = 0; idx < num_pkinds; idx++) {
+ act = npc_get_ikpu_nth_entry(rvu, idx);
+ if (!act)
+ continue;
+
+ npc_config_kpmaction(rvu, blkaddr, act,
0, idx, true);
+ }
/* Program KPM CAM and Action profiles */
npc_program_kpm_profile(rvu, blkaddr, hw->npc_kpms);
@@ -679,7 +695,7 @@ struct npc_priv_t *npc_priv_get(void)
}
static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr,
- struct npc_mcam_kex_extr *mkex_extr,
+ const struct npc_mcam_kex_extr *mkex_extr,
u8 intf)
{
u8 num_extr = rvu->hw->npc_kex_extr;
@@ -708,7 +724,7 @@ 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_extr *mkex_extr,
+ const struct npc_mcam_kex_extr *mkex_extr,
u8 intf)
{
u8 num_extr = rvu->hw->npc_kex_extr;
@@ -737,7 +753,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_extr *mkex_extr)
+ const struct npc_mcam_kex_extr *mkex_extr)
{
struct rvu_hwinfo *hw = rvu->hw;
u8 intf;
@@ -1630,8 +1646,8 @@ npc_cn20k_update_action_entries_n_flags(struct rvu *rvu,
int npc_cn20k_apply_custom_kpu(struct rvu *rvu,
struct npc_kpu_profile_adapter *profile)
{
+ const struct npc_cn20k_kpu_profile_fwdata *fw = rvu->kpu_fwdata;
size_t hdr_sz = sizeof(struct npc_cn20k_kpu_profile_fwdata);
- struct npc_cn20k_kpu_profile_fwdata *fw = rvu->kpu_fwdata;
struct npc_kpu_profile_action *action;
struct npc_kpu_profile_cam *cam;
struct npc_kpu_fwdata *fw_kpu;
@@ -1676,8 +1692,15 @@ int npc_cn20k_apply_custom_kpu(struct rvu *rvu,
}
/* 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 */
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;
}
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..2a2f2287e0c0 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -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;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index 5fa9e1c7ae9f..a0f97e683145 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -1495,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;
@@ -1524,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;
@@ -1553,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;
@@ -1693,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;
@@ -1706,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,
@@ -1717,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)
@@ -1788,26 +1872,168 @@ 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;
- if (is_cn20k(rvu->pdev))
- return npc_cn20k_apply_custom_kpu(rvu, profile);
+ 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 > 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);
+
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);
@@ -1835,42 +2061,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,
@@ -1958,45 +2180,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;
-
- /* 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);
+ int fw_kpus = 0;
- /* 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.
*/
@@ -2010,10 +2206,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,
@@ -2021,22 +2213,98 @@ 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;
+ }
+
+ 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;
@@ -2060,7 +2328,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;
@@ -2068,6 +2338,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)
@@ -2297,18 +2572,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.
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
^ permalink raw reply related
* [PATCH v12 net-next 6/9] octeontx2: cn20k: Coordinate default rules with NIX LF lifecycle
From: Ratheesh Kannoth @ 2026-05-08 3:49 UTC (permalink / raw)
To: intel-wired-lan, linux-kernel, linux-rdma, netdev, oss-drivers
Cc: akiyano, andrew+netdev, anthony.l.nguyen, arkadiusz.kubalewski,
brett.creeley, darinzon, davem, donald.hunter, edumazet, horms,
idosch, ivecera, jiri, kuba, leon, mbloch, michael.chan, pabeni,
pavan.chebbi, petrm, Prathosh.Satish, przemyslaw.kitszel, saeedm,
sgoutham, tariqt, vadim.fedorenko, Ratheesh Kannoth
In-Reply-To: <20260508034912.4082520-1-rkannoth@marvell.com>
Add NIX_LF_DONT_FREE_DFT_IDXS so the PF can send NIX LF free during hw
reinit or teardown without the AF freeing CN20K default NPC rule indexes
while the driver still owns that state (otx2_init_hw_resources and
otx2_free_hw_resources).
On CN20K, allocate default NPC rules from NIX LF alloc before
nix_interface_init, roll back with npc_cn20k_dft_rules_free on failure,
and free from NIX LF free when the new flag is not set. Tighten
rvu_mbox_handler_nix_lf_alloc error handling: use a single rc, propagate
qmem_alloc and other errors, and set -ENOMEM only when kcalloc fails
(remove the blanket -ENOMEM at the free_mem path).
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../net/ethernet/marvell/octeontx2/af/mbox.h | 1 +
.../ethernet/marvell/octeontx2/af/rvu_nix.c | 69 ++++++++++++-------
.../ethernet/marvell/octeontx2/af/rvu_npc.c | 22 ++++--
.../ethernet/marvell/octeontx2/nic/otx2_pf.c | 6 +-
4 files changed, 62 insertions(+), 36 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index dc42c81c0942..e07fbf842b94 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -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;
};
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index f977734ae712..7df256a9e01c 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -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,7 +1500,7 @@ 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;
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = req->hdr.pcifunc;
struct rvu_block *block;
@@ -1555,8 +1556,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 +1567,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 +1586,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 +1604,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 +1622,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 +1646,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 +1691,16 @@ 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_alloc(rvu, pcifunc);
+ if (rc)
+ goto free_mem;
+ }
+
+ 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 +1712,12 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
goto exit;
+free_dft:
+ if (is_cn20k(rvu->pdev))
+ 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 +1791,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;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index 3c814d157ab9..5fa9e1c7ae9f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -990,7 +990,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf,
u16 vf_func;
/* Only CGX PF/VF can add allmulticast entry */
- if (is_lbk_vf(rvu, pcifunc) && is_sdp_vf(rvu, pcifunc))
+ if (is_lbk_vf(rvu, pcifunc) || is_sdp_vf(rvu, pcifunc))
return;
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
@@ -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);
@@ -4085,12 +4095,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);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index ee623476e5ff..81b088f5a016 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -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.
*/
--
2.43.0
^ permalink raw reply related
* [PATCH v12 net-next 5/9] octeontx2-af: npc: cn20k: add subbank search order control
From: Ratheesh Kannoth @ 2026-05-08 3:49 UTC (permalink / raw)
To: intel-wired-lan, linux-kernel, linux-rdma, netdev, oss-drivers
Cc: akiyano, andrew+netdev, anthony.l.nguyen, arkadiusz.kubalewski,
brett.creeley, darinzon, davem, donald.hunter, edumazet, horms,
idosch, ivecera, jiri, kuba, leon, mbloch, michael.chan, pabeni,
pavan.chebbi, petrm, Prathosh.Satish, przemyslaw.kitszel, saeedm,
sgoutham, tariqt, vadim.fedorenko, Ratheesh Kannoth
In-Reply-To: <20260508034912.4082520-1-rkannoth@marvell.com>
CN20K NPC MCAM is split into 32 subbanks that are searched in a
predefined order during allocation. Lower-numbered subbanks have
higher priority than higher-numbered ones.
Add a runtime "srch_order" to control the order in which
subbanks are searched during MCAM allocation.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../ethernet/marvell/octeontx2/af/cn20k/npc.c | 54 ++++++++++-
.../ethernet/marvell/octeontx2/af/cn20k/npc.h | 3 +
.../marvell/octeontx2/af/rvu_devlink.c | 92 +++++++++++++++++--
3 files changed, 137 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index e9aad0ad3fa6..6f8f42234b06 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -3376,7 +3376,7 @@ rvu_mbox_handler_npc_cn20k_get_kex_cfg(struct rvu *rvu,
return 0;
}
-static int *subbank_srch_order;
+static u32 *subbank_srch_order;
static void npc_populate_restricted_idxs(int num_subbanks)
{
@@ -3388,7 +3388,7 @@ static int npc_create_srch_order(int cnt)
{
int val = 0;
- subbank_srch_order = kcalloc(cnt, sizeof(int),
+ subbank_srch_order = kcalloc(cnt, sizeof(u32),
GFP_KERNEL);
if (!subbank_srch_order)
return -ENOMEM;
@@ -3906,6 +3906,56 @@ static void npc_unlock_all_subbank(void)
mutex_unlock(&npc_priv.sb[i].lock);
}
+int npc_cn20k_search_order_set(struct rvu *rvu,
+ u64 narr[MAX_NUM_SUB_BANKS], int cnt)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+ struct npc_subbank *sb;
+ struct xarray *xa;
+ int sb_idx, rc;
+
+ if (cnt != npc_priv.num_subbanks) {
+ dev_err(rvu->dev, "Number of entries(%u) != %u\n",
+ cnt, npc_priv.num_subbanks);
+ return -EINVAL;
+ }
+
+ mutex_lock(&mcam->lock);
+ npc_lock_all_subbank();
+ restrict_valid = false;
+
+ for (sb_idx = 0; sb_idx < cnt; sb_idx++) {
+ sb = &npc_priv.sb[sb_idx];
+
+ xa = &npc_priv.xa_sb_free;
+ if (sb->flags & NPC_SUBBANK_FLAG_USED)
+ xa = &npc_priv.xa_sb_used;
+
+ sb->arr_idx = narr[sb_idx];
+
+ rc = xa_err(xa_store(xa, sb->arr_idx,
+ xa_mk_value(sb_idx), GFP_KERNEL));
+ if (rc)
+ goto fail;
+ }
+
+ for (int i = 0; i < cnt; i++)
+ subbank_srch_order[i] = (u32)narr[i];
+
+fail:
+ npc_unlock_all_subbank();
+ mutex_unlock(&mcam->lock);
+
+ return rc;
+}
+
+const u32 *npc_cn20k_search_order_get(bool *restricted_order, u32 *sz)
+{
+ *restricted_order = restrict_valid;
+ *sz = npc_priv.num_subbanks;
+ return subbank_srch_order;
+}
+
/* Only non-ref non-contigous mcam indexes
* are picked for defrag process
*/
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
index 9567a2d80b58..bf030e40fbf9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
@@ -343,5 +343,8 @@ 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 */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
index a42404e6db7c..aa3ecab5ebd8 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
@@ -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))
--
2.43.0
^ permalink raw reply related
* [PATCH v12 net-next 3/9] devlink: pass param values by pointer
From: Ratheesh Kannoth @ 2026-05-08 3:49 UTC (permalink / raw)
To: intel-wired-lan, linux-kernel, linux-rdma, netdev, oss-drivers
Cc: akiyano, andrew+netdev, anthony.l.nguyen, arkadiusz.kubalewski,
brett.creeley, darinzon, davem, donald.hunter, edumazet, horms,
idosch, ivecera, jiri, kuba, leon, mbloch, michael.chan, pabeni,
pavan.chebbi, petrm, Prathosh.Satish, przemyslaw.kitszel, saeedm,
sgoutham, tariqt, vadim.fedorenko, Ratheesh Kannoth
In-Reply-To: <20260508034912.4082520-1-rkannoth@marvell.com>
union devlink_param_value grows substantially once U64 array
parameters are added to devlink (from 32 bytes to over 264 bytes).
devlink_nl_param_value_fill_one() and devlink_nl_param_value_put()
copy the union by value in several places. Passing two instances as
value arguments alone consumes over 528 bytes of stack; combined with
deeper call chains the parameter stack can approach 800 bytes and trip
CONFIG_FRAME_WARN more easily.
Switch internal helpers and exported driver APIs to pass pointers to
union devlink_param_value rather than passing the union by value.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
drivers/dpll/zl3073x/devlink.c | 6 +-
drivers/net/ethernet/amazon/ena/ena_devlink.c | 8 +--
drivers/net/ethernet/amd/pds_core/core.h | 2 +-
drivers/net/ethernet/amd/pds_core/devlink.c | 2 +-
.../net/ethernet/broadcom/bnxt/bnxt_devlink.c | 6 +-
.../net/ethernet/intel/ice/devlink/devlink.c | 30 ++++----
.../marvell/octeontx2/af/rvu_devlink.c | 22 +++---
.../marvell/octeontx2/nic/otx2_devlink.c | 4 +-
drivers/net/ethernet/mellanox/mlx4/main.c | 14 ++--
.../net/ethernet/mellanox/mlx5/core/devlink.c | 72 +++++++++----------
.../mellanox/mlx5/core/eswitch_offloads.c | 2 +-
.../net/ethernet/mellanox/mlx5/core/fs_core.c | 4 +-
.../mellanox/mlx5/core/lib/nv_param.c | 12 ++--
drivers/net/ethernet/mellanox/mlxsw/core.c | 8 +--
.../ethernet/netronome/nfp/devlink_param.c | 6 +-
drivers/net/netdevsim/dev.c | 4 +-
include/net/devlink.h | 4 +-
net/devlink/param.c | 32 ++++-----
18 files changed, 119 insertions(+), 119 deletions(-)
diff --git a/drivers/dpll/zl3073x/devlink.c b/drivers/dpll/zl3073x/devlink.c
index ccc22332b346..218b08fd8a30 100644
--- a/drivers/dpll/zl3073x/devlink.c
+++ b/drivers/dpll/zl3073x/devlink.c
@@ -315,10 +315,10 @@ EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X");
static int
zl3073x_devlink_param_clock_id_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- if (!val.vu64) {
+ if (!val->vu64) {
NL_SET_ERR_MSG_MOD(extack, "'clock_id' must be non-zero");
return -EINVAL;
}
@@ -377,7 +377,7 @@ int zl3073x_devlink_register(struct zl3073x_dev *zldev)
value.vu64 = zldev->clock_id;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
- value);
+ &value);
/* Register devlink instance */
devl_register(devlink);
diff --git a/drivers/net/ethernet/amazon/ena/ena_devlink.c b/drivers/net/ethernet/amazon/ena/ena_devlink.c
index 4772185e669d..5ea9fef149aa 100644
--- a/drivers/net/ethernet/amazon/ena/ena_devlink.c
+++ b/drivers/net/ethernet/amazon/ena/ena_devlink.c
@@ -8,12 +8,12 @@
#include "ena_phc.h"
static int ena_devlink_enable_phc_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink);
- if (!val.vbool)
+ if (!val->vbool)
return 0;
if (!ena_com_phc_supported(adapter->ena_dev)) {
@@ -57,7 +57,7 @@ void ena_devlink_disable_phc_param(struct devlink *devlink)
value.vbool = false;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC,
- value);
+ &value);
devl_unlock(devlink);
}
@@ -151,7 +151,7 @@ static int ena_devlink_configure_params(struct devlink *devlink)
value.vbool = ena_phc_is_enabled(adapter);
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC,
- value);
+ &value);
devl_unlock(devlink);
return 0;
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index 4a6b35c84dab..b7fe9ad73349 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -261,7 +261,7 @@ int pdsc_dl_enable_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack);
int pdsc_dl_enable_validate(struct devlink *dl, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack);
void __iomem *pdsc_map_dbpage(struct pdsc *pdsc, int page_num);
diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
index b576be626a29..fe0595d31683 100644
--- a/drivers/net/ethernet/amd/pds_core/devlink.c
+++ b/drivers/net/ethernet/amd/pds_core/devlink.c
@@ -68,7 +68,7 @@ int pdsc_dl_enable_set(struct devlink *dl, u32 id,
}
int pdsc_dl_enable_validate(struct devlink *dl, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct pdsc *pdsc = devlink_priv(dl);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
index 835f2b413931..eb17a3454b4c 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
@@ -1123,7 +1123,7 @@ static int bnxt_dl_nvm_param_set(struct devlink *dl, u32 id,
}
static int bnxt_dl_roce_validate(struct devlink *dl, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
const struct bnxt_dl_nvm_param nvm_roce_cap = {0, NVM_OFF_RDMA_CAPABLE,
@@ -1149,7 +1149,7 @@ static int bnxt_dl_roce_validate(struct devlink *dl, u32 id,
}
static int bnxt_dl_msix_validate(struct devlink *dl, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
int max_val = -1;
@@ -1160,7 +1160,7 @@ static int bnxt_dl_msix_validate(struct devlink *dl, u32 id,
if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN)
max_val = BNXT_MSIX_VEC_MIN_MAX;
- if (val.vu32 > max_val) {
+ if (val->vu32 > max_val) {
NL_SET_ERR_MSG_MOD(extack, "MSIX value is exceeding the range");
return -EINVAL;
}
diff --git a/drivers/net/ethernet/intel/ice/devlink/devlink.c b/drivers/net/ethernet/intel/ice/devlink/devlink.c
index 641d6e289d5c..22b7d8e6bd9e 100644
--- a/drivers/net/ethernet/intel/ice/devlink/devlink.c
+++ b/drivers/net/ethernet/intel/ice/devlink/devlink.c
@@ -671,10 +671,10 @@ static int ice_devlink_tx_sched_layers_set(struct devlink *devlink, u32 id,
* error.
*/
static int ice_devlink_tx_sched_layers_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- if (val.vu8 != ICE_SCHED_5_LAYERS && val.vu8 != ICE_SCHED_9_LAYERS) {
+ if (val->vu8 != ICE_SCHED_5_LAYERS && val->vu8 != ICE_SCHED_9_LAYERS) {
NL_SET_ERR_MSG_MOD(extack,
"Wrong number of tx scheduler layers provided.");
return -EINVAL;
@@ -1398,7 +1398,7 @@ static int ice_devlink_enable_roce_set(struct devlink *devlink, u32 id,
static int
ice_devlink_enable_roce_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
@@ -1465,7 +1465,7 @@ static int ice_devlink_enable_iw_set(struct devlink *devlink, u32 id,
static int
ice_devlink_enable_iw_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
@@ -1591,10 +1591,10 @@ static int ice_devlink_local_fwd_set(struct devlink *devlink, u32 id,
* error.
*/
static int ice_devlink_local_fwd_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- if (ice_devlink_local_fwd_str_to_mode(val.vstr) < 0) {
+ if (ice_devlink_local_fwd_str_to_mode(val->vstr) < 0) {
NL_SET_ERR_MSG_MOD(extack, "Error: Requested value is not supported.");
return -EINVAL;
}
@@ -1604,12 +1604,12 @@ static int ice_devlink_local_fwd_validate(struct devlink *devlink, u32 id,
static int
ice_devlink_msix_max_pf_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
- if (val.vu32 > pf->hw.func_caps.common_cap.num_msix_vectors)
+ if (val->vu32 > pf->hw.func_caps.common_cap.num_msix_vectors)
return -EINVAL;
return 0;
@@ -1617,21 +1617,21 @@ ice_devlink_msix_max_pf_validate(struct devlink *devlink, u32 id,
static int
ice_devlink_msix_min_pf_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- if (val.vu32 < ICE_MIN_MSIX)
+ if (val->vu32 < ICE_MIN_MSIX)
return -EINVAL;
return 0;
}
static int ice_devlink_enable_rdma_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
- bool new_state = val.vbool;
+ bool new_state = val->vbool;
if (new_state && !test_bit(ICE_FLAG_RDMA_ENA, pf->flags))
return -EOPNOTSUPP;
@@ -1791,16 +1791,16 @@ int ice_devlink_register_params(struct ice_pf *pf)
value.vu32 = pf->msix.max;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
- value);
+ &value);
value.vu32 = pf->msix.min;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
- value);
+ &value);
value.vbool = test_bit(ICE_FLAG_RDMA_ENA, pf->flags);
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,
- value);
+ &value);
return 0;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
index 6494a9ee2f0d..a42404e6db7c 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
@@ -1180,12 +1180,12 @@ static void rvu_health_reporters_destroy(struct rvu *rvu)
/* Devlink Params APIs */
static int rvu_af_dl_dwrr_mtu_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
- int dwrr_mtu = val.vu32;
+ int dwrr_mtu = val->vu32;
struct nix_txsch *txsch;
struct nix_hw *nix_hw;
@@ -1295,14 +1295,14 @@ static int rvu_af_npc_defrag(struct devlink *devlink, u32 id,
}
static int rvu_af_npc_defrag_feature_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
u64 enable;
- if (kstrtoull(val.vstr, 10, &enable)) {
+ if (kstrtoull(val->vstr, 10, &enable)) {
NL_SET_ERR_MSG_MOD(extack,
"Only 1 value is supported");
return -EINVAL;
@@ -1351,14 +1351,14 @@ static int rvu_af_npc_exact_feature_disable(struct devlink *devlink, u32 id,
}
static int rvu_af_npc_exact_feature_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
u64 enable;
- if (kstrtoull(val.vstr, 10, &enable)) {
+ if (kstrtoull(val->vstr, 10, &enable)) {
NL_SET_ERR_MSG_MOD(extack,
"Only 1 value is supported");
return -EINVAL;
@@ -1414,7 +1414,7 @@ static int rvu_af_dl_npc_mcam_high_zone_percent_set(struct devlink *devlink, u32
}
static int rvu_af_dl_npc_mcam_high_zone_percent_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
@@ -1422,7 +1422,7 @@ static int rvu_af_dl_npc_mcam_high_zone_percent_validate(struct devlink *devlink
struct npc_mcam *mcam;
/* The percent of high prio zone must range from 12% to 100% of unreserved mcam space */
- if (val.vu8 < 12 || val.vu8 > 100) {
+ if (val->vu8 < 12 || val->vu8 > 100) {
NL_SET_ERR_MSG_MOD(extack,
"mcam high zone percent must be between 12% to 100%");
return -EINVAL;
@@ -1504,7 +1504,7 @@ static int rvu_af_dl_nix_maxlf_set(struct devlink *devlink, u32 id,
}
static int rvu_af_dl_nix_maxlf_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
@@ -1528,13 +1528,13 @@ static int rvu_af_dl_nix_maxlf_validate(struct devlink *devlink, u32 id,
return -EPERM;
}
- if (max_nix0_lf && val.vu16 > max_nix0_lf) {
+ if (max_nix0_lf && val->vu16 > max_nix0_lf) {
NL_SET_ERR_MSG_MOD(extack,
"requested nixlf is greater than the max supported nix0_lf");
return -EPERM;
}
- if (max_nix1_lf && val.vu16 > max_nix1_lf) {
+ if (max_nix1_lf && val->vu16 > max_nix1_lf) {
NL_SET_ERR_MSG_MOD(extack,
"requested nixlf is greater than the max supported nix1_lf");
return -EINVAL;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
index a72694219df4..4a5ce0e67dda 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
@@ -8,7 +8,7 @@
/* Devlink Params APIs */
static int otx2_dl_mcam_count_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
@@ -97,7 +97,7 @@ static int otx2_dl_ucast_flt_cnt_get(struct devlink *devlink, u32 id,
}
static int otx2_dl_ucast_flt_cnt_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index e6b7e75894ff..401bef135a90 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -213,10 +213,10 @@ static int mlx4_devlink_crdump_snapshot_set(struct devlink *devlink, u32 id,
static int
mlx4_devlink_max_macs_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- u32 value = val.vu32;
+ u32 value = val->vu32;
if (value < 1 || value > 128)
return -ERANGE;
@@ -266,27 +266,27 @@ static void mlx4_devlink_set_params_init_values(struct devlink *devlink)
value.vbool = !!mlx4_internal_err_reset;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
- value);
+ &value);
value.vu32 = 1UL << log_num_mac;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
- value);
+ &value);
value.vbool = enable_64b_cqe_eqe;
devl_param_driverinit_value_set(devlink,
MLX4_DEVLINK_PARAM_ID_ENABLE_64B_CQE_EQE,
- value);
+ &value);
value.vbool = enable_4k_uar;
devl_param_driverinit_value_set(devlink,
MLX4_DEVLINK_PARAM_ID_ENABLE_4K_UAR,
- value);
+ &value);
value.vbool = false;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT,
- value);
+ &value);
}
static inline void mlx4_set_num_reserved_uars(struct mlx4_dev *dev,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index 73cf0321bb86..c31e05529fc4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -459,11 +459,11 @@ void mlx5_devlink_free(struct devlink *devlink)
}
static int mlx5_devlink_enable_roce_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
- bool new_state = val.vbool;
+ bool new_state = val->vbool;
if (new_state && !MLX5_CAP_GEN(dev, roce) &&
!(MLX5_CAP_GEN(dev, roce_rw_supported) && MLX5_CAP_GEN_MAX(dev, roce))) {
@@ -480,10 +480,10 @@ static int mlx5_devlink_enable_roce_validate(struct devlink *devlink, u32 id,
#ifdef CONFIG_MLX5_ESWITCH
static int mlx5_devlink_large_group_num_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- int group_num = val.vu32;
+ int group_num = val->vu32;
if (group_num < 1 || group_num > 1024) {
NL_SET_ERR_MSG_MOD(extack,
@@ -496,27 +496,27 @@ static int mlx5_devlink_large_group_num_validate(struct devlink *devlink, u32 id
#endif
static int mlx5_devlink_eq_depth_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- return (val.vu32 >= 64 && val.vu32 <= 4096) ? 0 : -EINVAL;
+ return (val->vu32 >= 64 && val->vu32 <= 4096) ? 0 : -EINVAL;
}
static int
mlx5_devlink_hairpin_num_queues_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- return val.vu32 ? 0 : -EINVAL;
+ return val->vu32 ? 0 : -EINVAL;
}
static int
mlx5_devlink_hairpin_queue_size_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
- u32 val32 = val.vu32;
+ u32 val32 = val->vu32;
if (!is_power_of_2(val32)) {
NL_SET_ERR_MSG_MOD(extack, "Value is not power of two");
@@ -534,11 +534,11 @@ mlx5_devlink_hairpin_queue_size_validate(struct devlink *devlink, u32 id,
}
static int mlx5_devlink_num_doorbells_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *mdev = devlink_priv(devlink);
- u32 val32 = val.vu32;
+ u32 val32 = val->vu32;
u32 max_num_channels;
max_num_channels = mlx5e_get_max_num_channels(mdev);
@@ -567,13 +567,13 @@ static void mlx5_devlink_hairpin_params_init_values(struct devlink *devlink)
value.vu32 = link_speed64;
devl_param_driverinit_value_set(
- devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, value);
+ devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, &value);
value.vu32 =
BIT(min_t(u32, 16 - MLX5_MPWRQ_MIN_LOG_STRIDE_SZ(dev),
MLX5_CAP_GEN(dev, log_max_hairpin_num_packets)));
devl_param_driverinit_value_set(
- devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, value);
+ devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, &value);
}
static const struct devlink_param mlx5_devlink_params[] = {
@@ -600,24 +600,24 @@ static void mlx5_devlink_set_params_init_values(struct devlink *devlink)
value.vbool = MLX5_CAP_GEN(dev, roce) && !mlx5_dev_is_lightweight(dev);
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE,
- value);
+ &value);
#ifdef CONFIG_MLX5_ESWITCH
value.vu32 = ESW_OFFLOADS_DEFAULT_NUM_GROUPS;
devl_param_driverinit_value_set(devlink,
MLX5_DEVLINK_PARAM_ID_ESW_LARGE_GROUP_NUM,
- value);
+ &value);
#endif
value.vu32 = MLX5_COMP_EQ_SIZE;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
- value);
+ &value);
value.vu32 = MLX5_NUM_ASYNC_EQE;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
- value);
+ &value);
}
static const struct devlink_param mlx5_devlink_eth_params[] = {
@@ -653,14 +653,14 @@ static int mlx5_devlink_eth_params_register(struct devlink *devlink)
value.vbool = !mlx5_dev_is_lightweight(dev);
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH,
- value);
+ &value);
mlx5_devlink_hairpin_params_init_values(devlink);
value.vu32 = MLX5_DEFAULT_NUM_DOORBELLS;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_NUM_DOORBELLS,
- value);
+ &value);
return 0;
}
@@ -681,12 +681,12 @@ static void mlx5_devlink_eth_params_unregister(struct devlink *devlink)
static int
mlx5_devlink_pcie_cong_thresh_validate(struct devlink *devl, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- if (val.vu16 > MLX5_PCIE_CONG_THRESH_MAX) {
+ if (val->vu16 > MLX5_PCIE_CONG_THRESH_MAX) {
NL_SET_ERR_MSG_FMT_MOD(extack, "Value %u > max supported (%u)",
- val.vu16, MLX5_PCIE_CONG_THRESH_MAX);
+ val->vu16, MLX5_PCIE_CONG_THRESH_MAX);
return -EINVAL;
}
@@ -711,19 +711,19 @@ static void mlx5_devlink_pcie_cong_init_values(struct devlink *devlink)
value.vu16 = MLX5_PCIE_CONG_THRESH_DEF_LOW;
id = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_LOW;
- devl_param_driverinit_value_set(devlink, id, value);
+ devl_param_driverinit_value_set(devlink, id, &value);
value.vu16 = MLX5_PCIE_CONG_THRESH_DEF_HIGH;
id = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_HIGH;
- devl_param_driverinit_value_set(devlink, id, value);
+ devl_param_driverinit_value_set(devlink, id, &value);
value.vu16 = MLX5_PCIE_CONG_THRESH_DEF_LOW;
id = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_LOW;
- devl_param_driverinit_value_set(devlink, id, value);
+ devl_param_driverinit_value_set(devlink, id, &value);
value.vu16 = MLX5_PCIE_CONG_THRESH_DEF_HIGH;
id = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_HIGH;
- devl_param_driverinit_value_set(devlink, id, value);
+ devl_param_driverinit_value_set(devlink, id, &value);
}
static const struct devlink_param mlx5_devlink_pcie_cong_params[] = {
@@ -775,11 +775,11 @@ static void mlx5_devlink_pcie_cong_params_unregister(struct devlink *devlink)
}
static int mlx5_devlink_enable_rdma_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
- bool new_state = val.vbool;
+ bool new_state = val->vbool;
if (new_state && !mlx5_rdma_supported(dev))
return -EOPNOTSUPP;
@@ -808,7 +808,7 @@ static int mlx5_devlink_rdma_params_register(struct devlink *devlink)
value.vbool = !mlx5_dev_is_lightweight(dev);
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,
- value);
+ &value);
return 0;
}
@@ -843,7 +843,7 @@ static int mlx5_devlink_vnet_params_register(struct devlink *devlink)
value.vbool = !mlx5_dev_is_lightweight(dev);
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,
- value);
+ &value);
return 0;
}
@@ -890,22 +890,22 @@ static void mlx5_devlink_auxdev_params_unregister(struct devlink *devlink)
}
static int mlx5_devlink_max_uc_list_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
- if (val.vu32 == 0) {
+ if (val->vu32 == 0) {
NL_SET_ERR_MSG_MOD(extack, "max_macs value must be greater than 0");
return -EINVAL;
}
- if (!is_power_of_2(val.vu32)) {
+ if (!is_power_of_2(val->vu32)) {
NL_SET_ERR_MSG_MOD(extack, "Only power of 2 values are supported for max_macs");
return -EINVAL;
}
- if (ilog2(val.vu32) >
+ if (ilog2(val->vu32) >
MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list)) {
NL_SET_ERR_MSG_MOD(extack, "max_macs value is out of the supported range");
return -EINVAL;
@@ -936,7 +936,7 @@ static int mlx5_devlink_max_uc_list_params_register(struct devlink *devlink)
value.vu32 = 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list);
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
- value);
+ &value);
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index dea5647de548..c46c3a462a40 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -2728,7 +2728,7 @@ static int esw_port_metadata_get(struct devlink *devlink, u32 id,
}
static int esw_port_metadata_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
index 61a6ba1e49dd..c8f6adae6f51 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -3765,11 +3765,11 @@ static int init_egress_root_ns(struct mlx5_flow_steering *steering)
}
static int mlx5_fs_mode_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
- char *value = val.vstr;
+ char *value = val->vstr;
u8 eswitch_mode;
eswitch_mode = mlx5_eswitch_mode(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c
index 19bb620b7436..4a7275e8b62e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c
@@ -270,13 +270,13 @@ mlx5_nv_param_devlink_cqe_compress_get(struct devlink *devlink, u32 id,
static int
mlx5_nv_param_devlink_cqe_compress_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
int i;
for (i = 0; i < ARRAY_SIZE(cqe_compress_str); i++) {
- if (!strcmp(val.vstr, cqe_compress_str[i]))
+ if (!strcmp(val->vstr, cqe_compress_str[i]))
return 0;
}
@@ -374,7 +374,7 @@ mlx5_devlink_swp_l4_csum_mode_get(struct devlink *devlink, u32 id,
static int
mlx5_devlink_swp_l4_csum_mode_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
@@ -383,7 +383,7 @@ mlx5_devlink_swp_l4_csum_mode_validate(struct devlink *devlink, u32 id,
int err, i;
for (i = 0; i < ARRAY_SIZE(swp_l4_csum_mode_str); i++) {
- if (!strcmp(val.vstr, swp_l4_csum_mode_str[i]))
+ if (!strcmp(val->vstr, swp_l4_csum_mode_str[i]))
break;
}
@@ -727,7 +727,7 @@ static int mlx5_devlink_total_vfs_set(struct devlink *devlink, u32 id,
}
static int mlx5_devlink_total_vfs_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
@@ -746,7 +746,7 @@ static int mlx5_devlink_total_vfs_validate(struct devlink *devlink, u32 id,
return 0; /* optimistic, but set might fail later */
max = MLX5_GET(nv_global_pci_cap, data, max_vfs_per_pf);
- if (val.vu16 > max) {
+ if (val->vu16 > max) {
NL_SET_ERR_MSG_FMT_MOD(extack,
"Max allowed by device is %u", max);
return -EINVAL;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c
index d76246301f67..308d8a94865f 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -1306,11 +1306,11 @@ static int mlxsw_core_fw_flash_update(struct mlxsw_core *mlxsw_core,
}
static int mlxsw_core_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
- if (val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER &&
- val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH) {
+ if (val->vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER &&
+ val->vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH) {
NL_SET_ERR_MSG_MOD(extack, "'fw_load_policy' must be 'driver' or 'flash'");
return -EINVAL;
}
@@ -1337,7 +1337,7 @@ static int mlxsw_core_fw_params_register(struct mlxsw_core *mlxsw_core)
value.vu8 = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
- value);
+ &value);
return 0;
}
diff --git a/drivers/net/ethernet/netronome/nfp/devlink_param.c b/drivers/net/ethernet/netronome/nfp/devlink_param.c
index 85e3b19e6165..826527992e4a 100644
--- a/drivers/net/ethernet/netronome/nfp/devlink_param.c
+++ b/drivers/net/ethernet/netronome/nfp/devlink_param.c
@@ -170,7 +170,7 @@ nfp_devlink_param_u8_set(struct devlink *devlink, u32 id,
static int
nfp_devlink_param_u8_validate(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
const struct nfp_devlink_param_u8_arg *arg;
@@ -180,12 +180,12 @@ nfp_devlink_param_u8_validate(struct devlink *devlink, u32 id,
arg = &nfp_devlink_u8_args[id];
- if (val.vu8 > arg->max_dl_val) {
+ if (val->vu8 > arg->max_dl_val) {
NL_SET_ERR_MSG_MOD(extack, "parameter out of range");
return -EINVAL;
}
- if (val.vu8 == arg->invalid_dl_val) {
+ if (val->vu8 == arg->invalid_dl_val) {
NL_SET_ERR_MSG_MOD(extack, "unknown/invalid value specified");
return -EINVAL;
}
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index f00fc2f9ebde..aed9ad5f1b43 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -597,11 +597,11 @@ static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev,
value.vu32 = nsim_dev->max_macs;
devl_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
- value);
+ &value);
value.vbool = nsim_dev->test1;
devl_param_driverinit_value_set(devlink,
NSIM_DEVLINK_PARAM_ID_TEST1,
- value);
+ &value);
}
static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink)
diff --git a/include/net/devlink.h b/include/net/devlink.h
index bcd31de1f890..5f4083dc4345 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -501,7 +501,7 @@ struct devlink_param {
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack);
int (*validate)(struct devlink *devlink, u32 id,
- union devlink_param_value val,
+ union devlink_param_value *val,
struct netlink_ext_ack *extack);
int (*get_default)(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
@@ -1923,7 +1923,7 @@ void devlink_params_unregister(struct devlink *devlink,
int devl_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
union devlink_param_value *val);
void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
- union devlink_param_value init_val);
+ union devlink_param_value *init_val);
void devl_param_value_changed(struct devlink *devlink, u32 param_id);
struct devlink_region *devl_region_create(struct devlink *devlink,
const struct devlink_region_ops *ops,
diff --git a/net/devlink/param.c b/net/devlink/param.c
index cf95268da5b0..1a196d3a843d 100644
--- a/net/devlink/param.c
+++ b/net/devlink/param.c
@@ -216,28 +216,28 @@ static int devlink_param_reset_default(struct devlink *devlink,
static int
devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type,
- int nla_type, union devlink_param_value val,
+ int nla_type, union devlink_param_value *val,
bool flag_as_u8)
{
switch (type) {
case DEVLINK_PARAM_TYPE_U8:
- if (nla_put_u8(msg, nla_type, val.vu8))
+ if (nla_put_u8(msg, nla_type, val->vu8))
return -EMSGSIZE;
break;
case DEVLINK_PARAM_TYPE_U16:
- if (nla_put_u16(msg, nla_type, val.vu16))
+ if (nla_put_u16(msg, nla_type, val->vu16))
return -EMSGSIZE;
break;
case DEVLINK_PARAM_TYPE_U32:
- if (nla_put_u32(msg, nla_type, val.vu32))
+ if (nla_put_u32(msg, nla_type, val->vu32))
return -EMSGSIZE;
break;
case DEVLINK_PARAM_TYPE_U64:
- if (devlink_nl_put_u64(msg, nla_type, val.vu64))
+ if (devlink_nl_put_u64(msg, nla_type, val->vu64))
return -EMSGSIZE;
break;
case DEVLINK_PARAM_TYPE_STRING:
- if (nla_put_string(msg, nla_type, val.vstr))
+ if (nla_put_string(msg, nla_type, val->vstr))
return -EMSGSIZE;
break;
case DEVLINK_PARAM_TYPE_BOOL:
@@ -245,10 +245,10 @@ devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type,
* false can be distinguished from not present
*/
if (flag_as_u8) {
- if (nla_put_u8(msg, nla_type, val.vbool))
+ if (nla_put_u8(msg, nla_type, val->vbool))
return -EMSGSIZE;
} else {
- if (val.vbool && nla_put_flag(msg, nla_type))
+ if (val->vbool && nla_put_flag(msg, nla_type))
return -EMSGSIZE;
}
break;
@@ -260,8 +260,8 @@ static int
devlink_nl_param_value_fill_one(struct sk_buff *msg,
enum devlink_param_type type,
enum devlink_param_cmode cmode,
- union devlink_param_value val,
- union devlink_param_value default_val,
+ union devlink_param_value *val,
+ union devlink_param_value *default_val,
bool has_default)
{
struct nlattr *param_value_attr;
@@ -383,8 +383,8 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
if (!param_value_set[i])
continue;
err = devlink_nl_param_value_fill_one(msg, param->type,
- i, param_value[i],
- default_value[i],
+ i, ¶m_value[i],
+ &default_value[i],
default_value_set[i]);
if (err)
goto values_list_nest_cancel;
@@ -621,7 +621,7 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
if (err)
return err;
if (param->validate) {
- err = param->validate(devlink, param->id, value,
+ err = param->validate(devlink, param->id, &value,
info->extack);
if (err)
return err;
@@ -888,7 +888,7 @@ EXPORT_SYMBOL_GPL(devl_param_driverinit_value_get);
* configuration mode default value.
*/
void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
- union devlink_param_value init_val)
+ union devlink_param_value *init_val)
{
struct devlink_param_item *param_item;
@@ -902,9 +902,9 @@ void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
DEVLINK_PARAM_CMODE_DRIVERINIT)))
return;
- param_item->driverinit_value = init_val;
+ param_item->driverinit_value = *init_val;
param_item->driverinit_value_valid = true;
- param_item->driverinit_default = init_val;
+ param_item->driverinit_default = *init_val;
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
}
--
2.43.0
^ permalink raw reply related
* [PATCH v12 net-next 4/9] devlink: Implement devlink param multi attribute nested data values
From: Ratheesh Kannoth @ 2026-05-08 3:49 UTC (permalink / raw)
To: intel-wired-lan, linux-kernel, linux-rdma, netdev, oss-drivers
Cc: akiyano, andrew+netdev, anthony.l.nguyen, arkadiusz.kubalewski,
brett.creeley, darinzon, davem, donald.hunter, edumazet, horms,
idosch, ivecera, jiri, kuba, leon, mbloch, michael.chan, pabeni,
pavan.chebbi, petrm, Prathosh.Satish, przemyslaw.kitszel, saeedm,
sgoutham, tariqt, vadim.fedorenko, Ratheesh Kannoth
In-Reply-To: <20260508034912.4082520-1-rkannoth@marvell.com>
From: Saeed Mahameed <saeedm@nvidia.com>
Devlink param value attribute is not defined since devlink is handling
the value validating and parsing internally, this allows us to implement
multi attribute values without breaking any policies.
Devlink param multi-attribute values are considered to be dynamically
sized arrays of u64 values, by introducing a new devlink param type
DEVLINK_PARAM_TYPE_U64_ARRAY, driver and user space can set a variable
count of u32 values into the DEVLINK_ATTR_PARAM_VALUE_DATA attribute.
Implement get/set parsing and add to the internal value structure passed
to drivers.
This is useful for devices that need to configure a list of values for
a specific configuration.
example:
$ devlink dev param show pci/... name multi-value-param
name multi-value-param type driver-specific
values:
cmode permanent value: 0,1,2,3,4,5,6,7
$ devlink dev param set pci/... name multi-value-param \
value 4,5,6,7,0,1,2,3 cmode permanent
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
Documentation/netlink/specs/devlink.yaml | 4 ++
include/net/devlink.h | 8 +++
include/uapi/linux/devlink.h | 1 +
net/devlink/netlink_gen.c | 2 +
net/devlink/param.c | 88 +++++++++++++++++++-----
5 files changed, 86 insertions(+), 17 deletions(-)
diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml
index 247b147d689f..52ad1e7805d1 100644
--- a/Documentation/netlink/specs/devlink.yaml
+++ b/Documentation/netlink/specs/devlink.yaml
@@ -234,6 +234,10 @@ definitions:
value: 10
-
name: binary
+ -
+ name: u64-array
+ value: 129
+
-
name: rate-tc-index-max
type: const
diff --git a/include/net/devlink.h b/include/net/devlink.h
index 5f4083dc4345..dd546dbd57cf 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -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 {
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 0b165eac7619..ca713bcc47b9 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -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 {
diff --git a/net/devlink/netlink_gen.c b/net/devlink/netlink_gen.c
index 81899786fd98..f52b0c2b19ed 100644
--- a/net/devlink/netlink_gen.c
+++ b/net/devlink/netlink_gen.c
@@ -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");
diff --git a/net/devlink/param.c b/net/devlink/param.c
index 1a196d3a843d..4cc479bd019f 100644
--- a/net/devlink/param.c
+++ b/net/devlink/param.c
@@ -252,6 +252,11 @@ devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type,
return -EMSGSIZE;
}
break;
+ case DEVLINK_PARAM_TYPE_U64_ARRAY:
+ 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 +309,78 @@ 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 = kmalloc_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;
+ param_value[i] = ctx->val;
- err = devlink_param_get_default(devlink, param, &ctx,
+ 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 +420,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 +431,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 +541,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 +581,26 @@ 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)))
+ return -EINVAL;
+
+ value->u64arr.val[cnt] = (u64)nla_get_uint(param_data);
+ cnt++;
+ }
+
+ value->u64arr.size = cnt;
+ break;
}
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v12 net-next 2/9] net/mlx5e: trim stack use in PCIe congestion threshold helper
From: Ratheesh Kannoth @ 2026-05-08 3:49 UTC (permalink / raw)
To: intel-wired-lan, linux-kernel, linux-rdma, netdev, oss-drivers
Cc: akiyano, andrew+netdev, anthony.l.nguyen, arkadiusz.kubalewski,
brett.creeley, darinzon, davem, donald.hunter, edumazet, horms,
idosch, ivecera, jiri, kuba, leon, mbloch, michael.chan, pabeni,
pavan.chebbi, petrm, Prathosh.Satish, przemyslaw.kitszel, saeedm,
sgoutham, tariqt, vadim.fedorenko, Ratheesh Kannoth
In-Reply-To: <20260508034912.4082520-1-rkannoth@marvell.com>
union devlink_param_value grew when U64 array parameters were added.
Keeping a four-element array of that union in
mlx5e_pcie_cong_get_thresh_config() inflated the stack frame past the
-Wframe-larger-than limit.
Read each driverinit value into a single reused union, then store the
four u16 thresholds in struct mlx5e_pcie_cong_thresh field order via a
temporary u16 pointer to config.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../mellanox/mlx5/core/en/pcie_cong_event.c | 34 +++++++++++--------
1 file changed, 19 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c b/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c
index 2eb666a46f39..88e76be3a73d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c
@@ -252,28 +252,32 @@ static int
mlx5e_pcie_cong_get_thresh_config(struct mlx5_core_dev *dev,
struct mlx5e_pcie_cong_thresh *config)
{
+ enum {
+ INBOUND_HIGH,
+ INBOUND_LOW,
+ OUTBOUND_HIGH,
+ OUTBOUND_LOW,
+ };
+
u32 ids[4] = {
- MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_LOW,
- MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_HIGH,
- MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_LOW,
- MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_HIGH,
+ [INBOUND_LOW] = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_LOW,
+ [INBOUND_HIGH] = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_HIGH,
+ [OUTBOUND_LOW] = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_LOW,
+ [OUTBOUND_HIGH] = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_HIGH,
};
- struct devlink *devlink = priv_to_devlink(dev);
- union devlink_param_value val[4];
- for (int i = 0; i < 4; i++) {
- u32 id = ids[i];
- int err;
+ struct devlink *devlink = priv_to_devlink(dev);
+ union devlink_param_value val;
+ u16 *dst = (u16 *)config;
+ int err;
- err = devl_param_driverinit_value_get(devlink, id, &val[i]);
+ for (int i = 0; i < ARRAY_SIZE(ids); i++) {
+ err = devl_param_driverinit_value_get(devlink, ids[i], &val);
if (err)
return err;
- }
- config->inbound_low = val[0].vu16;
- config->inbound_high = val[1].vu16;
- config->outbound_low = val[2].vu16;
- config->outbound_high = val[3].vu16;
+ dst[i] = val.vu16;
+ }
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH iwl-next v5 4/4] igc: add support for forcing link speed without autonegotiation
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Khai Wen Tan
In-Reply-To: <20260507214706.309984-1-khai.wen.tan@linux.intel.com>
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Allow users to force 10/100 Mb/s link speed and duplex via ethtool
when autonegotiation is disabled. Previously, the driver rejected
these requests with "Force mode currently not supported.".
Forcing at 1000 Mb/s and 2500 Mb/s is not supported.
Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
---
drivers/net/ethernet/intel/igc/igc_base.c | 35 ++++-
drivers/net/ethernet/intel/igc/igc_defines.h | 9 +-
drivers/net/ethernet/intel/igc/igc_ethtool.c | 138 ++++++++++++++-----
drivers/net/ethernet/intel/igc/igc_hw.h | 9 ++
drivers/net/ethernet/intel/igc/igc_mac.c | 12 ++
drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
drivers/net/ethernet/intel/igc/igc_phy.c | 65 ++++++++-
drivers/net/ethernet/intel/igc/igc_phy.h | 1 +
8 files changed, 220 insertions(+), 51 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_base.c b/drivers/net/ethernet/intel/igc/igc_base.c
index 1613b562d17c..ab9120a3127f 100644
--- a/drivers/net/ethernet/intel/igc/igc_base.c
+++ b/drivers/net/ethernet/intel/igc/igc_base.c
@@ -114,11 +114,35 @@ static s32 igc_setup_copper_link_base(struct igc_hw *hw)
u32 ctrl;
ctrl = rd32(IGC_CTRL);
- ctrl |= IGC_CTRL_SLU;
- ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX);
- wr32(IGC_CTRL, ctrl);
-
- ret_val = igc_setup_copper_link(hw);
+ ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX |
+ IGC_CTRL_SPEED_MASK | IGC_CTRL_FD);
+
+ if (hw->mac.autoneg_enabled) {
+ ctrl |= IGC_CTRL_SLU;
+ wr32(IGC_CTRL, ctrl);
+ ret_val = igc_setup_copper_link(hw);
+ } else {
+ ctrl |= IGC_CTRL_SLU | IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX;
+
+ switch (hw->mac.forced_speed_duplex) {
+ case IGC_FORCED_10H:
+ ctrl |= IGC_CTRL_SPEED_10;
+ break;
+ case IGC_FORCED_10F:
+ ctrl |= IGC_CTRL_SPEED_10 | IGC_CTRL_FD;
+ break;
+ case IGC_FORCED_100H:
+ ctrl |= IGC_CTRL_SPEED_100;
+ break;
+ case IGC_FORCED_100F:
+ ctrl |= IGC_CTRL_SPEED_100 | IGC_CTRL_FD;
+ break;
+ default:
+ return -IGC_ERR_CONFIG;
+ }
+ wr32(IGC_CTRL, ctrl);
+ ret_val = igc_setup_copper_link(hw);
+ }
return ret_val;
}
@@ -443,6 +467,7 @@ static const struct igc_phy_operations igc_phy_ops_base = {
.reset = igc_phy_hw_reset,
.read_reg = igc_read_phy_reg_gpy,
.write_reg = igc_write_phy_reg_gpy,
+ .force_speed_duplex = igc_force_speed_duplex,
};
const struct igc_info igc_base_info = {
diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h
index 9482ab11f050..3f504751c2d9 100644
--- a/drivers/net/ethernet/intel/igc/igc_defines.h
+++ b/drivers/net/ethernet/intel/igc/igc_defines.h
@@ -129,10 +129,13 @@
#define IGC_ERR_SWFW_SYNC 13
/* Device Control */
+#define IGC_CTRL_FD BIT(0) /* Full Duplex */
#define IGC_CTRL_RST 0x04000000 /* Global reset */
-
#define IGC_CTRL_PHY_RST 0x80000000 /* PHY Reset */
#define IGC_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
+#define IGC_CTRL_SPEED_MASK GENMASK(10, 8)
+#define IGC_CTRL_SPEED_10 FIELD_PREP(IGC_CTRL_SPEED_MASK, 0)
+#define IGC_CTRL_SPEED_100 FIELD_PREP(IGC_CTRL_SPEED_MASK, 1)
#define IGC_CTRL_FRCSPD 0x00000800 /* Force Speed */
#define IGC_CTRL_FRCDPX 0x00001000 /* Force Duplex */
#define IGC_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
@@ -673,6 +676,10 @@
#define IGC_GEN_POLL_TIMEOUT 1920
/* PHY Control Register */
+#define MII_CR_SPEED_MASK (BIT(6) | BIT(13))
+#define MII_CR_SPEED_10 0x0000 /* SSM=0, SSL=0: 10 Mb/s */
+#define MII_CR_SPEED_100 BIT(13) /* SSM=0, SSL=1: 100 Mb/s */
+#define MII_CR_DUPLEX_EN BIT(8) /* 0 = Half Duplex, 1 = Full Duplex */
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
#define MII_CR_POWER_DOWN 0x0800 /* Power down */
#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
index cfcbf2fdad6e..b103836a895f 100644
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
@@ -1914,44 +1914,58 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
- /* advertising link modes */
- if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half);
- if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full);
- if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half);
- if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full);
- if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full);
- if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full);
-
/* set autoneg settings */
ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
- ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
+ if (hw->mac.autoneg_enabled) {
+ ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
+ cmd->base.autoneg = AUTONEG_ENABLE;
+
+ /* advertising link modes only apply when autoneg is on */
+ if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 10baseT_Half);
+ if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 10baseT_Full);
+ if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 100baseT_Half);
+ if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 100baseT_Full);
+ if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 1000baseT_Full);
+ if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 2500baseT_Full);
+
+ /* Set pause flow control advertising */
+ switch (hw->fc.requested_mode) {
+ case igc_fc_full:
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ Pause);
+ break;
+ case igc_fc_rx_pause:
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ Pause);
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ Asym_Pause);
+ break;
+ case igc_fc_tx_pause:
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ Asym_Pause);
+ break;
+ default:
+ break;
+ }
+ } else {
+ cmd->base.autoneg = AUTONEG_DISABLE;
+ }
- /* Set pause flow control settings */
+ /* Pause is always supported */
ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
- switch (hw->fc.requested_mode) {
- case igc_fc_full:
- ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
- break;
- case igc_fc_rx_pause:
- ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
- ethtool_link_ksettings_add_link_mode(cmd, advertising,
- Asym_Pause);
- break;
- case igc_fc_tx_pause:
- ethtool_link_ksettings_add_link_mode(cmd, advertising,
- Asym_Pause);
- break;
- default:
- break;
- }
-
status = pm_runtime_suspended(&adapter->pdev->dev) ?
0 : rd32(IGC_STATUS);
@@ -1983,7 +1997,6 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
cmd->base.duplex = DUPLEX_UNKNOWN;
}
cmd->base.speed = speed;
- cmd->base.autoneg = AUTONEG_ENABLE;
/* MDI-X => 2; MDI =>1; Invalid =>0 */
if (hw->phy.media_type == igc_media_type_copper)
@@ -2000,6 +2013,37 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
return 0;
}
+/**
+ * igc_handle_autoneg_disabled - Configure forced speed/duplex settings
+ * @adapter: private driver structure
+ * @speed: requested speed (must be SPEED_10 or SPEED_100)
+ * @duplex: requested duplex
+ *
+ * Records forced speed/duplex when autoneg is disabled.
+ * Caller must validate speed before calling this function.
+ */
+static void igc_handle_autoneg_disabled(struct igc_adapter *adapter, u32 speed,
+ u8 duplex)
+{
+ struct igc_mac_info *mac = &adapter->hw.mac;
+
+ switch (speed) {
+ case SPEED_10:
+ mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ?
+ IGC_FORCED_10F : IGC_FORCED_10H;
+ break;
+ case SPEED_100:
+ mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ?
+ IGC_FORCED_100F : IGC_FORCED_100H;
+ break;
+ default:
+ WARN_ONCE(1, "Unsupported speed %u\n", speed);
+ return;
+ }
+
+ mac->autoneg_enabled = false;
+}
+
/**
* igc_handle_autoneg_enabled - Configure autonegotiation advertisement
* @adapter: private driver structure
@@ -2038,6 +2082,7 @@ static void igc_handle_autoneg_enabled(struct igc_adapter *adapter,
10baseT_Half))
advertised |= ADVERTISE_10_HALF;
+ hw->mac.autoneg_enabled = true;
hw->phy.autoneg_advertised = advertised;
if (adapter->fc_autoneg)
hw->fc.requested_mode = igc_fc_default;
@@ -2059,6 +2104,12 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
return -EINVAL;
}
+ if (cmd->base.autoneg != AUTONEG_ENABLE &&
+ cmd->base.autoneg != AUTONEG_DISABLE) {
+ netdev_info(dev, "Unsupported autoneg setting\n");
+ return -EINVAL;
+ }
+
/* MDI setting is only allowed when autoneg enabled because
* some hardware doesn't allow MDI setting when speed or
* duplex is forced.
@@ -2071,14 +2122,25 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
}
}
+ if (cmd->base.autoneg == AUTONEG_DISABLE) {
+ if (cmd->base.speed != SPEED_10 && cmd->base.speed != SPEED_100) {
+ netdev_info(dev, "Unsupported speed for forced link\n");
+ return -EINVAL;
+ }
+ if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL) {
+ netdev_info(dev, "Duplex must be half or full for forced link\n");
+ return -EINVAL;
+ }
+ }
+
while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
usleep_range(1000, 2000);
- if (cmd->base.autoneg == AUTONEG_ENABLE) {
+ if (cmd->base.autoneg == AUTONEG_ENABLE)
igc_handle_autoneg_enabled(adapter, cmd);
- } else {
- netdev_info(dev, "Force mode currently not supported\n");
- }
+ else
+ igc_handle_autoneg_disabled(adapter, cmd->base.speed,
+ cmd->base.duplex);
/* MDI-X => 2; MDI => 1; Auto => 3 */
if (cmd->base.eth_tp_mdix_ctrl) {
diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h
index 86ab8f566f44..62aaee55668a 100644
--- a/drivers/net/ethernet/intel/igc/igc_hw.h
+++ b/drivers/net/ethernet/intel/igc/igc_hw.h
@@ -73,6 +73,13 @@ struct igc_info {
extern const struct igc_info igc_base_info;
+enum igc_forced_speed_duplex {
+ IGC_FORCED_10H,
+ IGC_FORCED_10F,
+ IGC_FORCED_100H,
+ IGC_FORCED_100F,
+};
+
struct igc_mac_info {
struct igc_mac_operations ops;
@@ -93,6 +100,8 @@ struct igc_mac_info {
bool arc_subsystem_valid;
bool get_link_status;
+ bool autoneg_enabled;
+ enum igc_forced_speed_duplex forced_speed_duplex;
};
struct igc_nvm_operations {
diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
index 0a3d3f357505..d6f3f6618469 100644
--- a/drivers/net/ethernet/intel/igc/igc_mac.c
+++ b/drivers/net/ethernet/intel/igc/igc_mac.c
@@ -446,6 +446,17 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
u16 speed, duplex;
s32 ret_val = 0;
+ /* Without autoneg, flow control capability is not exchanged with the
+ * link partner. IEEE 802.3 prohibits flow control in half-duplex mode.
+ */
+ if (!hw->mac.autoneg_enabled) {
+ if (hw->mac.forced_speed_duplex == IGC_FORCED_10H ||
+ hw->mac.forced_speed_duplex == IGC_FORCED_100H)
+ hw->fc.current_mode = igc_fc_none;
+
+ goto force_fc;
+ }
+
/* In auto-neg, we need to check and see if Auto-Neg has completed,
* and if so, how the PHY and link partner has flow control
* configured.
@@ -607,6 +618,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
/* Now we call a subroutine to actually force the MAC
* controller to use the correct flow control settings.
*/
+force_fc:
ret_val = igc_force_mac_fc(hw);
if (ret_val) {
hw_dbg("Error forcing flow control settings\n");
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 72bc5128d8b8..437e1d1ef1e4 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -7298,7 +7298,7 @@ static int igc_probe(struct pci_dev *pdev,
/* Initialize link properties that are user-changeable */
adapter->fc_autoneg = true;
hw->phy.autoneg_advertised = 0xaf;
-
+ hw->mac.autoneg_enabled = true;
hw->fc.requested_mode = igc_fc_default;
hw->fc.current_mode = igc_fc_default;
diff --git a/drivers/net/ethernet/intel/igc/igc_phy.c b/drivers/net/ethernet/intel/igc/igc_phy.c
index 6c4d204aecfa..4cf737fb3b21 100644
--- a/drivers/net/ethernet/intel/igc/igc_phy.c
+++ b/drivers/net/ethernet/intel/igc/igc_phy.c
@@ -494,12 +494,20 @@ s32 igc_setup_copper_link(struct igc_hw *hw)
s32 ret_val = 0;
bool link;
- /* Setup autoneg and flow control advertisement and perform
- * autonegotiation.
- */
- ret_val = igc_copper_link_autoneg(hw);
- if (ret_val)
- goto out;
+ if (hw->mac.autoneg_enabled) {
+ /* Setup autoneg and flow control advertisement and perform
+ * autonegotiation.
+ */
+ ret_val = igc_copper_link_autoneg(hw);
+ if (ret_val)
+ goto out;
+ } else {
+ ret_val = hw->phy.ops.force_speed_duplex(hw);
+ if (ret_val) {
+ hw_dbg("Error Forcing Speed/Duplex\n");
+ goto out;
+ }
+ }
/* Check link status. Wait up to 100 microseconds for link to become
* valid.
@@ -778,3 +786,48 @@ u16 igc_read_phy_fw_version(struct igc_hw *hw)
return gphy_version;
}
+
+/**
+ * igc_force_speed_duplex - Force PHY speed and duplex settings
+ * @hw: pointer to the HW structure
+ *
+ * Programs the GPY PHY control register to disable autonegotiation
+ * and force the speed/duplex indicated by hw->mac.forced_speed_duplex.
+ */
+s32 igc_force_speed_duplex(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ u16 phy_ctrl;
+ s32 ret_val;
+
+ ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
+ if (ret_val)
+ return ret_val;
+
+ phy_ctrl &= ~(MII_CR_SPEED_MASK | MII_CR_DUPLEX_EN |
+ MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
+
+ switch (hw->mac.forced_speed_duplex) {
+ case IGC_FORCED_10H:
+ phy_ctrl |= MII_CR_SPEED_10;
+ break;
+ case IGC_FORCED_10F:
+ phy_ctrl |= MII_CR_SPEED_10 | MII_CR_DUPLEX_EN;
+ break;
+ case IGC_FORCED_100H:
+ phy_ctrl |= MII_CR_SPEED_100;
+ break;
+ case IGC_FORCED_100F:
+ phy_ctrl |= MII_CR_SPEED_100 | MII_CR_DUPLEX_EN;
+ break;
+ default:
+ return -IGC_ERR_CONFIG;
+ }
+
+ ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
+ if (ret_val)
+ return ret_val;
+
+ hw->mac.get_link_status = true;
+ return 0;
+}
diff --git a/drivers/net/ethernet/intel/igc/igc_phy.h b/drivers/net/ethernet/intel/igc/igc_phy.h
index 832a7e359f18..d37a89174826 100644
--- a/drivers/net/ethernet/intel/igc/igc_phy.h
+++ b/drivers/net/ethernet/intel/igc/igc_phy.h
@@ -18,5 +18,6 @@ void igc_power_down_phy_copper(struct igc_hw *hw);
s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data);
s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data);
u16 igc_read_phy_fw_version(struct igc_hw *hw);
+s32 igc_force_speed_duplex(struct igc_hw *hw);
#endif
--
2.43.0
^ permalink raw reply related
* [PATCH iwl-next v5 3/4] igc: replace goto out with direct returns in igc_config_fc_after_link_up()
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Khai Wen Tan
In-Reply-To: <20260507214706.309984-1-khai.wen.tan@linux.intel.com>
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
The out: label only returns ret_val with no cleanup. The kernel coding
style guide states: "If there is no cleanup needed then just return
directly." (Documentation/process/coding-style.rst, section 7).
This improves readability ahead of a subsequent patch that introduces a
new goto label in this function.
No functional change.
Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
---
drivers/net/ethernet/intel/igc/igc_mac.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
index 142beb9ae557..0a3d3f357505 100644
--- a/drivers/net/ethernet/intel/igc/igc_mac.c
+++ b/drivers/net/ethernet/intel/igc/igc_mac.c
@@ -458,15 +458,15 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
&mii_status_reg);
if (ret_val)
- goto out;
+ return ret_val;
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
&mii_status_reg);
if (ret_val)
- goto out;
+ return ret_val;
if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
hw_dbg("Copper PHY and Auto Neg has not completed.\n");
- goto out;
+ return ret_val;
}
/* The AutoNeg process has completed, so we now need to
@@ -478,11 +478,11 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV,
&mii_nway_adv_reg);
if (ret_val)
- goto out;
+ return ret_val;
ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY,
&mii_nway_lp_ability_reg);
if (ret_val)
- goto out;
+ return ret_val;
/* Two bits in the Auto Negotiation Advertisement Register
* (Address 4) and two bits in the Auto Negotiation Base
* Page Ability Register (Address 5) determine flow control
@@ -598,7 +598,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex);
if (ret_val) {
hw_dbg("Error getting link speed and duplex\n");
- goto out;
+ return ret_val;
}
if (duplex == HALF_DUPLEX)
@@ -610,10 +610,9 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = igc_force_mac_fc(hw);
if (ret_val) {
hw_dbg("Error forcing flow control settings\n");
- goto out;
+ return ret_val;
}
-out:
return ret_val;
}
--
2.43.0
^ permalink raw reply related
* [PATCH iwl-next v5 2/4] igc: move autoneg-enabled settings into igc_handle_autoneg_enabled()
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Aleksandr Loktionov, Khai Wen Tan
In-Reply-To: <20260507214706.309984-1-khai.wen.tan@linux.intel.com>
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Move the advertised link modes and flow control configuration from
igc_ethtool_set_link_ksettings() into igc_handle_autoneg_enabled().
No functional change.
Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
---
drivers/net/ethernet/intel/igc/igc_ethtool.c | 72 ++++++++++++--------
1 file changed, 44 insertions(+), 28 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
index 0122009bedd0..cfcbf2fdad6e 100644
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
@@ -2000,6 +2000,49 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
return 0;
}
+/**
+ * igc_handle_autoneg_enabled - Configure autonegotiation advertisement
+ * @adapter: private driver structure
+ * @cmd: ethtool link ksettings from user
+ *
+ * Records advertised speeds and flow control settings when autoneg
+ * is enabled.
+ */
+static void igc_handle_autoneg_enabled(struct igc_adapter *adapter,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct igc_hw *hw = &adapter->hw;
+ u16 advertised = 0;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 2500baseT_Full))
+ advertised |= ADVERTISE_2500_FULL;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 1000baseT_Full))
+ advertised |= ADVERTISE_1000_FULL;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 100baseT_Full))
+ advertised |= ADVERTISE_100_FULL;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 100baseT_Half))
+ advertised |= ADVERTISE_100_HALF;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 10baseT_Full))
+ advertised |= ADVERTISE_10_FULL;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 10baseT_Half))
+ advertised |= ADVERTISE_10_HALF;
+
+ hw->phy.autoneg_advertised = advertised;
+ if (adapter->fc_autoneg)
+ hw->fc.requested_mode = igc_fc_default;
+}
+
static int
igc_ethtool_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *cmd)
@@ -2007,7 +2050,6 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
struct igc_adapter *adapter = netdev_priv(netdev);
struct net_device *dev = adapter->netdev;
struct igc_hw *hw = &adapter->hw;
- u16 advertised = 0;
/* When adapter in resetting mode, autoneg/speed/duplex
* cannot be changed
@@ -2032,34 +2074,8 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
usleep_range(1000, 2000);
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 2500baseT_Full))
- advertised |= ADVERTISE_2500_FULL;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 1000baseT_Full))
- advertised |= ADVERTISE_1000_FULL;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 100baseT_Full))
- advertised |= ADVERTISE_100_FULL;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 100baseT_Half))
- advertised |= ADVERTISE_100_HALF;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 10baseT_Full))
- advertised |= ADVERTISE_10_FULL;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 10baseT_Half))
- advertised |= ADVERTISE_10_HALF;
-
if (cmd->base.autoneg == AUTONEG_ENABLE) {
- hw->phy.autoneg_advertised = advertised;
- if (adapter->fc_autoneg)
- hw->fc.requested_mode = igc_fc_default;
+ igc_handle_autoneg_enabled(adapter, cmd);
} else {
netdev_info(dev, "Force mode currently not supported\n");
}
--
2.43.0
^ permalink raw reply related
* [PATCH iwl-next v5 1/4] igc: remove unused autoneg_failed field
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Aleksandr Loktionov, Khai Wen Tan
In-Reply-To: <20260507214706.309984-1-khai.wen.tan@linux.intel.com>
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
autoneg_failed in struct igc_mac_info is never set in the igc driver.
Remove the field and the dead code checking it in
igc_config_fc_after_link_up().
The field originates from the e1000/e1000e fiber/serdes forced-link
path, where MAC-level autoneg timeout sets it to signal the flow-control
code to force pause. igc supports only copper, so it never needs to set
this field.
Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
---
drivers/net/ethernet/intel/igc/igc_hw.h | 1 -
drivers/net/ethernet/intel/igc/igc_mac.c | 16 +---------------
2 files changed, 1 insertion(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h
index be8a49a86d09..86ab8f566f44 100644
--- a/drivers/net/ethernet/intel/igc/igc_hw.h
+++ b/drivers/net/ethernet/intel/igc/igc_hw.h
@@ -92,7 +92,6 @@ struct igc_mac_info {
bool asf_firmware_present;
bool arc_subsystem_valid;
- bool autoneg_failed;
bool get_link_status;
};
diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
index 7ac6637f8db7..142beb9ae557 100644
--- a/drivers/net/ethernet/intel/igc/igc_mac.c
+++ b/drivers/net/ethernet/intel/igc/igc_mac.c
@@ -438,28 +438,14 @@ void igc_config_collision_dist(struct igc_hw *hw)
* Checks the status of auto-negotiation after link up to ensure that the
* speed and duplex were not forced. If the link needed to be forced, then
* flow control needs to be forced also. If auto-negotiation is enabled
- * and did not fail, then we configure flow control based on our link
- * partner.
+ * then we configure flow control based on our link partner.
*/
s32 igc_config_fc_after_link_up(struct igc_hw *hw)
{
u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
- struct igc_mac_info *mac = &hw->mac;
u16 speed, duplex;
s32 ret_val = 0;
- /* Check for the case where we have fiber media and auto-neg failed
- * so we had to force link. In this case, we need to force the
- * configuration of the MAC to match the "fc" parameter.
- */
- if (mac->autoneg_failed)
- ret_val = igc_force_mac_fc(hw);
-
- if (ret_val) {
- hw_dbg("Error forcing flow control settings\n");
- goto out;
- }
-
/* In auto-neg, we need to check and see if Auto-Neg has completed,
* and if so, how the PHY and link partner has flow control
* configured.
--
2.43.0
^ permalink raw reply related
* [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
This series adds support for forcing 10/100 Mb/s link speed via ethtool
when autonegotiation is disabled on the igc driver.
Changes in v5:
- add removal justification to include copper context in commit
description for igc: remove unused autoneg_failed field (Paul)
- check that cmd->base.duplex is either DUPLEX_HALF or DUPLEX_FULL
in igc_ethtool_set_link_ksettings() (Simon)
- dynamically override hw->fc.current_mode to igc_fc_none during
link configuration instead of mutating requested_mode (Simon)
Changes in v4:
- Validate that autoneg is AUTONEG_ENABLE or AUTONEG_DISABLE early
in igc_ethtool_set_link_ksettings() to avoid passing unexpected
values to igc_handle_autoneg_disabled(). (Simon Horman)
Changes in v3:
- Modify condition from "if (duplex == DUPLEX_HALF)" to
"if (duplex != DUPLEX_FULL)". (Simon Horman)
Changes in v2:
- When forcing half-duplex, set hw->fc.requested_mode = igc_fc_none,
since half-duplex cannot support flow control per IEEE 802.3.
(Simon Horman)
- Split the original single patch into three patches for clarity:
patches 1 and 2 are preparatory cleanups; patch 3 carries the
functional change.
v4 at:
https://patchwork.ozlabs.org/project/intel-wired-lan/cover/20260428060009.311393-1-khai.wen.tan@linux.intel.com/
v3 at:
https://patchwork.ozlabs.org/project/intel-wired-lan/cover/20260422155701.7420-1-khai.wen.tan@linux.intel.com/
v2 at:
https://patchwork.kernel.org/project/netdevbpf/patch/20260416015520.6090-4-khai.wen.tan@linux.intel.com/
v1 at:
https://patchwork.ozlabs.org/project/intel-wired-lan/patch/20260409072747.217836-1-khai.wen.tan@linux.intel.com/
Faizal Rahim (4):
igc: remove unused autoneg_failed field
igc: move autoneg-enabled settings into igc_handle_autoneg_enabled()
igc: replace goto out with direct returns in
igc_config_fc_after_link_up()
igc: add support for forcing link speed without autonegotiation
drivers/net/ethernet/intel/igc/igc_base.c | 35 +++-
drivers/net/ethernet/intel/igc/igc_defines.h | 9 +-
drivers/net/ethernet/intel/igc/igc_ethtool.c | 210 +++++++++++++------
drivers/net/ethernet/intel/igc/igc_hw.h | 10 +-
drivers/net/ethernet/intel/igc/igc_mac.c | 35 ++--
drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
drivers/net/ethernet/intel/igc/igc_phy.c | 65 +++++-
drivers/net/ethernet/intel/igc/igc_phy.h | 1 +
8 files changed, 268 insertions(+), 99 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH ipsec-next] xfrm: Use regular error handling instead of BUG_ON() in the netlink API.
From: Antony Antony @ 2026-05-08 3:44 UTC (permalink / raw)
To: Sabrina Dubroca; +Cc: Antony Antony, Steffen Klassert, netdev, devel
In-Reply-To: <afxJOGtMkRK5FrvG@krikkit>
On Thu, May 07, 2026 at 10:11:36AM +0200, Sabrina Dubroca via Devel wrote:
> 2026-05-07, 06:21:57 +0100, Antony Antony wrote:
> > wHi Steffen,
> >
> > Thanks Steffen, I was hit by this in the new XFRM_MIGRATE_STATE I am adding.
> > I am glad to see we are addressing this.
> >
> > On Wed, May 06, 2026 at 06:08:55PM +0200, Steffen Klassert via Devel wrote:
> > > The xfrm netlink API uses BUG_ON() on failures since it exists.
> > > However all these error are uncritical and can be handled
> > > with regular error handling. This fixes machine crashes
> > > in situations where an emergency break is not needed.
> >
> > While BUG_ON is an extreme measure for a recoverable netlink error, it does
> > have diagnostic value: it leaves a stack trace. The patch trades
> > a crash + stack trace for a silent error return, which loses observability.
> >
> > Would you consider using WARN_ONCE instead of a bare if (err < 0)?
> >
> > - BUG_ON(err < 0);
> > + if (WARN_ONCE(err < 0, "xfrm: build_spdinfo failed: %d\n", err)) {
> > + kfree_skb(r_skb);
> > + return err;
> > + }
>
> OTOH we already have a bunch of functions doing something similar
> without using BUG_ON/WARN_ON, so at least with this patch it becomes
> consistent.
>
> xfrm_notify_userpolicy
> xfrm_get_default
> xfrm_get_ae
> xfrm_exp_state_notify
> xfrm_notify_sa_flush
> xfrm_notify_sa
> xfrm_notify_policy
> xfrm_notify_policy_flush
>
>
> (I'm looking into generic ways to avoid this split getsize/fill that
> always becomes inconsistent in areas where new attributes are added
> frequently, but nothing to share yet)
>
> > Something like the above would preserve the "shouldn't happen" signal with a
> > stack trace on first occurrence, without panicking the machine.
> > Or are there better signaling styles in Kernel?
>
> Maybe DEBUG_NET_WARN_ON_ONCE so that only developers see those messages.
I would discourage DEBUG_NET_* here, because these situations are not often
caught by testing or by developers. Typically IPsec builds disable debug
completely - and I would even speculate that is why it was BUG_ON in the
early days. If IPsec failed, halting was justified back then. While these
days it is less so.
I vote for something along the lines of WARN_*. That has a higher chance of gathering better diagnostics for developers.
Would other IKE userspace developers like to chime in?
-antony
^ permalink raw reply
* [net-next v40] mctp pcc: Implement MCTP over PCC Transport
From: Adam Young @ 2026-05-08 3:29 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel, Sudeep Holla, Jonathan Cameron, Huisong Li
Implementation of network driver for
Management Component Transport Protocol(MCTP)
over Platform Communication Channel(PCC)
DMTF DSP:0292
Link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0292_1.0.0WIP50.pdf
The transport mechanism is called Platform Communication Channels (PCC)
is part of the ACPI spec:
Link: https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/14_Platform_Communications_Channel/Platform_Comm_Channel.html
The PCC mechanism is managed via a mailbox implemented at
drivers/mailbox/pcc.c
MCTP devices are specified via ACPI by entries in DSDT/SSDT and
reference channels specified in the PCCT. Messages are sent on a type
3 and received on a type 4 channel. Communication with other devices
use the PCC based doorbell mechanism; a shared memory segment with a
corresponding interrupt and a memory register used to trigger remote
interrupts.
The shared buffer must be at least 68 bytes long as that is the minimum
MTU as defined by the MCTP specification.
Unlike the existing PCC Type 2 based drivers, the mssg parameter to
mbox_send_msg is actively used. The data section of the struct sk_buff
that contains the outgoing packet is sent to the mailbox, already
properly formatted as a PCC exctended message.
If the mailbox ring buffer is full, the driver stops the incoming
packet queues until a message has been sent, freeing space in the
ring buffer.
When the Type 3 channel outbox receives a txdone response interrupt,
it consumes the outgoing sk_buff, allowing it to be freed.
Bringing up an interface creates the channel between the network driver
and the mailbox driver. This enables communication with the remote
endpoint, to include the receipt of new messages. Bringing down an
interface removes the channel, and no new messages can be delivered.
Stopping the interface will leave any packets that are cached in the
mailbox ringbuffer. They cannot safely be freed until the PCC mailbox
attempts to deliver them and has removed them from the ring buffer.
PCC is based on a shared buffer and a set of I/O mapped memory locations
that the Spec calls registers. This mechanism exists regardless of the
existence of the driver. If the user has the ability to map these
physical location to virtual locations, they have the ability to drive the
hardware. Thus, there is a security aspect to this mechanism that extends
beyond the responsibilities of the operating system.
If the hardware does not expose the PCC in the ACPI table, this device
will never be enabled. Thus it is only an issue on hardware that does
support PCC. In that case, it is up to the remote controller to sanitize
communication; MCTP will be exposed as a socket interface, and userland
can send any crafted packet it wants. It would also be incumbent on
the hardware manufacturer to allow the end user to disable MCTP over PCC
communication if they did not want to expose it.
Link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0292_1.0.0WIP50.pdf
Link: https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/14_Platform_Communications_Channel/Platform_Comm_Channel.html
Signed-off-by: Adam Young <admiyo@os.amperecomputing.com>
---
Previous Version:
https://lore.kernel.org/lkml/20260506214327.276069-1-admiyo@os.amperecomputing.com/
Changes from Previous version
mctp_pcc_client_rx_callback common error handler
MCTP_PCC only builds on little endian systems
WARN_ONCE to ndev_warn_once
remove change_mtu function
---
MAINTAINERS | 5 +
drivers/net/mctp/Kconfig | 16 ++
drivers/net/mctp/Makefile | 1 +
drivers/net/mctp/mctp-pcc.c | 413 ++++++++++++++++++++++++++++++++++++
4 files changed, 435 insertions(+)
create mode 100644 drivers/net/mctp/mctp-pcc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 5bbbbde6b907..252c77b24791 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15403,6 +15403,11 @@ F: include/net/mctpdevice.h
F: include/net/netns/mctp.h
F: net/mctp/
+MANAGEMENT COMPONENT TRANSPORT PROTOCOL (MCTP) over PCC (MCTP-PCC) Driver
+M: Adam Young <admiyo@os.amperecomputing.com>
+S: Maintained
+F: drivers/net/mctp/mctp-pcc.c
+
MAPLE TREE
M: Liam R. Howlett <liam@infradead.org>
R: Alice Ryhl <aliceryhl@google.com>
diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig
index cf325ab0b1ef..e68d23794a80 100644
--- a/drivers/net/mctp/Kconfig
+++ b/drivers/net/mctp/Kconfig
@@ -47,6 +47,22 @@ config MCTP_TRANSPORT_I3C
A MCTP protocol network device is created for each I3C bus
having a "mctp-controller" devicetree property.
+config MCTP_TRANSPORT_PCC
+ tristate "MCTP PCC transport"
+ depends on ACPI
+ depends on PCC
+ depends on 64BIT
+ depends on CPU_LITTLE_ENDIAN
+ help
+ Provides a driver to access MCTP devices over PCC transport,
+ A MCTP protocol network device is created via ACPI for each
+ entry in the DSDT/SSDT that matches the identifier. The Platform
+ communication channels are selected from the corresponding
+ entries in the PCCT.
+
+ Say y here if you need to connect to MCTP endpoints over PCC. To
+ compile as a module, use m; the module will be called mctp-pcc.
+
config MCTP_TRANSPORT_USB
tristate "MCTP USB transport"
depends on USB
diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile
index c36006849a1e..0a591299ffa9 100644
--- a/drivers/net/mctp/Makefile
+++ b/drivers/net/mctp/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o
obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o
obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o
+obj-$(CONFIG_MCTP_TRANSPORT_PCC) += mctp-pcc.o
obj-$(CONFIG_MCTP_TRANSPORT_USB) += mctp-usb.o
diff --git a/drivers/net/mctp/mctp-pcc.c b/drivers/net/mctp/mctp-pcc.c
new file mode 100644
index 000000000000..086f7e7712f2
--- /dev/null
+++ b/drivers/net/mctp/mctp-pcc.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mctp-pcc.c - Driver for MCTP over PCC.
+ * Copyright (c) 2024-2026, Ampere Computing LLC
+ *
+ */
+
+/* Implementation of MCTP over PCC DMTF Specification DSP0256
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0292_1.0.0WIP50.pdf
+ */
+
+#include <linux/acpi.h>
+#include <linux/hrtimer.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acrestyp.h>
+#include <acpi/actbl.h>
+#include <acpi/pcc.h>
+#include <net/mctp.h>
+#include <net/mctpdevice.h>
+
+#define MCTP_SIGNATURE "MCTP"
+#define MCTP_SIGNATURE_LENGTH (sizeof(MCTP_SIGNATURE) - 1)
+#define MCTP_MIN_MTU 68
+#define PCC_HEADER_SIZE sizeof(struct acpi_pcct_ext_pcc_shared_memory)
+#define MCTP_PCC_MIN_SIZE (PCC_HEADER_SIZE + MCTP_MIN_MTU)
+#define PCC_EXTRA_LEN (PCC_HEADER_SIZE - sizeof(pcc_header.command))
+struct mctp_pcc_mailbox {
+ u32 index;
+ struct pcc_mbox_chan *chan;
+ struct mbox_client client;
+};
+
+/* The netdev structure. One of these per PCC adapter. */
+struct mctp_pcc_ndev {
+ struct net_device *ndev;
+ struct acpi_device *acpi_device;
+ struct mctp_pcc_mailbox inbox;
+ struct mctp_pcc_mailbox outbox;
+};
+
+static void mctp_pcc_client_rx_callback(struct mbox_client *cl, void *mssg)
+{
+ struct acpi_pcct_ext_pcc_shared_memory pcc_header;
+ struct mctp_pcc_ndev *mctp_pcc_ndev;
+ struct mctp_pcc_mailbox *inbox;
+ struct mctp_skb_cb *cb;
+ struct sk_buff *skb;
+ u32 header_length;
+ int size;
+
+ mctp_pcc_ndev = container_of(cl, struct mctp_pcc_ndev, inbox.client);
+ inbox = &mctp_pcc_ndev->inbox;
+ memcpy_fromio(&pcc_header, inbox->chan->shmem, sizeof(pcc_header));
+
+ // The message must at least have the PCC command indicating it is an MCTP
+ // message followed by the MCTP header, or we have a malformed message.
+ // This may be run on big endian system, but the data in the buffer is
+ // explicitly little endian.
+ header_length = le32_to_cpu(pcc_header.length);
+
+ if (header_length < sizeof(pcc_header.command) + sizeof(struct mctp_hdr))
+ goto error;
+ // If the reported size is larger than the shared memory minus headers,
+ // something is wrong and treat the buffer as corrupted data.
+ if (header_length > inbox->chan->shmem_size - PCC_EXTRA_LEN)
+ goto error;
+ if (memcmp(&pcc_header.command, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH) != 0)
+ goto error;
+
+ size = header_length + PCC_EXTRA_LEN;
+ skb = netdev_alloc_skb(mctp_pcc_ndev->ndev, size);
+ if (!skb)
+ goto error;
+ skb_put(skb, size);
+ skb->protocol = htons(ETH_P_MCTP);
+ memcpy_fromio(skb->data, inbox->chan->shmem, size);
+ dev_dstats_rx_add(mctp_pcc_ndev->ndev, size);
+ skb_pull(skb, sizeof(pcc_header));
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ cb = __mctp_cb(skb);
+ cb->halen = 0;
+ netif_rx(skb);
+ return;
+error:
+ dev_dstats_rx_dropped(mctp_pcc_ndev->ndev);
+}
+
+static netdev_tx_t mctp_pcc_tx(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct acpi_pcct_ext_pcc_shared_memory *pcc_header;
+ struct mctp_pcc_ndev *mpnd = netdev_priv(ndev);
+ int len = skb->len;
+
+ /* Consolidated a fragmented packet into contiguous memory */
+ if (skb_is_nonlinear(skb)) {
+ if (skb_linearize(skb))
+ goto error;
+ }
+
+ if (skb_cow_head(skb, sizeof(*pcc_header)))
+ goto error;
+ /**
+ * This code could potentially be run on A Big Endian
+ * System. The ACPI specification requires that values
+ * in the shared buffer be little endian.
+ */
+ pcc_header = skb_push(skb, sizeof(*pcc_header));
+ pcc_header->signature = PCC_SIGNATURE | mpnd->outbox.index;
+ pcc_header->flags = PCC_CMD_COMPLETION_NOTIFY;
+ memcpy(&pcc_header->command, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH);
+ pcc_header->length = len + MCTP_SIGNATURE_LENGTH;
+
+ if (mbox_send_message(mpnd->outbox.chan->mchan, skb) < 0) {
+ // Remove the header in case it gets sent again
+ skb_pull(skb, sizeof(*pcc_header));
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
+ }
+
+ return NETDEV_TX_OK;
+error:
+ dev_dstats_tx_dropped(ndev);
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static void mctp_pcc_tx_prepare(struct mbox_client *cl, void *mssg)
+{
+ struct mctp_pcc_ndev *mctp_pcc_ndev;
+ struct mctp_pcc_mailbox *outbox;
+ struct sk_buff *skb = mssg;
+
+ mctp_pcc_ndev = container_of(cl, struct mctp_pcc_ndev, outbox.client);
+ outbox = &mctp_pcc_ndev->outbox;
+
+ /* The PCC Mailbox typically does not make use of the mssg pointer
+ * The mctp-over pcc driver is the only client that uses it.
+ * This value should always be non-null; it is possible
+ * that a change in the Mailbox level will break that assumption.
+ */
+ if (!skb) {
+ netdev_warn_once(mctp_pcc_ndev->ndev,
+ "%s called with null message.\n", __func__);
+ return;
+ }
+
+ if (skb->len > outbox->chan->shmem_size) {
+ dev_dstats_tx_dropped(mctp_pcc_ndev->ndev);
+ return;
+ }
+ memcpy_toio(outbox->chan->shmem, skb->data, skb->len);
+ dev_dstats_tx_add(mctp_pcc_ndev->ndev, skb->len);
+}
+
+static void mctp_pcc_tx_done(struct mbox_client *c, void *mssg, int r)
+{
+ struct mctp_pcc_ndev *mctp_pcc_ndev;
+ struct sk_buff *skb = mssg;
+
+ /*
+ * If there is a packet in flight during driver cleanup
+ * It may have been freed already.
+ */
+ if (!mssg)
+ return;
+ /*
+ * If the return code is non-zero, we should not report the packet
+ * as transmitted. However, we are in IRQ context right now, and we
+ * cannot safely write transmission statistics. This packet was already
+ * reported as transmitted in in mctp_pcc_tx_prepare.
+ */
+ mctp_pcc_ndev = container_of(c, struct mctp_pcc_ndev, outbox.client);
+ dev_consume_skb_any(skb);
+ netif_wake_queue(mctp_pcc_ndev->ndev);
+}
+
+static int mctp_pcc_open(struct net_device *ndev)
+{
+ struct mctp_pcc_ndev *mctp_pcc_ndev = netdev_priv(ndev);
+ struct mctp_pcc_mailbox *outbox, *inbox;
+
+ outbox = &mctp_pcc_ndev->outbox;
+ inbox = &mctp_pcc_ndev->inbox;
+
+ outbox->chan = pcc_mbox_request_channel(&outbox->client, outbox->index);
+ if (IS_ERR(outbox->chan))
+ return PTR_ERR(outbox->chan);
+ if (outbox->chan->shmem_size < MCTP_PCC_MIN_SIZE) {
+ pcc_mbox_free_channel(outbox->chan);
+ return -EINVAL;
+ }
+
+ inbox->client.rx_callback = mctp_pcc_client_rx_callback;
+ inbox->chan = pcc_mbox_request_channel(&inbox->client, inbox->index);
+ if (IS_ERR(inbox->chan)) {
+ pcc_mbox_free_channel(outbox->chan);
+ return PTR_ERR(inbox->chan);
+ }
+ if (inbox->chan->shmem_size < MCTP_PCC_MIN_SIZE) {
+ pcc_mbox_free_channel(outbox->chan);
+ pcc_mbox_free_channel(inbox->chan);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mctp_pcc_stop(struct net_device *ndev)
+{
+ struct mctp_pcc_ndev *mctp_pcc_ndev;
+ unsigned int count, idx;
+ struct mbox_chan *chan;
+ struct sk_buff *skb;
+
+ mctp_pcc_ndev = netdev_priv(ndev);
+ chan = mctp_pcc_ndev->outbox.chan->mchan;
+ pcc_mbox_free_channel(mctp_pcc_ndev->inbox.chan);
+ scoped_guard(spinlock_irqsave, &chan->lock) {
+ skb = chan->active_req;
+ chan->active_req = NULL;
+ if (skb) {
+ dev_dstats_tx_dropped(ndev);
+ dev_consume_skb_any(skb);
+ }
+ while (chan->msg_count > 0) {
+ count = chan->msg_count;
+ idx = chan->msg_free;
+ if (idx >= count)
+ idx -= count;
+ else
+ idx += MBOX_TX_QUEUE_LEN - count;
+ skb = chan->msg_data[idx];
+ dev_dstats_tx_dropped(ndev);
+ dev_consume_skb_any(skb);
+ chan->msg_count--;
+ }
+ }
+ pcc_mbox_free_channel(mctp_pcc_ndev->outbox.chan);
+ return 0;
+}
+
+static const struct net_device_ops mctp_pcc_netdev_ops = {
+ .ndo_open = mctp_pcc_open,
+ .ndo_stop = mctp_pcc_stop,
+ .ndo_start_xmit = mctp_pcc_tx,
+};
+
+static void mctp_pcc_setup(struct net_device *ndev)
+{
+ ndev->type = ARPHRD_MCTP;
+ ndev->hard_header_len = sizeof(struct acpi_pcct_ext_pcc_shared_memory);
+ ndev->tx_queue_len = 0;
+ ndev->flags = IFF_NOARP;
+ ndev->netdev_ops = &mctp_pcc_netdev_ops;
+ ndev->needs_free_netdev = true;
+ ndev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
+}
+
+struct mctp_pcc_lookup_context {
+ int index;
+ u32 inbox_index;
+ u32 outbox_index;
+};
+
+static acpi_status lookup_pcct_indices(struct acpi_resource *ares,
+ void *context)
+{
+ struct mctp_pcc_lookup_context *luc = context;
+ struct acpi_resource_address32 *addr;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_ADDRESS32)
+ return AE_OK;
+
+ addr = ACPI_CAST_PTR(struct acpi_resource_address32, &ares->data);
+ switch (luc->index) {
+ case 0:
+ luc->outbox_index = addr[0].address.minimum;
+ break;
+ case 1:
+ luc->inbox_index = addr[0].address.minimum;
+ break;
+ default:
+ return AE_ERROR;
+ }
+ luc->index++;
+ return AE_OK;
+}
+
+static void mctp_cleanup_netdev(void *data)
+{
+ struct net_device *ndev = data;
+
+ mctp_unregister_netdev(ndev);
+}
+
+static int initialize_mtu(struct net_device *ndev)
+{
+ struct mctp_pcc_ndev *mctp_pcc_ndev;
+ struct mctp_pcc_mailbox *outbox;
+ struct pcc_mbox_chan *pchan;
+ int mctp_pcc_max_mtu;
+
+ mctp_pcc_ndev = netdev_priv(ndev);
+ outbox = &mctp_pcc_ndev->outbox;
+ pchan = pcc_mbox_request_channel(&outbox->client, outbox->index);
+ if (IS_ERR(pchan))
+ return PTR_ERR(pchan);
+ if (pchan->shmem_size < MCTP_MIN_MTU + sizeof(struct acpi_pcct_ext_pcc_shared_memory)) {
+ pcc_mbox_free_channel(pchan);
+ return -EINVAL;
+ }
+ mctp_pcc_max_mtu = pchan->shmem_size - sizeof(struct acpi_pcct_ext_pcc_shared_memory);
+ pcc_mbox_free_channel(pchan);
+
+ ndev->mtu = MCTP_MIN_MTU;
+ ndev->max_mtu = mctp_pcc_max_mtu;
+ ndev->min_mtu = MCTP_MIN_MTU;
+
+ return 0;
+}
+
+static int mctp_pcc_driver_add(struct acpi_device *acpi_dev)
+{
+ struct mctp_pcc_lookup_context context = {0};
+ struct mctp_pcc_ndev *mctp_pcc_ndev;
+ struct device *dev = &acpi_dev->dev;
+ struct net_device *ndev;
+ acpi_handle dev_handle;
+ acpi_status status;
+ char name[32];
+ int rc;
+
+ dev_dbg(dev, "Adding mctp_pcc device for HID %s\n",
+ acpi_device_hid(acpi_dev));
+ dev_handle = acpi_device_handle(acpi_dev);
+ status = acpi_walk_resources(dev_handle, "_CRS", lookup_pcct_indices,
+ &context);
+ if (!ACPI_SUCCESS(status)) {
+ dev_err(dev, "FAILED to lookup PCC indexes from CRS\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Ensure we have exactly 2 channels: an outbox and an inbox.
+ */
+ if (context.index != 2)
+ return -EINVAL;
+
+ snprintf(name, sizeof(name), "mctppcc%d", context.inbox_index);
+ ndev = alloc_netdev(sizeof(*mctp_pcc_ndev), name, NET_NAME_PREDICTABLE,
+ mctp_pcc_setup);
+ if (!ndev)
+ return -ENOMEM;
+
+ mctp_pcc_ndev = netdev_priv(ndev);
+
+ mctp_pcc_ndev->inbox.index = context.inbox_index;
+ mctp_pcc_ndev->inbox.client.dev = dev;
+ mctp_pcc_ndev->outbox.index = context.outbox_index;
+ mctp_pcc_ndev->outbox.client.dev = dev;
+
+ mctp_pcc_ndev->outbox.client.tx_prepare = mctp_pcc_tx_prepare;
+ mctp_pcc_ndev->outbox.client.tx_done = mctp_pcc_tx_done;
+ mctp_pcc_ndev->acpi_device = acpi_dev;
+ mctp_pcc_ndev->ndev = ndev;
+ acpi_dev->driver_data = mctp_pcc_ndev;
+
+ rc = initialize_mtu(ndev);
+ if (rc)
+ goto free_netdev;
+
+ rc = mctp_register_netdev(ndev, NULL, MCTP_PHYS_BINDING_PCC);
+ if (rc)
+ goto free_netdev;
+
+ return devm_add_action_or_reset(dev, mctp_cleanup_netdev, ndev);
+free_netdev:
+ free_netdev(ndev);
+ return rc;
+}
+
+static const struct acpi_device_id mctp_pcc_device_ids[] = {
+ { "DMT0001" },
+ {}
+};
+
+static struct acpi_driver mctp_pcc_driver = {
+ .name = "mctp_pcc",
+ .class = "Unknown",
+ .ids = mctp_pcc_device_ids,
+ .ops = {
+ .add = mctp_pcc_driver_add,
+ },
+};
+
+module_acpi_driver(mctp_pcc_driver);
+
+MODULE_DEVICE_TABLE(acpi, mctp_pcc_device_ids);
+
+MODULE_DESCRIPTION("MCTP PCC ACPI device");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Adam Young <admiyo@os.amperecomputing.com>");
--
2.43.0
^ permalink raw reply related
* [PATCH net-next] selftests: net: add tests for PPPoL2TP
From: Qingfang Deng @ 2026-05-08 3:21 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan, Felix Maurer, Qingfang Deng,
Sebastian Andrzej Siewior, Petr Machata, linux-kernel, linux-ppp,
netdev, linux-kselftest
Add ping, iperf3, and recursion tests for PPPoL2TP.
Assisted-by: Gemini:gemini-3-flash
Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
---
tools/testing/selftests/net/ppp/Makefile | 1 +
tools/testing/selftests/net/ppp/config | 3 +
tools/testing/selftests/net/ppp/pppol2tp.sh | 93 +++++++++++++++++++++
3 files changed, 97 insertions(+)
create mode 100755 tools/testing/selftests/net/ppp/pppol2tp.sh
diff --git a/tools/testing/selftests/net/ppp/Makefile b/tools/testing/selftests/net/ppp/Makefile
index b39b0abadde6..6036fa134351 100644
--- a/tools/testing/selftests/net/ppp/Makefile
+++ b/tools/testing/selftests/net/ppp/Makefile
@@ -5,6 +5,7 @@ top_srcdir = ../../../../..
TEST_PROGS := \
ppp_async.sh \
pppoe.sh \
+ pppol2tp.sh \
# end of TEST_PROGS
TEST_FILES := \
diff --git a/tools/testing/selftests/net/ppp/config b/tools/testing/selftests/net/ppp/config
index b45d25c5b970..8bc3b7c33d5c 100644
--- a/tools/testing/selftests/net/ppp/config
+++ b/tools/testing/selftests/net/ppp/config
@@ -1,4 +1,6 @@
CONFIG_IPV6=y
+CONFIG_L2TP=m
+CONFIG_NETKIT=y
CONFIG_PACKET=y
CONFIG_PPP=m
CONFIG_PPP_ASYNC=m
@@ -6,4 +8,5 @@ CONFIG_PPP_BSDCOMP=m
CONFIG_PPP_DEFLATE=m
CONFIG_PPPOE=m
CONFIG_PPPOE_HASH_BITS_4=y
+CONFIG_PPPOL2TP=m
CONFIG_VETH=y
diff --git a/tools/testing/selftests/net/ppp/pppol2tp.sh b/tools/testing/selftests/net/ppp/pppol2tp.sh
new file mode 100755
index 000000000000..5ddcc6c41c33
--- /dev/null
+++ b/tools/testing/selftests/net/ppp/pppol2tp.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+source ppp_common.sh
+
+NK_SERVER="nk0"
+NK_CLIENT="nk1"
+OUTER_IP_SERVER="172.16.1.1"
+OUTER_IP_CLIENT="172.16.1.2"
+
+PPPOL2TP_DIR=$(mktemp -d /tmp/pppol2tp.XXXXXX)
+
+# shellcheck disable=SC2329
+cleanup() {
+ cleanup_all_ns
+ rm -rf "$PPPOL2TP_DIR"
+}
+
+trap cleanup EXIT
+
+require_command xl2tpd
+ppp_common_init
+modprobe -q l2tp_ppp
+
+# Create the netkit pair
+ip -netns "$NS_CLIENT" link add "$NK_CLIENT" type netkit
+ip -netns "$NS_CLIENT" link set "$NK_SERVER" netns "$NS_SERVER"
+ip -netns "$NS_SERVER" link set "$NK_SERVER" up
+ip -netns "$NS_CLIENT" link set "$NK_CLIENT" up
+ip -netns "$NS_SERVER" address add dev "$NK_SERVER" "$OUTER_IP_SERVER" peer "$OUTER_IP_CLIENT"
+ip -netns "$NS_CLIENT" address add dev "$NK_CLIENT" "$OUTER_IP_CLIENT" peer "$OUTER_IP_SERVER"
+
+# Generate configuration files
+cat > "$PPPOL2TP_DIR/l2tp-server.conf" <<EOF
+[global]
+listen-addr = $OUTER_IP_SERVER
+access control = no
+
+[lns default]
+ip range = $IP_CLIENT
+local ip = $IP_SERVER
+require authentication = no
+require chap = no
+require pap = no
+ppp debug = yes
+pppoptfile = $(pwd)/pppoe-server-options
+EOF
+
+cat > "$PPPOL2TP_DIR/l2tp-client.conf" <<EOF
+[global]
+listen-addr = $OUTER_IP_CLIENT
+access control = no
+
+[lac server]
+lns = $OUTER_IP_SERVER
+require authentication = no
+require chap = no
+require pap = no
+ppp debug = yes
+pppoptfile = $(pwd)/pppoe-server-options
+EOF
+
+# Start the L2TP Server
+ip netns exec "$NS_SERVER" xl2tpd -D -c "$PPPOL2TP_DIR/l2tp-server.conf" \
+ -p "$PPPOL2TP_DIR/l2tp-server.pid" -C "$PPPOL2TP_DIR/l2tp-server.control" &
+
+# Start the L2TP Client
+ip netns exec "$NS_CLIENT" xl2tpd -D -c "$PPPOL2TP_DIR/l2tp-client.conf" \
+ -p "$PPPOL2TP_DIR/l2tp-client.pid" -C "$PPPOL2TP_DIR/l2tp-client.control" &
+
+# Wait for xl2tpd to start and open their control pipes
+slowwait 2 [ -p "$PPPOL2TP_DIR/l2tp-server.control" ]
+slowwait 2 [ -p "$PPPOL2TP_DIR/l2tp-client.control" ]
+
+# Connect LAC to LNS
+echo "c server" > "$PPPOL2TP_DIR/l2tp-client.control"
+
+ppp_test_connectivity
+
+log_test "PPPoL2TP"
+
+# Recursion test
+# Delete route to LNS IP
+ip -netns "$NS_CLIENT" route del "$OUTER_IP_SERVER"
+# Add default route through ppp0
+ip -netns "$NS_CLIENT" route add default dev ppp0
+# ping (we expect the ping to fail but not deadlock the system)
+ip netns exec "$NS_CLIENT" ping -c 1 "$IP_SERVER" -w 1
+check_fail $?
+
+log_test "PPPoL2TP Recursion"
+
+exit "$EXIT_STATUS"
--
2.43.0
^ permalink raw reply related
* [PATCH iwl-next 8/8] ixgbe: add IXGBE_ITR_ADAPTIVE_MASK_USECS constant
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov; +Cc: netdev
In-Reply-To: <20260508031226.3601800-1-aleksandr.loktionov@intel.com>
From: Alexander Duyck <alexander.h.duyck@intel.com>
ixgbe_set_itr() clears the mode flag (IXGBE_ITR_ADAPTIVE_LATENCY, bit 7)
with the open-coded complement expression ~IXGBE_ITR_ADAPTIVE_LATENCY.
This is equivalent to keeping only bits [6:0], i.e. the usecs sub-field.
Add IXGBE_ITR_ADAPTIVE_MASK_USECS = IXGBE_ITR_ADAPTIVE_LATENCY - 1 =
0x7F to name this mask explicitly and replace the open-coded AND-NOT
operation with the cleaner AND form. The two expressions are
arithmetically identical; the change improves readability.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 +
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index cf2df18..20e2a97 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -478,6 +478,7 @@ static inline unsigned int ixgbe_rx_pg_order(struct ixgbe_ring *ring)
#define IXGBE_ITR_ADAPTIVE_MAX_USECS 126
#define IXGBE_ITR_ADAPTIVE_LATENCY 0x80
#define IXGBE_ITR_ADAPTIVE_BULK 0x00
+#define IXGBE_ITR_ADAPTIVE_MASK_USECS (IXGBE_ITR_ADAPTIVE_LATENCY - 1)
struct ixgbe_ring_container {
struct ixgbe_ring *ring; /* pointer to linked list of rings */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index ba7b013..be40655 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -2959,7 +2959,7 @@ static void ixgbe_set_itr(struct ixgbe_q_vector *q_vector)
new_itr = min(q_vector->rx.itr, q_vector->tx.itr);
/* Clear latency flag if set, shift into correct position */
- new_itr &= ~IXGBE_ITR_ADAPTIVE_LATENCY;
+ new_itr &= IXGBE_ITR_ADAPTIVE_MASK_USECS;
new_itr <<= 2;
if (new_itr != q_vector->itr) {
--
2.52.0
^ permalink raw reply related
* [PATCH iwl-next 7/8] ixgbe: limit ITR decrease in latency mode to prevent ACK overdrive
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov; +Cc: netdev
In-Reply-To: <20260508031226.3601800-1-aleksandr.loktionov@intel.com>
From: Alexander Duyck <alexander.h.duyck@intel.com>
When operating in latency mode and the computed ITR is lower than the
current setting, the algorithm can reduce the interrupt rate too
aggressively in a single step. For a TCP workload this means the ACK
stream (a latency-sensitive, low-packet-rate workload) can drive the
moderation down to very high interrupt rates, starving CPU time from
the sender side.
After the speed-based ITR calculation is complete, check whether the
result is in latency mode and would decrease below the current setting.
If so, limit the decrease to at most IXGBE_ITR_ADAPTIVE_MIN_INC (2 us)
per update. This ensures the number of interrupts grows by no more
than 2x per adjustment step for latency-class workloads, dialling in
smoothly rather than overshooting.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index aea76b3..ba7b013 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -2888,6 +2888,17 @@ static void ixgbe_update_itr(struct ixgbe_q_vector *q_vector,
break;
}
+ /* In the case of a latency specific workload only allow us to
+ * reduce the ITR by at most 2us. By doing this we should dial
+ * in so that our number of interrupts is no more than 2x the number
+ * of packets for the least busy workload. So for example in the case
+ * of a TCP workload the ACK packets being received would set the
+ * interrupt rate as they are a latency specific workload.
+ */
+ if ((itr & IXGBE_ITR_ADAPTIVE_LATENCY) && itr < ring_container->itr)
+ itr = max_t(unsigned int, itr,
+ ring_container->itr - IXGBE_ITR_ADAPTIVE_MIN_INC);
+
clear_counts:
/* write back value */
ring_container->itr = itr;
--
2.52.0
^ permalink raw reply related
* [PATCH iwl-next 6/8] ixgbe: extract ixgbe_restart_auto_neg() to avoid code duplication
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov; +Cc: netdev
In-Reply-To: <20260508031226.3601800-1-aleksandr.loktionov@intel.com>
From: Jakub Chylkowski <jakubx.chylkowski@intel.com>
Both ixgbe_setup_phy_link_generic() and ixgbe_setup_phy_link_tnx()
end with the same three-line sequence that reads MDIO_CTRL1, sets
the MDIO_AN_CTRL1_RESTART bit, and writes MDIO_CTRL1 back.
Factor it out into a static helper ixgbe_restart_auto_neg() and call
it from both sites.
While at it, also check the return value of phy.ops.read_reg() in the
helper and skip the write on failure. The original inlined code
ignored the read result and would OR MDIO_AN_CTRL1_RESTART into a
stale autoneg_reg value (left over from the prior MDIO_AN_ADVERTISE
write) and unconditionally write it back to MDIO_CTRL1 if the read
failed. This is a small behavioral change: on read_reg() failure the
restart write is now skipped instead of being issued with a
potentially garbage value.
Signed-off-by: Jakub Chylkowski <jakubx.chylkowski@intel.com>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 36 ++++++++++++--------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
index de8f6c6..c7387a4 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -1089,6 +1089,26 @@ int ixgbe_mii_bus_init(struct ixgbe_hw *hw)
return mdiobus_register(bus);
}
+/**
+ * ixgbe_restart_auto_neg - restart PHY autonegotiation
+ * @hw: pointer to hardware structure
+ *
+ * Sets the restart autoneg bit in MDIO_CTRL1 to trigger a new
+ * autonegotiation cycle.
+ **/
+static void ixgbe_restart_auto_neg(struct ixgbe_hw *hw)
+{
+ u16 autoneg_reg;
+ int status;
+
+ status = hw->phy.ops.read_reg(hw, MDIO_CTRL1, MDIO_MMD_AN,
+ &autoneg_reg);
+ if (status)
+ return;
+ autoneg_reg |= MDIO_AN_CTRL1_RESTART;
+ hw->phy.ops.write_reg(hw, MDIO_CTRL1, MDIO_MMD_AN, autoneg_reg);
+}
+
/**
* ixgbe_setup_phy_link_generic - Set and restart autoneg
* @hw: pointer to hardware structure
@@ -1156,13 +1176,7 @@ int ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw)
return 0;
/* Restart PHY autonegotiation and wait for completion */
- hw->phy.ops.read_reg(hw, MDIO_CTRL1,
- MDIO_MMD_AN, &autoneg_reg);
-
- autoneg_reg |= MDIO_AN_CTRL1_RESTART;
-
- hw->phy.ops.write_reg(hw, MDIO_CTRL1,
- MDIO_MMD_AN, autoneg_reg);
+ ixgbe_restart_auto_neg(hw);
return status;
}
@@ -1386,13 +1400,7 @@ int ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw)
return 0;
/* Restart PHY autonegotiation and wait for completion */
- hw->phy.ops.read_reg(hw, MDIO_CTRL1,
- MDIO_MMD_AN, &autoneg_reg);
-
- autoneg_reg |= MDIO_AN_CTRL1_RESTART;
-
- hw->phy.ops.write_reg(hw, MDIO_CTRL1,
- MDIO_MMD_AN, autoneg_reg);
+ ixgbe_restart_auto_neg(hw);
return 0;
}
--
2.52.0
^ permalink raw reply related
* [PATCH iwl-next 5/8] ixgbe: use ktime_get_real_ns() in ixgbe_ptp_reset()
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov
Cc: netdev, Jacob Keller, Marcin Szycik, Simon Horman
In-Reply-To: <20260508031226.3601800-1-aleksandr.loktionov@intel.com>
From: Jacob Keller <jacob.e.keller@intel.com>
Replace ktime_to_ns(ktime_get_real()) with the direct equivalent
ktime_get_real_ns() in ixgbe_ptp_reset(). Using the combined helper
avoids the unnecessary intermediate ktime_t variable and makes the
intent clearer.
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
index 6885d23..a7d1635 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
@@ -1347,7 +1347,7 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
spin_lock_irqsave(&adapter->tmreg_lock, flags);
timecounter_init(&adapter->hw_tc, &adapter->hw_cc,
- ktime_to_ns(ktime_get_real()));
+ ktime_get_real_ns());
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
adapter->last_overflow_check = jiffies;
--
2.52.0
^ permalink raw reply related
* [PATCH iwl-next 4/8] ixgbe: increase SECRX_RDY polling frequency in ixgbe_disable_rx_buff_generic
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov; +Cc: netdev
In-Reply-To: <20260508031226.3601800-1-aleksandr.loktionov@intel.com>
From: Maciej Rabeda <maciej.rabeda@intel.com>
ixgbe_disable_rx_buff_generic polls for SECRX_RDY with 40 iterations
and a 1000 us (1 ms) busy-wait per iteration via udelay(), giving an
exact total wait of 40 ms. On fast hardware the security block is
typically ready well under 1 ms, so each iteration wastes up to
999 us of stalled initialization time.
Replace udelay(1000) with usleep_range(10, 20) and raise the iteration
limit to 4000. Because usleep_range(min, max) is guaranteed to sleep
at least 'min' microseconds, 4000 * 10 us preserves the original 40 ms
minimum wait before timeout. The worst-case is now ~80 ms (4000 * 20
us) plus scheduler latency, which is acceptable since SECRX_RDY
failing to assert is a non-fatal informational condition (the function
just logs a debug message).
On platforms where SECRX_RDY asserts quickly this reduces the typical
stall from up to ~1 ms per iteration to ~10-20 us. The function is
called only from process context, so usleep_range is appropriate.
Signed-off-by: Maciej Rabeda <maciej.rabeda@intel.com>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
index 3ea6765..c85618c 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
@@ -2683,7 +2683,7 @@ int prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked)
**/
int ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw)
{
-#define IXGBE_MAX_SECRX_POLL 40
+#define IXGBE_MAX_SECRX_POLL 4000
int i;
int secrxreg;
@@ -2695,8 +2695,7 @@ int ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw)
if (secrxreg & IXGBE_SECRXSTAT_SECRX_RDY)
break;
else
- /* Use interrupt-safe sleep just in case */
- udelay(1000);
+ usleep_range(10, 20);
}
/* For informational purposes only */
--
2.52.0
^ permalink raw reply related
* [PATCH iwl-next 3/8] ixgbe: prevent adding duplicate FDIR perfect filter rules
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov
Cc: netdev, Piotr Skajewski
In-Reply-To: <20260508031226.3601800-1-aleksandr.loktionov@intel.com>
From: Piotr Skajewski <piotrx.skajewski@intel.com>
When the same flow specification is added twice (same 5-tuple with
different sw_idx values), ixgbe_add_ethtool_fdir_entry() silently
programs the duplicate into hardware using a second FDIR table slot.
This wastes a scarce FDIR entry and can cause confusing behaviour
when deleting rules.
Add a helper ixgbe_match_ethtool_fdir_entry() that walks the in-kernel
filter list before programming hardware. If an entry with an
identical filter (excluding the sw_idx) already exists, the new add
request is rejected with -EEXIST.
Signed-off-by: Piotr Skajewski <piotrx.skajewski@intel.com>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
.../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 27 +++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index ba049b3..a2009df 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -2938,6 +2938,23 @@ static int ixgbe_flowspec_to_flow_type(struct ethtool_rx_flow_spec *fsp,
return 1;
}
+static bool ixgbe_match_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
+ struct ixgbe_fdir_filter *input)
+{
+ struct ixgbe_fdir_filter *rule = NULL;
+
+ hlist_for_each_entry(rule, &adapter->fdir_filter_list, fdir_node) {
+ if (rule->sw_idx == input->sw_idx)
+ continue;
+ if (!memcmp(&rule->filter, &input->filter,
+ sizeof(rule->filter))) {
+ e_warn(drv, "FDIR filter already exists\n");
+ return true;
+ }
+ }
+ return false;
+}
+
static int ixgbe_add_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
struct ethtool_rxnfc *cmd)
{
@@ -2947,7 +2964,7 @@ static int ixgbe_add_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
struct ixgbe_fdir_filter *input;
union ixgbe_atr_input mask;
u8 queue;
- int err;
+ int err = -EINVAL;
if (!(adapter->flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE))
return -EOPNOTSUPP;
@@ -3050,6 +3067,12 @@ static int ixgbe_add_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
/* apply mask and compute/store hash */
ixgbe_atr_compute_perfect_hash_82599(&input->filter, &mask);
+ /* check for a duplicate filter */
+ if (ixgbe_match_ethtool_fdir_entry(adapter, input)) {
+ err = -EEXIST;
+ goto err_out_w_lock;
+ }
+
/* program filters to filter memory */
err = ixgbe_fdir_write_perfect_filter_82599(hw,
&input->filter, input->sw_idx, queue);
@@ -3065,7 +3088,7 @@ static int ixgbe_add_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
spin_unlock(&adapter->fdir_perfect_lock);
err_out:
kfree(input);
- return -EINVAL;
+ return err;
}
static int ixgbe_del_ethtool_fdir_entry(struct ixgbe_adapter *adapter,
--
2.52.0
^ permalink raw reply related
* [PATCH iwl-next 2/8] ixgbe: use int instead of u32 for error code variables
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov
Cc: netdev, Simon Horman
In-Reply-To: <20260508031226.3601800-1-aleksandr.loktionov@intel.com>
The variables used to store return values of kernel and driver functions
throughout the ixgbe driver are declared as u32 in several places. Such
functions return negative errno values on error (e.g. -EIO, -EFAULT),
which are sign-extended negative integers. Storing them in an unsigned
u32 silently wraps the value: -EIO (0xFFFFFFF7) stored in u32 becomes a
large positive number, so any "if (status)" truthiness check still works
by accident, but comparisons against specific negative error codes or
propagation up the call stack produce wrong results.
In the Linux kernel, u32 is reserved for fixed-width quantities used in
hardware interfaces or protocol structures. Using it for generic error
codes misleads reviewers into thinking the value is hardware-constrained.
Change all such local variables from u32 to int driver-wide: one in
ixgbe_main.c (ixgbe_resume), three in ixgbe_phy.c
(ixgbe_identify_phy_generic, ixgbe_tn_check_overtemp,
ixgbe_set_copper_phy_power), and six in ixgbe_x550.c
(ixgbe_check_link_t_X550em, ixgbe_get_lasi_ext_t_x550em,
ixgbe_enable_lasi_ext_t_x550em, ixgbe_handle_lasi_ext_t_x550em,
ixgbe_ext_phy_t_x550em_get_link, ixgbe_setup_internal_phy_t_x550em).
No functional change.
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +-
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 6 +++---
drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 12 ++++++------
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 65426a1..b6308c1 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7528,7 +7528,7 @@ static int ixgbe_resume(struct device *dev_d)
struct pci_dev *pdev = to_pci_dev(dev_d);
struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
struct net_device *netdev = adapter->netdev;
- u32 err;
+ int err;
adapter->hw.hw_addr = adapter->io_addr;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
index ab733e7..de8f6c6 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -262,7 +262,7 @@ static bool ixgbe_probe_phy(struct ixgbe_hw *hw, u16 phy_addr)
**/
int ixgbe_identify_phy_generic(struct ixgbe_hw *hw)
{
- u32 status = -EFAULT;
+ int status = -EFAULT;
u32 phy_addr;
if (!hw->phy.phy_semaphore_mask) {
@@ -2811,7 +2811,7 @@ static void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw)
bool ixgbe_tn_check_overtemp(struct ixgbe_hw *hw)
{
u16 phy_data = 0;
- u32 status;
+ int status;
if (hw->device_id != IXGBE_DEV_ID_82599_T3_LOM)
return false;
@@ -2831,7 +2831,7 @@ bool ixgbe_tn_check_overtemp(struct ixgbe_hw *hw)
**/
int ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on)
{
- u32 status;
+ int status;
u16 reg;
/* Bail if we don't have copper phy */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
index 76d2fa3..9b14f3b 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
@@ -1911,7 +1911,7 @@ static int ixgbe_check_link_t_X550em(struct ixgbe_hw *hw,
bool *link_up,
bool link_up_wait_to_complete)
{
- u32 status;
+ int status;
u16 i, autoneg_status;
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
@@ -2330,7 +2330,7 @@ static int ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw,
static int ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc,
bool *is_overtemp)
{
- u32 status;
+ int status;
u16 reg;
*is_overtemp = false;
@@ -2418,7 +2418,7 @@ static int ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc,
static int ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw)
{
bool lsc, overtemp;
- u32 status;
+ int status;
u16 reg;
/* Clear interrupt flags */
@@ -2512,7 +2512,7 @@ static int ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw,
{
struct ixgbe_phy_info *phy = &hw->phy;
bool lsc;
- u32 status;
+ int status;
status = ixgbe_get_lasi_ext_t_x550em(hw, &lsc, is_overtemp);
if (status)
@@ -2606,7 +2606,7 @@ static int ixgbe_setup_kr_x550em(struct ixgbe_hw *hw)
**/
static int ixgbe_ext_phy_t_x550em_get_link(struct ixgbe_hw *hw, bool *link_up)
{
- u32 ret;
+ int ret;
u16 autoneg_status;
*link_up = false;
@@ -2642,7 +2642,7 @@ static int ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw)
{
ixgbe_link_speed force_speed;
bool link_up;
- u32 status;
+ int status;
u16 speed;
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
--
2.52.0
^ permalink raw reply related
* [PATCH iwl-next 1/8] ixgbe: rename numa_node to node in struct ixgbe_q_vector
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov
Cc: netdev, Jacob Keller
In-Reply-To: <20260508031226.3601800-1-aleksandr.loktionov@intel.com>
From: Jacob Keller <jacob.e.keller@intel.com>
The 'numa_node' field in struct ixgbe_q_vector shadows the 'numa_node'
accessor for struct device, which triggers a sparse warning about
shadowing a built-in object. The stored value here is a plain NUMA
node number, not a device attribute, so rename it to 'node' to avoid
the shadow and keep the naming consistent with other Intel drivers.
Update all three usage sites in ixgbe_lib.c and ixgbe_main.c.
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 +-
drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c | 2 +-
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index 9b82175..cf2df18 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -515,7 +515,7 @@ struct ixgbe_q_vector {
struct rcu_head rcu; /* to avoid race with update stats on free */
cpumask_t affinity_mask;
- int numa_node;
+ int node;
char name[IFNAMSIZ + 9];
/* for dynamic allocation of rings associated with this q_vector */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
index 1db4bd5..5a4f05d 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
@@ -864,7 +864,7 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
/* setup affinity mask and node */
if (cpu != -1)
cpumask_set_cpu(cpu, &q_vector->affinity_mask);
- q_vector->numa_node = node;
+ q_vector->node = node;
#ifdef CONFIG_IXGBE_DCA
/* initialize CPU for DCA */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 2646ee6..65426a1 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7074,7 +7074,7 @@ int ixgbe_setup_tx_resources(struct ixgbe_ring *tx_ring)
size = sizeof(struct ixgbe_tx_buffer) * tx_ring->count;
if (tx_ring->q_vector)
- ring_node = tx_ring->q_vector->numa_node;
+ ring_node = tx_ring->q_vector->node;
tx_ring->tx_buffer_info = vmalloc_node(size, ring_node);
if (!tx_ring->tx_buffer_info)
@@ -7175,7 +7175,7 @@ int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
size = sizeof(struct ixgbe_rx_buffer) * rx_ring->count;
if (rx_ring->q_vector)
- ring_node = rx_ring->q_vector->numa_node;
+ ring_node = rx_ring->q_vector->node;
rx_ring->rx_buffer_info = vmalloc_node(size, ring_node);
if (!rx_ring->rx_buffer_info)
--
2.52.0
^ permalink raw reply related
* [PATCH iwl-next 0/8] ixgbe: small cleanups and improvements
From: Aleksandr Loktionov @ 2026-05-08 3:12 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov; +Cc: netdev
Eight independent cleanups and improvements for the ixgbe driver,
grouped by theme:
Naming / type hygiene (patches 1-2):
Patch 1 renames ixgbe_q_vector::numa_node to ::node. The field name
shadows the struct device numa_node accessor, causing a sparse warning.
The rename aligns with other Intel drivers (ice, igc).
Patch 2 changes local error-code variables from u32 to int in six
functions across ixgbe_main.c, ixgbe_phy.c and ixgbe_x550.c. Storing
a signed errno in an unsigned type works by accident for truthiness
checks but breaks exact comparisons and proper propagation.
Bug fix (patch 3):
Patch 3 rejects duplicate FDIR perfect-filter rules before programming
hardware. The same 5-tuple with a different sw_idx would silently
consume a second scarce FDIR slot and confuse rule deletion.
Returns -EEXIST to userspace on duplicate rather than -EINVAL.
Performance (patch 4):
Patch 4 replaces the busy-wait udelay(1000) in the SECRX_RDY poll
loop with usleep_range(10, 20) and raises the iteration count from
40 to 4000. Because usleep_range(min, max) is guaranteed to sleep
at least 'min' us, 4000 * 10 us preserves the original 40 ms
minimum-wait-before-timeout; worst-case grows to ~80 ms (acceptable
since SECRX_RDY failing to assert is non-fatal and only logged).
Typical stall on fast hardware drops from up to ~1 ms per iteration
to ~10-20 us.
Code quality (patches 5-8):
Patch 5 replaces ktime_to_ns(ktime_get_real()) with the direct
ktime_get_real_ns() helper.
Patch 6 factors the three-line autoneg-restart sequence duplicated in
ixgbe_setup_phy_link_generic() and ixgbe_setup_phy_link_tnx() into a
static helper ixgbe_restart_auto_neg(). The helper checks the return
value of read_reg() and returns early on error to avoid writing
uninitialized data to the PHY register.
Patches 7-8 improve the adaptive-ITR algorithm in two steps:
7/8: Limit ITR decrease in latency mode to at most
IXGBE_ITR_ADAPTIVE_MIN_INC (2 us) per step so that ACK-driven
workloads do not overdrive interrupt rate. Uses max_t() to
ensure the clamp never drives ITR below the algorithm's own
computation.
8/8: Add IXGBE_ITR_ADAPTIVE_MASK_USECS (= IXGBE_ITR_ADAPTIVE_LATENCY
- 1) to replace the open-coded ~IXGBE_ITR_ADAPTIVE_LATENCY
mask in ixgbe_set_itr() with the cleaner AND form.
Aleksandr Loktionov (1):
ixgbe: use int instead of u32 for error code variables
Alexander Duyck (2):
ixgbe: limit ITR decrease in latency mode to prevent ACK overdrive
ixgbe: add IXGBE_ITR_ADAPTIVE_MASK_USECS constant
Jacob Keller (2):
ixgbe: rename numa_node to node in struct ixgbe_q_vector
ixgbe: use ktime_get_real_ns() in ixgbe_ptp_reset()
Jakub Chylkowski (1):
ixgbe: extract ixgbe_restart_auto_neg() to avoid code duplication
Maciej Rabeda (1):
ixgbe: increase SECRX_RDY polling frequency in
ixgbe_disable_rx_buff_generic
Piotr Skajewski (1):
ixgbe: prevent adding duplicate FDIR perfect filter rules
drivers/net/ethernet/intel/ixgbe/ixgbe.h | 3 +-
.../net/ethernet/intel/ixgbe/ixgbe_common.c | 5 +--
.../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 27 +++++++++++-
drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c | 2 +-
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 19 ++++++++--
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 42 +++++++++++--------
drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 2 +-
drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 12 +++---
8 files changed, 77 insertions(+), 35 deletions(-)
--
2.52.0
^ permalink raw reply
* Re: [PATCH net-next v3 1/4] net: rnpgbe: Add interrupt handling
From: Yibo Dong @ 2026-05-08 3:06 UTC (permalink / raw)
To: Vadim Fedorenko
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, danishanwar, horms,
linux-kernel, netdev, yaojun
In-Reply-To: <5e9b15c7-5db4-408e-a5bc-75fe5752ed74@linux.dev>
On Thu, May 07, 2026 at 01:30:55PM +0100, Vadim Fedorenko wrote:
> On 07/05/2026 09:15, Dong Yibo wrote:
> > Add comprehensive interrupt handling for the RNPGBE driver:
> > - Implement msi-x/msi interrupt configuration and management
> > - Create library functions for interrupt registration and cleanup
> >
> > This infrastructure enables proper interrupt handling for the
> > RNPGBE driver.
> >
>
> [...]
>
> > +/**
> > + * rnpgbe_set_interrupt_capability - Set MSI-X or MSI if supported
> > + * @mucse: pointer to private structure
> > + *
> > + * Attempt to configure the interrupts using the best available
> > + * capabilities of the hardware.
> > + *
> > + * @return: 0 on success, negative on failure
> > + **/
> > +static int rnpgbe_set_interrupt_capability(struct mucse *mucse)
> > +{
> > + int v_budget;
> > +
> > + v_budget = min_t(int, mucse->num_tx_queues, mucse->num_rx_queues);
> > + v_budget = min_t(int, v_budget, MAX_Q_VECTORS);
>
> these 2 lines can be simplified to
>
> min3(mucse->num_tx_queues, mucse->num_rx_queues, MAX_Q_VECTORS);
>
Got it, I will update this.
> > + v_budget = min_t(int, v_budget, num_online_cpus());
> > + /* add one vector for mbx */> + v_budget += 1;
> > + v_budget = pci_alloc_irq_vectors(mucse->pdev, 1, v_budget,
> > + PCI_IRQ_MSI | PCI_IRQ_MSIX);
> > + if (v_budget < 0)
> > + return v_budget;
> > +
> > + if (mucse->pdev->msix_enabled) {
> > + /* q_vector not include mbx */
> > + if (v_budget > 1) {
> > + mucse->flags |= M_FLAG_MSIX_EN;
> > + mucse->num_q_vectors = v_budget - 1;
> > + } else {
> > + mucse->flags |= M_FLAG_MSIX_SINGLE_EN;
> > + mucse->num_q_vectors = 1;
> > + }
> > + } else {
> > + /* msi use only 1 irq */
> > + mucse->num_q_vectors = 1;
> > + mucse->flags |= M_FLAG_MSI_EN;
> > + }
>
> How will it work in case it's only 1 vector allocated? AFAIU, you need at
> least 2 irq vectors - 1 for queue processing, another one for mbx.
>
Mbx and tx/rx irq share 1 irq. And in fact, Hw only support 1 msi irq.
Just like ixgbe_intr in intel drivers.
> > +
> > + return 0;
> > +}
>
Thanks for your feedback.
^ permalink raw reply
* Re: [PATCH 0/6] SUNRPC: Address remaining cache_check_rcu() UAF in cache content files
From: yangerkun @ 2026-05-08 3:08 UTC (permalink / raw)
To: Chuck Lever, Misbah Anjum N, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Trond Myklebust,
Anna Schumaker, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, yi.zhang
Cc: linux-nfs, linux-kernel, netdev, Chuck Lever
In-Reply-To: <4bb9ed6b-1a64-406a-9239-b0560ca963cc@huawei.com>
在 2026/5/8 10:45, yangerkun 写道:
> Hello Chuck,
>
> 在 2026/5/8 0:12, Chuck Lever 写道:
>> Hello Erkun -
>>
>> On Thu, May 7, 2026, at 11:09 AM, yangerkun wrote:
>>> Hi,
>>>
>>> 在 2026/5/1 22:51, Chuck Lever 写道:
>>>> Misbah Anjum reported a use-after-free in cache_check_rcu()
>>>> reached through e_show() while sosreport was reading
>>>> /proc/fs/nfsd/exports on ppc64le. Two fixes for that report
>>>> landed in v7.0:
>>>>
>>>> 48db892356d6 ("NFSD: Defer sub-object cleanup in export put
>>>> callbacks")
>>>> e7fcf179b82d ("NFSD: Hold net reference for the lifetime of /
>>>> proc/fs/nfs/exports fd")
>>>
>>> Back to the problem fixed by this patches, I'm a little confused why
>>> this UAF can be trigged.
>>>
>>> Before this patches, svc_export_put show as follow:
>>>
>>> 368 static void svc_export_put(struct kref *ref)
>>> 369 {
>>> 370 struct svc_export *exp = container_of(ref, struct
>>> svc_export, h.ref);
>>> 371
>>> 372 path_put(&exp->ex_path);
>>> 373 auth_domain_put(exp->ex_client);
>>> 374 call_rcu(&exp->ex_rcu, svc_export_release);
>>> 375 }
>>>
>>> The auth_domain_put function releases ->name using call_rcu, and
>>> path_put may release the dentry also via call_rcu. All of this seems to
>>> prevent e_show from causing a UAF. Could you point out which line in
>>> d_path triggers the issue?
>>
>> The dentry, the mount, and the auth_domain ->name buffer all
>> end up RCU-freed (dentry_free() and delayed_free_vfsmnt in
>> fs/, svcauth_unix_domain_release_rcu() in svcauth_unix.c).
>> The eventual kfree isn't the problem.
>>
>> The problem is the synchronous teardown inside path_put(),
>> which runs before svc_export_put() ever reaches its own
>> call_rcu():
>>
>> path_put(&exp->ex_path)
>> -> dput(dentry)
>> -> __dentry_kill() [if last ref]
>> -> __d_drop() /* unhashes */
>> -> dentry_unlink_inode() /* d_inode = NULL */
>> -> d_op->d_release() if set
>> -> drops parent d_lockref /* may cascade up */
>> -> dentry_free() /* call_rcu deferred */
>> -> mntput(mnt) /* deferred via task_work */
>>
>> The dentry pointer itself is RCU-safe, so prepend_path()'s walk
>> of d_parent and d_name doesn't read freed memory. But by the
>> time the reader gets there, __d_clear_type_and_inode() has
>> already stored NULL into d_inode, __d_drop() has broken the
>> hash linkage, and the parent's d_lockref has been decremented
>> -- which can in turn fire __dentry_kill() on the parent, and
>> on up the tree. An e_show() that's still inside its cache RCU
>> read section walks into that half-dismantled state through
>> seq_path(), and that's the NULL deref Misbah reported.
>
> Thank you for your detailed explanation! Yes, e_show might be called
> when the state is partially dismantled, but after carefully reviewing
> the code with dput up to __dentry_kill, I still cannot find anything
> that could cause this issue. Additionally, the comments for prepend_path
> indicate that they have already taken into account that the dentry can
> be removed concurrently. I have also run some tests on my arm64 QEMU,
> but I couldn't reproduce the problem either. Could you please help me
> identify the specific line or pointer in the dentry that triggers this
> use-after-free or null pointer issue?
>
> Maybe I am not be very familiar with the code, which caused me to fail
> to identify the real root cause. I'm so sorry for that.
>
>
> 265 char *d_path(const struct path *path, char *buf, int buflen)
> 266 {
> 267 DECLARE_BUFFER(b, buf, buflen);
> 268 struct path root;
> 269
> 270 /*
> 271 * We have various synthetic filesystems that never get
> mounted. On
> 272 * these filesystems dentries are never used for lookup
> purposes, and
> 273 * thus don't need to be hashed. They also don't need a
> name until a
> 274 * user wants to identify the object in /proc/pid/fd/. The
> little hack
> 275 * below allows us to generate a name for these objects on
> demand:
> 276 *
> 277 * Some pseudo inodes are mountable. When they are mounted
> 278 * path->dentry == path->mnt->mnt_root. In that case don't
> call d_dname
> 279 * and instead have d_path return the mounted path.
> 280 */
> 281 if (path->dentry->d_op && path->dentry->d_op->d_dname &&
> 282 (!IS_ROOT(path->dentry) || path->dentry != path->mnt-
> >mnt_root))
> 283 return path->dentry->d_op->d_dname(path->dentry,
> buf, buflen);
> 284
> 285 rcu_read_lock();
> 286 get_fs_root_rcu(current->fs, &root);
> 287 if (unlikely(d_unlinked(path->dentry)))
> 288 prepend(&b, " (deleted)", 11);
> 289 else
> 290 prepend_char(&b, 0);
> 291 prepend_path(path, &root, &b);
> 292 rcu_read_unlock();
> 293
> 294 return extract_string(&b);
> 295 }
>
>
>>
>> The earlier fix (2530766492ec, "nfsd: fix UAF when access
>> ex_uuid or ex_stats") moved the kfree of ex_uuid and ex_stats
>> into svc_export_release() so those are RCU-safe now.
>> path_put() and auth_domain_put() couldn't go in there because
>> both may sleep, and call_rcu callbacks run in softirq context.
>> This series uses queue_rcu_work() instead: it defers past the
>> grace period AND runs the callback in process context, so the
>> sleeping puts move into the deferred path and the window
>> closes.
>
> Yeah, I can get this! Thanks again for your detail explanation!
Also, could the scenario described in this commit be triggered again?
commit 69d803c40edeaf94089fbc8751c9b746cdc35044
Author: Yang Erkun <yangerkun@huawei.com>
Date: Mon Dec 16 22:21:52 2024 +0800
nfsd: Revert "nfsd: release svc_expkey/svc_export with rcu_work"
This reverts commit f8c989a0c89a75d30f899a7cabdc14d72522bb8d.
Before this commit, svc_export_put or expkey_put will call path_put
with
sync mode. After this commit, path_put will be called with async mode.
And this can lead the unexpected results show as follow.
mkfs.xfs -f /dev/sda
echo "/ *(rw,no_root_squash,fsid=0)" > /etc/exports
echo "/mnt *(rw,no_root_squash,fsid=1)" >> /etc/exports
exportfs -ra
service nfs-server start
mount -t nfs -o vers=4.0 127.0.0.1:/mnt /mnt1
mount /dev/sda /mnt/sda
touch /mnt1/sda/file
exportfs -r
umount /mnt/sda # failed unexcepted
The touch will finally call nfsd_cross_mnt, add refcount to mount, and
then add cache_head. Before this commit, exportfs -r will call
cache_flush to cleanup all cache_head, and path_put in
svc_export_put/expkey_put will be finished with sync mode. So, the
latter umount will always success. However, after this commit, path_put
will be called with async mode, the latter umount may failed, and if
we add some delay, umount will success too. Personally I think this bug
and should be fixed. We first revert before bugfix patch, and then fix
the original bug with a different way.
Fixes: f8c989a0c89a ("nfsd: release svc_expkey/svc_export with
rcu_work")
Signed-off-by: Yang Erkun <yangerkun@huawei.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>
> Thanks,
> Erkun.
>
>>
>>
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox