DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v5 0/2] Update Rx Timestamp in IAVF PMD
From: Bruce Richardson @ 2026-05-20 14:52 UTC (permalink / raw)
  To: Soumyadeep Hore; +Cc: manoj.kumar.subbarao, aman.deep.singh, dev
In-Reply-To: <20260520184350.81934-1-soumyadeep.hore@intel.com>

On Wed, May 20, 2026 at 02:43:48PM -0400, Soumyadeep Hore wrote:
> PHC Polling from Rx Datapath is removed and existing alarm handlers are
> used to fix latency issues in IAVF PMD.
> ---
Series-acked-by: Bruce Richardson <bruce.richardson@intel.com>

Applied to next-net-intel.
Thanks,
/Bruce

^ permalink raw reply

* RE: [RFC PATCH 0/8] remove use of rte_memcpy from net/intel
From: Morten Brørup @ 2026-05-20 13:50 UTC (permalink / raw)
  To: Bruce Richardson, Stephen Hemminger; +Cc: dev
In-Reply-To: <ag1me_1XSByq3p9V@bricha3-mobl1.ger.corp.intel.com>

> From: Bruce Richardson [mailto:bruce.richardson@intel.com]
> Sent: Wednesday, 20 May 2026 09.45
PATCH 0/8] remove use of rte_memcpy from net/intel
> 
> On Tue, May 19, 2026 at 12:43:32PM -0700, Stephen Hemminger wrote:
> > On Tue, 19 May 2026 16:05:57 +0000
> > Bruce Richardson <bruce.richardson@intel.com> wrote:
> >
> > > This RFC proposed to replace all instances of rte_memcpy in Intel
> > > (and former-Intel) net drivers with just regular memcpy. This is
> > > done on the basis that the memcpy use is not datapath, but is used
> > > for flow configuration, virt-channel (to firmware or PF) messaging
> > > and other control path functions.
> >
> > Makes sense. You might also want to look for where structure
> > assignment can be used instead of memcpy. Keeping data types
> > is a good thing.
> 
> Yes, it would be nice to use in places. However, it's not a mechanical
> change so I've not taken the time to do the analysis. Just a quick set
> of sed replacements for this RFC.

If you consider structure assignment, look out for fixed-size structures ending with field[1]; they are really (ill defined) flex-arrays.


^ permalink raw reply

* RE: [PATCH v2 2/2] ethdev: add telemetry endpoint for list names
From: Morten Brørup @ 2026-05-20 13:29 UTC (permalink / raw)
  To: Chengwen Feng, thomas, stephen; +Cc: dev, andrew.rybchenko, bruce.richardson
In-Reply-To: <20260520093804.29102-3-fengchengwen@huawei.com>

> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
> Sent: Wednesday, 20 May 2026 11.38
> 
> Add /ethdev/list_names telemetry endpoint which returns a dictionary
> keyed by port ID with device name as the value, so users can
> identify ports by name directly from the telemetry output.
> 
> Original /ethdev/list output:
>   {"/ethdev/list": [0, 1]}
> 
> New /ethdev/list_names output:
>   {"/ethdev/list_names": {"0": "0000:7d:00.0",
>   "1": "0000:7d:00.1"}}
> 

<rant>

Unfortunately, the telemetry protocol in DPDK is not using a common design, but takes parameters specific to each path.
It should have used OData or something similar, to standardize listing, filtering, etc.
Then we could have queried this like:
/ethdev/info?$select=port_id,name
And return something like:
[
	{
		"port_id": 0,
		"name": "0000:7d:00.0"
	},
	{
		"port_id": 1,
		"name": "0000:7d:00.1"
	}
]
or:
[
	{
		0,
		"0000:7d:00.0"
	},
	{
		1,
		"0000:7d:00.1"
	}
]

But now we are stuck with what we have.

</rant>

So /etdev/list_names is OK.

I'm not really familiar with the DPDK telemetry, so I wonder if indexed arrays are normally returned as an object, like in this patch?

I would have expected a list function (such as list_names) to return an array.
Either a simple list:
{
	"/ethdev/list_names":
	[
		"0000:7d:00.0",
		"0000:7d:00.1"
	]
}

Or a list of objects:
{
	"/ethdev/list_names":
	[
		{
			"port_id": 0,
			"name": "0000:7d:00.0"
		},
		{
			"port_id": 1,
			"name": "0000:7d:00.1"
		}
	]
}


^ permalink raw reply

* [PATCH] net/mlx5: remove nonsensical flow action class_id checks
From: Adrian Schollmeyer @ 2026-05-20 13:25 UTC (permalink / raw)
  To: Dariusz Sosnowski, Viacheslav Ovsiienko, Bing Zhao, Ori Kam,
	Suanming Mou, Matan Azrad, Michael Baum
  Cc: dev, Michael Pfeiffer, stable, Adrian Schollmeyer

From: Michael Pfeiffer <m.pfeiffer@syseleven.de>

For a MODIFY_FIELD action, flow_hw_validate_action_modify_field() is
invoked and enforces class_id == 0 in the action's source and
destination, if the modified field is none of
RTE_FLOW_FIELD_GENEVE_OPT_*, as the value is used solely for GENEVE
fields.

However, this check is flawed due to the way rte_flow_field_data is
initialized. As it consists of unions and anonymous structs as members,
empty initialization of this struct or initializing just the tag_index
only guarantees initialization of the first union member, while the
remaining member's default initialization behavior is unspecified.
Therefore, depending on the compiler type, version and configuration,
the remaining members may either be default-initialized as well or
contain bytes from uninitialized memory. This causes the check to fail
depending on how the struct is initialized wherever it is used.

For example, rte_flow_configure() sometimes fails on mlx5 under these
circumstances with an error "destination class id is not supported"
during creation of representor tagging rules, as these internally use
MODIFY_FIELD actions in the following call stack:

  1. rte_flow_configure
  2. mlx5_flow_port_configure
  3. flow_hw_configure
  4. __flow_hw_configure
  5. flow_hw_setup_tx_repr_tagging
  6. flow_hw_create_tx_repr_tag_jump_acts_tmpl
     --> various rte_flow_action_modify_field are initialized here, but
         class_id remains uninitialized
  7. __flow_hw_actions_template_create
  8. mlx5_flow_hw_actions_validate
  9. flow_hw_validate_action_modify_field
     --> invoked with class_id containing uninitialized bytes and
         non-GENEVE field type

Remove the two checks for class_id in the non-GENEVE case, as this field
is unused for these actions and avoids additional implicit dependencies
on the correct ordering of union members.

Fixes: 1caa89ec1891 ("net/mlx5: support GENEVE options modification")
Cc: stable@dpdk.org

Signed-off-by: Michael Pfeiffer <m.pfeiffer@syseleven.de>
Signed-off-by: Adrian Schollmeyer <a.schollmeyer@syseleven.de>
---
 drivers/net/mlx5/mlx5_flow_hw.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/drivers/net/mlx5/mlx5_flow_hw.c b/drivers/net/mlx5/mlx5_flow_hw.c
index bca5b2769e..add48d7c8f 100644
--- a/drivers/net/mlx5/mlx5_flow_hw.c
+++ b/drivers/net/mlx5/mlx5_flow_hw.c
@@ -6264,10 +6264,6 @@ flow_hw_validate_action_modify_field(struct rte_eth_dev *dev,
 			return rte_flow_error_set(error, EINVAL,
 					RTE_FLOW_ERROR_TYPE_ACTION, action,
 					"destination tag index is not supported");
-		if (action_conf->dst.class_id)
-			return rte_flow_error_set(error, EINVAL,
-					RTE_FLOW_ERROR_TYPE_ACTION, action,
-					"destination class id is not supported");
 	}
 	if (mask_conf->dst.level != UINT8_MAX)
 		return rte_flow_error_set(error, EINVAL,
@@ -6290,10 +6286,6 @@ flow_hw_validate_action_modify_field(struct rte_eth_dev *dev,
 				return rte_flow_error_set(error, EINVAL,
 					RTE_FLOW_ERROR_TYPE_ACTION, action,
 					"source tag index is not supported");
-			if (action_conf->src.class_id)
-				return rte_flow_error_set(error, EINVAL,
-					RTE_FLOW_ERROR_TYPE_ACTION, action,
-					"source class id is not supported");
 		}
 		if (mask_conf->src.level != UINT8_MAX)
 			return rte_flow_error_set(error, EINVAL,
-- 
2.34.1


^ permalink raw reply related

* [PATCH] linux/mem: atomically prefault hugepages in alloc_seg
From: Michal Sieron @ 2026-05-20 12:57 UTC (permalink / raw)
  To: dev; +Cc: Michal Sieron

In rare cases, when a secondary process calls rte_eal_init() it can
cause a data race during page prefaulting in alloc_seg().

An atomic compare-exchange in a loop should eliminate the data race.

Signed-off-by: Michal Sieron <michal.sieron@nokia.com>
---
 lib/eal/linux/eal_memalloc.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/lib/eal/linux/eal_memalloc.c b/lib/eal/linux/eal_memalloc.c
index a39bc31c7b..cb92fda2e8 100644
--- a/lib/eal/linux/eal_memalloc.c
+++ b/lib/eal/linux/eal_memalloc.c
@@ -30,6 +30,7 @@
 #include <rte_eal.h>
 #include <rte_memory.h>
 #include <rte_cycles.h>
+#include <rte_atomic.h>
 
 #include "eal_filesystem.h"
 #include "eal_internal_cfg.h"
@@ -600,7 +601,9 @@ alloc_seg(struct rte_memseg *ms, void *addr, int socket_id,
 	 * that is already there, so read the old value, and write itback.
 	 * kernel populates the page with zeroes initially.
 	 */
-	*(volatile int *)addr = *(volatile int *)addr;
+	int snapshot = *(volatile int *)addr;
+	while (!rte_atomic_compare_exchange_strong((volatile int *)addr, &snapshot, snapshot))
+		;
 
 	iova = rte_mem_virt2iova(addr);
 	if (iova == RTE_BAD_PHYS_ADDR) {
-- 
2.43.0


^ permalink raw reply related

* [PATCH 10/10] net/e1000/base: update version info
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

Update the last shared code update date.

Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/README | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/intel/e1000/base/README b/drivers/net/intel/e1000/base/README
index 976391b170..f0c1e4af48 100644
--- a/drivers/net/intel/e1000/base/README
+++ b/drivers/net/intel/e1000/base/README
@@ -1,9 +1,9 @@
 /* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2010-2025 Intel Corporation
+ * Copyright(c) 2010-2026 Intel Corporation
  */
 
 This directory contains source code of the base driver code for em/igb/igc,
-with this snapshot generated on 2025-06-04-14.40.
+with this snapshot generated on 2026-05-07-13.50.
 
 This driver is valid for the product(s) listed below
 * Intel® Ethernet Controller 82540
-- 
2.43.0


^ permalink raw reply related

* [PATCH 09/10] net/e1000/base: auto-negotiation status for 1000BASE-T
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Mateusz Fryze, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Mateusz Fryze <mateusz.fryze@intel.com>

Add functionality to find auto-negotiation status on 1000BASE-T PHYs
based on specific PHY registers.

Signed-off-by: Mateusz Fryze <mateusz.fryze@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 .mailmap                                     |  1 +
 drivers/net/intel/e1000/base/e1000_82575.c   |  1 +
 drivers/net/intel/e1000/base/e1000_api.c     | 16 ++++
 drivers/net/intel/e1000/base/e1000_api.h     |  1 +
 drivers/net/intel/e1000/base/e1000_defines.h | 22 ++++++
 drivers/net/intel/e1000/base/e1000_hw.h      |  9 +++
 drivers/net/intel/e1000/base/e1000_phy.c     | 81 ++++++++++++++++++++
 drivers/net/intel/e1000/base/e1000_phy.h     |  2 +
 8 files changed, 133 insertions(+)

diff --git a/.mailmap b/.mailmap
index f3130df686..4a211b1211 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1033,6 +1033,7 @@ Masoud Hasanifard <masoudhasanifard@gmail.com>
 Masoumeh Farhadi Nia <masoumeh.farhadinia@gmail.com>
 Matan Azrad <matan@nvidia.com> <matan@mellanox.com>
 Matej Vido <matejvido@gmail.com> <vido@cesnet.cz>
+Mateusz Fryze <mateusz.fryze@intel.com>
 Mateusz Kowalski <mateusz.kowalski@intel.com>
 Mateusz Pacuszka <mateuszx.pacuszka@intel.com>
 Mateusz Polchlopek <mateusz.polchlopek@intel.com>
diff --git a/drivers/net/intel/e1000/base/e1000_82575.c b/drivers/net/intel/e1000/base/e1000_82575.c
index 8ae2b77d5f..a2b563975e 100644
--- a/drivers/net/intel/e1000/base/e1000_82575.c
+++ b/drivers/net/intel/e1000/base/e1000_82575.c
@@ -233,6 +233,7 @@ STATIC s32 e1000_init_phy_params_82575(struct e1000_hw *hw)
 		phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82580;
 		phy->ops.force_speed_duplex =
 				e1000_phy_force_speed_duplex_82577;
+		phy->ops.get_an_status = e1000_1gbase_t_autoneg_status;
 		break;
 	case I210_I_PHY_ID:
 		phy->type		= e1000_phy_i210;
diff --git a/drivers/net/intel/e1000/base/e1000_api.c b/drivers/net/intel/e1000/base/e1000_api.c
index a7877613e3..83b3d6c439 100644
--- a/drivers/net/intel/e1000/base/e1000_api.c
+++ b/drivers/net/intel/e1000/base/e1000_api.c
@@ -1138,6 +1138,22 @@ s32 e1000_get_phy_info(struct e1000_hw *hw)
 	return E1000_SUCCESS;
 }
 
+/**
+ *  e1000_get_an_status - Finds Auto-negotiation status based on PHY registers
+ *  @hw: pointer to the HW structure
+ *  @an_status: AN status
+ *
+ *  This function gets information from the PHY specific registers to determine
+ *  Auto-negotiation status.
+ **/
+s32 e1000_get_an_status(struct e1000_hw *hw, u8 *an_status)
+{
+	if (hw->phy.ops.get_an_status)
+		return hw->phy.ops.get_an_status(hw, an_status);
+
+	return E1000_SUCCESS;
+}
+
 /**
  *  e1000_phy_hw_reset - Hard PHY reset
  *  @hw: pointer to the HW structure
diff --git a/drivers/net/intel/e1000/base/e1000_api.h b/drivers/net/intel/e1000/base/e1000_api.h
index ca3248c214..1f81f49bd5 100644
--- a/drivers/net/intel/e1000/base/e1000_api.h
+++ b/drivers/net/intel/e1000/base/e1000_api.h
@@ -59,6 +59,7 @@ s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data);
 s32 e1000_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg, u32 offset,
 			      u8 data);
 s32 e1000_get_phy_info(struct e1000_hw *hw);
+s32 e1000_get_an_status(struct e1000_hw *hw, u8 *an_status);
 void e1000_release_phy(struct e1000_hw *hw);
 s32 e1000_acquire_phy(struct e1000_hw *hw);
 s32 e1000_cfg_on_link_up(struct e1000_hw *hw);
diff --git a/drivers/net/intel/e1000/base/e1000_defines.h b/drivers/net/intel/e1000/base/e1000_defines.h
index 6c710300a6..ba12dde770 100644
--- a/drivers/net/intel/e1000/base/e1000_defines.h
+++ b/drivers/net/intel/e1000/base/e1000_defines.h
@@ -1103,6 +1103,28 @@
 #define PHY_1000T_STATUS	0x0A /* 1000Base-T Status Reg */
 #define PHY_EXT_STATUS		0x0F /* Extended Status Reg */
 
+/* PHY Status Register */
+#define PHY_STATUS_LINK_STATUS_SHIFT		2
+#define PHY_STATUS_AN_ABILITY_SHIFT			3
+#define PHY_STATUS_REMOTE_FAULT_SHIFT		4
+#define PHY_STATUS_AN_COMPLETE_SHIFT		5
+#define PHY_STATUS_LINK_STATUS_MASK			(0x01 << PHY_STATUS_LINK_STATUS_SHIFT)
+#define PHY_STATUS_AN_ABILITY_MASK			(0x01 << PHY_STATUS_AN_ABILITY_SHIFT)
+#define PHY_STATUS_REMOTE_FAULT_MASK		(0x01 << PHY_STATUS_REMOTE_FAULT_SHIFT)
+#define PHY_STATUS_AN_COMPLETE_MASK			(0x01 << PHY_STATUS_AN_COMPLETE_SHIFT)
+
+/* Auto negotiation Expansion Register */
+#define PHY_AUTONEG_EXP_LP_AN_ABLE_SHIFT			0
+#define PHY_AUTONEG_EXP_PAGE_RECVD_SHIFT			1
+#define PHY_AUTONEG_EXP_NEXT_PAGE_ABLE_SHIFT		2
+#define PHY_AUTONEG_EXP_LP_NEXT_PAGE_ABLE_SHIFT		3
+#define PHY_AUTONEG_EXP_PARALLEL_DETECT_FLT_SHIFT	4
+#define PHY_AUTONEG_EXP_LP_AN_ABLE_MASK			(0x01 << PHY_AUTONEG_EXP_LP_AN_ABLE_SHIFT)
+#define PHY_AUTONEG_EXP_PAGE_RECVD_MASK			(0x01 << PHY_AUTONEG_EXP_PAGE_RECVD_SHIFT)
+#define PHY_AUTONEG_EXP_NEXT_PAGE_ABLE_MASK	(0x01 << PHY_AUTONEG_EXP_NEXT_PAGE_ABLE_SHIFT)
+#define PHY_AUTONEG_EXP_LP_NEXT_PAGE_ABLE_MASK	(0x01 << PHY_AUTONEG_EXP_LP_NEXT_PAGE_ABLE_SHIFT)
+#define PHY_AUTONEG_EXP_PARALLEL_DETECT_FLT_MASK (0x01 << PHY_AUTONEG_EXP_PARALLEL_DETECT_FLT_SHIFT)
+
 /* PHY GPY 211 registers */
 #define STANDARD_AN_REG_MASK	0x0007 /* MMD */
 #define ANEG_MULTIGBT_AN_CTRL	0x0020 /* MULTI GBT AN Control Register */
diff --git a/drivers/net/intel/e1000/base/e1000_hw.h b/drivers/net/intel/e1000/base/e1000_hw.h
index 9b1fafd75c..99c2195916 100644
--- a/drivers/net/intel/e1000/base/e1000_hw.h
+++ b/drivers/net/intel/e1000/base/e1000_hw.h
@@ -409,6 +409,14 @@ enum e1000_serdes_link_state {
 	e1000_serdes_link_forced_up
 };
 
+enum e1000_autoneg_status {
+	e1000_an_off = 0,	/* No conn.; AN unsupported, disabled, or disabled on the LP */
+	e1000_an_failed,	/* Remote Fault or Parallel Detection Fault reported */
+	e1000_an_in_progress,
+	e1000_an_complete,
+	e1000_an_status_unavailable	/* AN status could not be obtained */
+};
+
 enum e1000_invm_structure_type {
 	e1000_invm_uninitialized_structure		= 0x00,
 	e1000_invm_word_autoload_structure		= 0x01,
@@ -803,6 +811,7 @@ struct e1000_phy_operations {
 	s32  (*write_reg_page)(struct e1000_hw *, u32, u16);
 	void (*power_up)(struct e1000_hw *);
 	void (*power_down)(struct e1000_hw *);
+	s32  (*get_an_status)(struct e1000_hw *, u8 *);
 	s32 (*read_i2c_byte)(struct e1000_hw *, u8, u8, u8 *);
 	s32 (*write_i2c_byte)(struct e1000_hw *, u8, u8, u8);
 };
diff --git a/drivers/net/intel/e1000/base/e1000_phy.c b/drivers/net/intel/e1000/base/e1000_phy.c
index 31ef5089ba..c66ae44e7a 100644
--- a/drivers/net/intel/e1000/base/e1000_phy.c
+++ b/drivers/net/intel/e1000/base/e1000_phy.c
@@ -63,6 +63,7 @@ void e1000_init_phy_ops_generic(struct e1000_hw *hw)
 	phy->ops.write_reg_page = e1000_null_write_reg;
 	phy->ops.power_up = e1000_null_phy_generic;
 	phy->ops.power_down = e1000_null_phy_generic;
+	phy->ops.get_an_status = e1000_null_an_status;
 	phy->ops.read_i2c_byte = e1000_read_i2c_byte_null;
 	phy->ops.write_i2c_byte = e1000_write_i2c_byte_null;
 	phy->ops.cfg_on_link_up = e1000_null_ops_generic;
@@ -133,6 +134,21 @@ s32 e1000_null_write_reg(struct e1000_hw E1000_UNUSEDARG *hw,
 	return E1000_SUCCESS;
 }
 
+/**
+ *  e1000_null_link_info - No-op function, return 0
+ *  @hw: pointer to the HW structure
+ *  @status: dummy variable
+ **/
+s32 e1000_null_an_status(struct e1000_hw E1000_UNUSEDARG *hw,
+			u8 *status)
+{
+	DEBUGFUNC("e1000_null_an_status");
+	UNREFERENCED_1PARAMETER(hw);
+	*status = e1000_an_status_unavailable;
+
+	return E1000_SUCCESS;
+}
+
 /**
  *  e1000_read_i2c_byte_null - No-op function, return 0
  *  @hw: pointer to hardware structure
@@ -2427,6 +2443,71 @@ STATIC s32 e1000_wait_autoneg(struct e1000_hw *hw)
 	return ret_val;
 }
 
+/**
+ *  e1000_1gbase_t_autoneg_status - Gets information on current AN status.
+ *  @hw: pointer to the HW structure
+ *  @an_status: pointer to the AN status
+ *
+ *  The function finds the Auto-negotiation status of 1000BASE-T PHY based on
+ *  the data from PHY Status (PSTATUS) and Auto–Negotiation Expansion (ANE)
+ *  PHY registers.
+ *
+ *  @note The function will report Auto-negotiation OFF when there is no
+ *  media connected to the port. When used during the PHY reset, it might not
+ *  report a valid status.
+ **/
+s32 e1000_1gbase_t_autoneg_status(struct e1000_hw *hw, u8 *an_status)
+{
+	s32 ret_val = E1000_SUCCESS;
+	u16 phy_reg = 0;
+
+	DEBUGFUNC("e1000_1gbase_t_autoneg_status\n");
+
+	do {
+		*an_status = e1000_an_status_unavailable;
+		ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_reg);
+		if (ret_val) {
+			DEBUGOUT1("Reading PHY STATUS register returned error: %X\n", ret_val);
+			break;
+		}
+
+		if (!(phy_reg & PHY_STATUS_AN_ABILITY_MASK)) {
+			*an_status = e1000_an_off;
+			break;
+		}
+
+		if (phy_reg & (PHY_STATUS_AN_COMPLETE_MASK | PHY_STATUS_LINK_STATUS_MASK)) {
+			*an_status = e1000_an_complete;
+			break;
+		}
+
+		if (phy_reg & PHY_STATUS_REMOTE_FAULT_MASK) {
+			*an_status = e1000_an_failed;
+			break;
+		}
+
+		ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_reg);
+		if (ret_val) {
+			DEBUGOUT1("Reading PHY ANE register returned error: %X\n", ret_val);
+			break;
+		}
+
+		if (!(phy_reg & PHY_AUTONEG_EXP_LP_AN_ABLE_MASK)) {
+			*an_status = e1000_an_off;
+			break;
+		}
+
+		if (phy_reg & PHY_AUTONEG_EXP_PARALLEL_DETECT_FLT_MASK) {
+			*an_status = e1000_an_failed;
+			break;
+		}
+
+		*an_status = e1000_an_in_progress;
+	} while (0);
+
+	return ret_val;
+}
+
 /**
  *  e1000_phy_has_link_generic - Polls PHY for link
  *  @hw: pointer to the HW structure
diff --git a/drivers/net/intel/e1000/base/e1000_phy.h b/drivers/net/intel/e1000/base/e1000_phy.h
index 4c0b93c18f..d8fcc7ae10 100644
--- a/drivers/net/intel/e1000/base/e1000_phy.h
+++ b/drivers/net/intel/e1000/base/e1000_phy.h
@@ -11,6 +11,7 @@ void e1000_null_phy_generic(struct e1000_hw *hw);
 s32  e1000_null_lplu_state(struct e1000_hw *hw, bool active);
 s32  e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data);
 s32  e1000_null_set_page(struct e1000_hw *hw, u16 data);
+s32 e1000_null_an_status(struct e1000_hw *hw, u8 *status);
 s32 e1000_read_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset,
 			     u8 dev_addr, u8 *data);
 s32 e1000_write_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset,
@@ -22,6 +23,7 @@ s32  e1000_check_polarity_ife(struct e1000_hw *hw);
 s32  e1000_check_reset_block_generic(struct e1000_hw *hw);
 s32  e1000_phy_setup_autoneg(struct e1000_hw *hw);
 s32  e1000_copper_link_autoneg(struct e1000_hw *hw);
+s32  e1000_1gbase_t_autoneg_status(struct e1000_hw *hw, u8 *an_status);
 s32  e1000_copper_link_setup_igp(struct e1000_hw *hw);
 s32  e1000_copper_link_setup_m88(struct e1000_hw *hw);
 s32  e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw);
-- 
2.43.0


^ permalink raw reply related

* [PATCH 08/10] net/e1000/base: propagate PHY control register write error
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Dima Ruinskiy, stable, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Dima Ruinskiy <dima.ruinskiy@intel.com>

During HV PHY workaround handling, a failure writing to the PHY control
register was silently discarded rather than returned to the caller. Add
the missing early return so the error is correctly propagated.

Fixes: 5a32a257f957 ("e1000: more NICs in base driver")
Cc: stable@dpdk.org

Signed-off-by: Dima Ruinskiy <dima.ruinskiy@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/e1000_ich8lan.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/intel/e1000/base/e1000_ich8lan.c b/drivers/net/intel/e1000/base/e1000_ich8lan.c
index e62847fb4e..0290a7dcb4 100644
--- a/drivers/net/intel/e1000/base/e1000_ich8lan.c
+++ b/drivers/net/intel/e1000/base/e1000_ich8lan.c
@@ -2696,6 +2696,8 @@ STATIC s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
 			e1000_phy_sw_reset_generic(hw);
 			ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL,
 							0x3140);
+			if (ret_val)
+				return ret_val;
 		}
 	}
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 07/10] net/e1000/base: fix NVM loop bounds and pointer access
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Menachem Fogel, stable, Dima Ruinskiy, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Menachem Fogel <menachem.fogel@intel.com>

Improve the NVM checksum routines by ensuring loop bounds are compared
at the correct integer width. Use array indexing instead of explicit
pointer arithmetic.

Fixes: af75078fece3 ("first public release")
Cc: stable@dpdk.org

Signed-off-by: Menachem Fogel <menachem.fogel@intel.com>
Signed-off-by: Dima Ruinskiy <dima.ruinskiy@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/e1000_82575.c  | 4 ++--
 drivers/net/intel/e1000/base/e1000_manage.c | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/intel/e1000/base/e1000_82575.c b/drivers/net/intel/e1000/base/e1000_82575.c
index c263f42ca1..8ae2b77d5f 100644
--- a/drivers/net/intel/e1000/base/e1000_82575.c
+++ b/drivers/net/intel/e1000/base/e1000_82575.c
@@ -2395,7 +2395,7 @@ s32 e1000_validate_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset)
 
 	DEBUGFUNC("e1000_validate_nvm_checksum_with_offset");
 
-	for (i = offset; i < ((NVM_CHECKSUM_REG + offset) + 1); i++) {
+	for (i = offset; i < (u16)((NVM_CHECKSUM_REG + offset) + 1); i++) {
 		ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
 		if (ret_val) {
 			DEBUGOUT("NVM Read Error\n");
@@ -2432,7 +2432,7 @@ s32 e1000_update_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset)
 
 	DEBUGFUNC("e1000_update_nvm_checksum_with_offset");
 
-	for (i = offset; i < (NVM_CHECKSUM_REG + offset); i++) {
+	for (i = offset; i < (u16)(NVM_CHECKSUM_REG + offset); i++) {
 		ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
 		if (ret_val) {
 			DEBUGOUT("NVM Read Error while updating checksum.\n");
diff --git a/drivers/net/intel/e1000/base/e1000_manage.c b/drivers/net/intel/e1000/base/e1000_manage.c
index 4b81028302..71df2890f3 100644
--- a/drivers/net/intel/e1000/base/e1000_manage.c
+++ b/drivers/net/intel/e1000/base/e1000_manage.c
@@ -129,7 +129,7 @@ bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw)
 	len    = E1000_MNG_DHCP_COOKIE_LENGTH >> 2;
 	offset = E1000_MNG_DHCP_COOKIE_OFFSET >> 2;
 	for (i = 0; i < len; i++)
-		*(buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF,
+		buffer[i] = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF,
 							   offset + i);
 	hdr_csum = hdr->checksum;
 	hdr->checksum = 0;
@@ -173,7 +173,7 @@ s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw,
 	/* Write the relevant command block into the ram area. */
 	for (i = 0; i < length; i++) {
 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i,
-					    *((u32 *) hdr + i));
+						((u32 *)hdr)[i]);
 		E1000_WRITE_FLUSH(hw);
 	}
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 06/10] net/e1000/base: fix coding style issues
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Dima Ruinskiy, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Dima Ruinskiy <dima.ruinskiy@intel.com>

Fix whitespace and tab inconsistencies.

Fixes: a3358d942fa9 ("net/e1000/base: enable new I219 devices")
Fixes: bdca22d62ff0 ("net/e1000/base: add PHY read/write retry mechanism")

Signed-off-by: Dima Ruinskiy <dima.ruinskiy@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/e1000_ich8lan.c | 6 +++---
 drivers/net/intel/e1000/base/e1000_phy.c     | 5 ++---
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/net/intel/e1000/base/e1000_ich8lan.c b/drivers/net/intel/e1000/base/e1000_ich8lan.c
index 96b9ad6a70..e62847fb4e 100644
--- a/drivers/net/intel/e1000/base/e1000_ich8lan.c
+++ b/drivers/net/intel/e1000/base/e1000_ich8lan.c
@@ -4178,7 +4178,7 @@ STATIC s32 e1000_update_nvm_checksum_spt(struct e1000_hw *hw)
 
 		usec_delay(100);
 
-		/* Write the data to the new bank. Offset in words*/
+		/* Write the data to the new bank. Offset in words */
 		act_offset = i + new_bank_offset;
 		ret_val = e1000_retry_write_flash_dword_ich8lan(hw, act_offset,
 								dword);
@@ -4201,7 +4201,7 @@ STATIC s32 e1000_update_nvm_checksum_spt(struct e1000_hw *hw)
 	 */
 	act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD;
 
-	/*offset in words but we read dword*/
+	/* offset in words but we read dword */
 	--act_offset;
 	ret_val = e1000_read_flash_dword_ich8lan(hw, act_offset, &dword);
 
@@ -4214,7 +4214,7 @@ STATIC s32 e1000_update_nvm_checksum_spt(struct e1000_hw *hw)
 	if (ret_val)
 		goto release;
 
-	/* offset in words but we read dword*/
+	/* offset in words but we read dword */
 	act_offset = old_bank_offset + E1000_ICH_NVM_SIG_WORD - 1;
 	ret_val = e1000_read_flash_dword_ich8lan(hw, act_offset, &dword);
 
diff --git a/drivers/net/intel/e1000/base/e1000_phy.c b/drivers/net/intel/e1000/base/e1000_phy.c
index b449c6ef42..31ef5089ba 100644
--- a/drivers/net/intel/e1000/base/e1000_phy.c
+++ b/drivers/net/intel/e1000/base/e1000_phy.c
@@ -253,7 +253,7 @@ s32 e1000_phy_reset_dsp_generic(struct e1000_hw *hw)
 	return hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0);
 }
 
-void e1000_disable_phy_retry_mechanism(struct e1000_hw* hw, u32* phy_retries_original)
+void e1000_disable_phy_retry_mechanism(struct e1000_hw *hw, u32 *phy_retries_original)
 {
 	DEBUGFUNC("e1000_disable_phy_retry_mechanism");
 
@@ -262,7 +262,7 @@ void e1000_disable_phy_retry_mechanism(struct e1000_hw* hw, u32* phy_retries_ori
 	hw->phy.current_retry_counter = 0;
 }
 
-void e1000_enable_phy_retry_mechanism(struct e1000_hw* hw, u32 phy_retries_original)
+void e1000_enable_phy_retry_mechanism(struct e1000_hw *hw, u32 phy_retries_original)
 {
 	DEBUGFUNC("e1000_enable_phy_retry_mechanism");
 
@@ -346,7 +346,6 @@ s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
 		}
 	}
 
-
 	return -E1000_ERR_PHY;
 }
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 05/10] net/e1000/base: clear DPG enable bit post MAC reset
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Vitaly Lifshits, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Vitaly Lifshits <vitaly.lifshits@intel.com>

The GbE autonomous power gating feature was added to support G3 to S5
flow. However, this changed the reset value of DPG_EN bit to 1, causing
a possible autonomous transition to power gating state during D0. This
might result in undefined errors such as: packet loss, packet corruption
and Tx/Rx hangs. Therefore, clear DPG_EN bit after hardware reset flow.

Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/e1000_defines.h | 1 +
 drivers/net/intel/e1000/base/e1000_ich8lan.c | 7 +++++++
 2 files changed, 8 insertions(+)

diff --git a/drivers/net/intel/e1000/base/e1000_defines.h b/drivers/net/intel/e1000/base/e1000_defines.h
index eb93675823..6c710300a6 100644
--- a/drivers/net/intel/e1000/base/e1000_defines.h
+++ b/drivers/net/intel/e1000/base/e1000_defines.h
@@ -36,6 +36,7 @@
 
 /* Extended Device Control */
 #define E1000_CTRL_EXT_LPCD		0x00000004 /* LCD Power Cycle Done */
+#define E1000_CTRL_EXT_DPG_EN		0x00000008 /* Dynamic Power Gating Enable */
 #define E1000_CTRL_EXT_SDP4_DATA	0x00000010 /* SW Definable Pin 4 data */
 #define E1000_CTRL_EXT_SDP6_DATA	0x00000040 /* SW Definable Pin 6 data */
 #define E1000_CTRL_EXT_SDP3_DATA	0x00000080 /* SW Definable Pin 3 data */
diff --git a/drivers/net/intel/e1000/base/e1000_ich8lan.c b/drivers/net/intel/e1000/base/e1000_ich8lan.c
index 6190052368..96b9ad6a70 100644
--- a/drivers/net/intel/e1000/base/e1000_ich8lan.c
+++ b/drivers/net/intel/e1000/base/e1000_ich8lan.c
@@ -5107,6 +5107,13 @@ STATIC s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
 	reg |= E1000_KABGTXD_BGSQLBIAS;
 	E1000_WRITE_REG(hw, E1000_KABGTXD, reg);
 
+	if (hw->mac.type >= e1000_pch_ptp) {
+		DEBUGOUT("Clearing DPG EN bit post reset\n");
+		reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+		reg &= ~E1000_CTRL_EXT_DPG_EN;
+		E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
+	}
+
 	return E1000_SUCCESS;
 }
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 04/10] net/e1000/base: reclassify MAC type
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Vitaly Lifshits, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Vitaly Lifshits <vitaly.lifshits@intel.com>

Reclassify LM19/V19 from e1000_pch_mtp to e1000_pch_adp.

Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/e1000_api.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/intel/e1000/base/e1000_api.c b/drivers/net/intel/e1000/base/e1000_api.c
index 7ff63414a7..a7877613e3 100644
--- a/drivers/net/intel/e1000/base/e1000_api.c
+++ b/drivers/net/intel/e1000/base/e1000_api.c
@@ -312,12 +312,12 @@ s32 e1000_set_mac_type(struct e1000_hw *hw)
 	case E1000_DEV_ID_PCH_ADL_I219_V17:
 	case E1000_DEV_ID_PCH_RPL_I219_LM22:
 	case E1000_DEV_ID_PCH_RPL_I219_V22:
+	case E1000_DEV_ID_PCH_MTP_I219_LM19:
+	case E1000_DEV_ID_PCH_MTP_I219_V19:
 		mac->type = e1000_pch_adp;
 		break;
 	case E1000_DEV_ID_PCH_MTP_I219_LM18:
 	case E1000_DEV_ID_PCH_MTP_I219_V18:
-	case E1000_DEV_ID_PCH_MTP_I219_LM19:
-	case E1000_DEV_ID_PCH_MTP_I219_V19:
 	case E1000_DEV_ID_PCH_LNL_I219_LM20:
 	case E1000_DEV_ID_PCH_LNL_I219_V20:
 	case E1000_DEV_ID_PCH_ARL_I219_LM24:
-- 
2.43.0


^ permalink raw reply related

* [PATCH 03/10] net/e1000/base: fix possible variable overflow
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Lukasz Czapnik, stable, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Lukasz Czapnik <lukasz.czapnik@intel.com>

Bits can be lost as temporary math is done on signed variables and the
result is assigned to an unsigned variable. Cast to u32 to force the
compiler to do operations on unsigned temporary variables.

Fixes: af75078fece3 ("first public release")
Cc: stable@dpdk.org

Signed-off-by: Lukasz Czapnik <lukasz.czapnik@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/e1000_mac.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/intel/e1000/base/e1000_mac.c b/drivers/net/intel/e1000/base/e1000_mac.c
index 41aae86ffe..408e8e46e8 100644
--- a/drivers/net/intel/e1000/base/e1000_mac.c
+++ b/drivers/net/intel/e1000/base/e1000_mac.c
@@ -1970,7 +1970,7 @@ s32 e1000_blink_led_generic(struct e1000_hw *hw)
 			     (mode == E1000_LEDCTL_MODE_LED_OFF))) {
 				ledctl_blink &=
 				    ~(E1000_LEDCTL_LED0_MODE_MASK << i);
-				ledctl_blink |= (E1000_LEDCTL_LED0_BLINK |
+				ledctl_blink |= (u32)(E1000_LEDCTL_LED0_BLINK |
 						 E1000_LEDCTL_MODE_LED_ON) << i;
 			}
 		}
-- 
2.43.0


^ permalink raw reply related

* [PATCH 02/10] net/e1000/base: fix typo in e1000 base code
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Vitaly Lifshits, stable, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Vitaly Lifshits <vitaly.lifshits@intel.com>

A typo was introduced in commit ba54bdc79d94 ("net/e1000/base: improve
clock synchronization workaround"). Fix it.

Fixes: ba54bdc79d94 ("net/e1000/base: improve clock synchronization workaround")
Cc: stable@dpdk.org

Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/e1000_ich8lan.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/intel/e1000/base/e1000_ich8lan.c b/drivers/net/intel/e1000/base/e1000_ich8lan.c
index 58597cce8a..6190052368 100644
--- a/drivers/net/intel/e1000/base/e1000_ich8lan.c
+++ b/drivers/net/intel/e1000/base/e1000_ich8lan.c
@@ -288,7 +288,7 @@ STATIC void e1000_toggle_lanphypc_pch_lpt(struct e1000_hw *hw)
  * e1000_reconfigure_k1_exit_timeout
  * @hw: pointer to the HW structure
  *
- * Reconfigure K1 exit timeout as a workaround to the PHY sycndhronization issue
+ * Reconfigure K1 exit timeout as a workaround to the PHY synchronization issue
  * on MTL, LNL, PTL and WCL.
  *
  * Assuming PHY semaphore is taken prior to this function call.
-- 
2.43.0


^ permalink raw reply related

* [PATCH 01/10] net/e1000/base: refactor K1 exit timeout configuration
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Vitaly Lifshits, stable, Ciara Loftus
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>

From: Vitaly Lifshits <vitaly.lifshits@intel.com>

Replace magic numbers in the FEXTNVM12 register access with named bit
definitions to improve readability. Add a missing error path to ensure
ME activity blocking a PHY reset is correctly propagated to the caller.
Also improve some code formatting.

Fixes: 38db3f7f50bd ("e1000: update base driver")
Cc: stable@dpdk.org

Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
 drivers/net/intel/e1000/base/e1000_ich8lan.c | 11 ++++++++---
 drivers/net/intel/e1000/base/e1000_ich8lan.h |  3 +++
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/drivers/net/intel/e1000/base/e1000_ich8lan.c b/drivers/net/intel/e1000/base/e1000_ich8lan.c
index ece61650f7..58597cce8a 100644
--- a/drivers/net/intel/e1000/base/e1000_ich8lan.c
+++ b/drivers/net/intel/e1000/base/e1000_ich8lan.c
@@ -305,8 +305,8 @@ STATIC s32 e1000_reconfigure_k1_exit_timeout(struct e1000_hw *hw)
 		return E1000_SUCCESS;
 
 	fextnvm12 = E1000_READ_REG(hw, E1000_FEXTNVM12);
-	fextnvm12 |= (1 << 23);
-	fextnvm12 &= ~((1 << 22));
+	fextnvm12 &= ~E1000_FEXTNVM12_PHYPD_CTRL_MASK;
+	fextnvm12 |= E1000_FEXTNVM12_PHYPD_CTRL_P1;
 	E1000_WRITE_REG(hw, E1000_FEXTNVM12, fextnvm12);
 
 	msec_delay_irq(1);
@@ -318,6 +318,9 @@ STATIC s32 e1000_reconfigure_k1_exit_timeout(struct e1000_hw *hw)
 	ret_val = hw->phy.ops.write_reg_locked(hw, E1000_PHY_TIMEOUTS_REG,
 					       phy_timeout);
 
+	DEBUGOUT1("e1000_reconfigure_k1_exit_timeout returns %d\n",
+		  ret_val);
+
 	return ret_val;
 }
 
@@ -460,8 +463,10 @@ STATIC s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
 		 *  the PHY is in.
 		 */
 		ret_val = hw->phy.ops.check_reset_block(hw);
-		if (ret_val)
+		if (ret_val) {
 			ERROR_REPORT("ME blocked access to PHY after reset\n");
+			goto out;
+		}
 
 		if (hw->mac.type >= e1000_pch_mtp) {
 			ret_val = hw->phy.ops.acquire(hw);
diff --git a/drivers/net/intel/e1000/base/e1000_ich8lan.h b/drivers/net/intel/e1000/base/e1000_ich8lan.h
index f2ba910ea6..43360400bc 100644
--- a/drivers/net/intel/e1000/base/e1000_ich8lan.h
+++ b/drivers/net/intel/e1000/base/e1000_ich8lan.h
@@ -89,6 +89,9 @@
 #define E1000_FEXTNVM11_DISABLE_PB_READ		0x00000200
 #define E1000_FEXTNVM11_DISABLE_MULR_FIX	0x00002000
 #define E1000_FEXTNVM12_DONT_WAK_DPG_CLKREQ	0x00001000
+#define E1000_FEXTNVM12_PHYPD_CTRL_MASK		0x00C00000
+#define E1000_FEXTNVM12_PHYPD_CTRL_P1		0x00800000
+
 /* bit24: RXDCTL thresholds granularity: 0 - cache lines, 1 - descriptors */
 #define E1000_RXDCTL_THRESH_UNIT_DESC	0x01000000
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 00/10] e1000 base code update
From: Ciara Loftus @ 2026-05-20 12:52 UTC (permalink / raw)
  To: dev; +Cc: Ciara Loftus

Update the e1000 base code. The changes are mostly fixes, refactoring,
and code cleanup.

Ciara Loftus (1):
  net/e1000/base: update version info

Dima Ruinskiy (2):
  net/e1000/base: fix coding style issues
  net/e1000/base: propagate PHY control register write error

Lukasz Czapnik (1):
  net/e1000/base: fix possible variable overflow

Mateusz Fryze (1):
  net/e1000/base: auto-negotiation status for 1000BASE-T

Menachem Fogel (1):
  net/e1000/base: fix NVM loop bounds and pointer access

Vitaly Lifshits (4):
  net/e1000/base: refactor K1 exit timeout configuration
  net/e1000/base: fix typo in e1000 base code
  net/e1000/base: reclassify MAC type
  net/e1000/base: clear DPG enable bit post MAC reset

 .mailmap                                     |  1 +
 drivers/net/intel/e1000/base/README          |  4 +-
 drivers/net/intel/e1000/base/e1000_82575.c   |  5 +-
 drivers/net/intel/e1000/base/e1000_api.c     | 20 ++++-
 drivers/net/intel/e1000/base/e1000_api.h     |  1 +
 drivers/net/intel/e1000/base/e1000_defines.h | 23 ++++++
 drivers/net/intel/e1000/base/e1000_hw.h      |  9 ++
 drivers/net/intel/e1000/base/e1000_ich8lan.c | 28 +++++--
 drivers/net/intel/e1000/base/e1000_ich8lan.h |  3 +
 drivers/net/intel/e1000/base/e1000_mac.c     |  2 +-
 drivers/net/intel/e1000/base/e1000_manage.c  |  4 +-
 drivers/net/intel/e1000/base/e1000_phy.c     | 86 +++++++++++++++++++-
 drivers/net/intel/e1000/base/e1000_phy.h     |  2 +
 13 files changed, 169 insertions(+), 19 deletions(-)

-- 
2.43.0


^ permalink raw reply

* [PATCH v4 11/11] test/bpf: add tests for error handling contracts
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Verify NULL parameter rejection in load APIs, graceful failure on
argument/flag mismatch in burst execution APIs, and safe return of the
libpcap stub.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 app/test/test_bpf.c | 128 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 127 insertions(+), 1 deletion(-)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index 026ba18b754a..6422bae6fe5f 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -3514,6 +3514,121 @@ run_test(const struct bpf_test *tst)
 
 }
 
+/* Test all eBPF load APIs with prm set to NULL. */
+static int
+test_bpf_load_null(void)
+{
+	struct rte_bpf *bpf;
+	int saved_errno;
+
+	rte_errno = 0;
+	bpf = rte_bpf_load(NULL);
+	saved_errno = rte_errno;
+	rte_bpf_destroy(bpf);
+	RTE_TEST_ASSERT_NULL(bpf, "rte_bpf_load(NULL) did not return NULL\n");
+	RTE_TEST_ASSERT_EQUAL(saved_errno, EINVAL,
+		"rte_bpf_load(NULL) did not set rte_errno to EINVAL\n");
+
+	rte_errno = 0;
+	bpf = rte_bpf_elf_load(NULL, "a", "b");
+	saved_errno = rte_errno;
+	rte_bpf_destroy(bpf);
+	RTE_TEST_ASSERT_NULL(bpf, "rte_bpf_elf_load(NULL, \"a\", \"b\") did not return NULL\n");
+	RTE_TEST_ASSERT_EQUAL(saved_errno, EINVAL,
+		"rte_bpf_elf_load(NULL, \"a\", \"b\") did not set rte_errno to EINVAL\n");
+
+	rte_errno = 0;
+	bpf = rte_bpf_load_ex(NULL);
+	saved_errno = rte_errno;
+	rte_bpf_destroy(bpf);
+	RTE_TEST_ASSERT_NULL(bpf, "rte_bpf_load_ex(NULL) did not return NULL\n");
+	RTE_TEST_ASSERT_EQUAL(saved_errno, EINVAL,
+		"rte_bpf_load_ex(NULL) did not set rte_errno to EINVAL\n");
+
+	return 0;
+}
+REGISTER_FAST_TEST(bpf_load_null_autotest, NOHUGE_OK, ASAN_OK, test_bpf_load_null);
+
+/* Test calling wrong API for execution of a multi-argument eBPF program. */
+static int
+test_bpf_exec_wrong_nb_prog_arg(void)
+{
+	static const struct ebpf_insn ins[] = {
+		{ .code = (EBPF_ALU64 | EBPF_MOV | BPF_K), .dst_reg = EBPF_REG_0, .imm = 0 },
+		{ .code = (BPF_JMP | EBPF_EXIT), }
+	};
+	static const struct rte_bpf_prm_ex prm = {
+		.sz = sizeof(struct rte_bpf_prm_ex),
+		.origin = RTE_BPF_ORIGIN_RAW,
+		.raw.ins = ins,
+		.raw.nb_ins = RTE_DIM(ins),
+		.prog_arg = {
+			{ .type = RTE_BPF_ARG_RAW, .size = sizeof(uint64_t) },
+			{ .type = RTE_BPF_ARG_RAW, .size = sizeof(uint64_t) },
+		},
+		.nb_prog_arg = 2, /* Intentionally mismatched: expects 2, burst gives 1 */
+	};
+
+	struct rte_bpf *bpf;
+	uint64_t rc[1];
+	void *ctx[1] = {NULL};
+	uint32_t result;
+	int saved_errno;
+
+	bpf = rte_bpf_load_ex(&prm);
+	RTE_TEST_ASSERT_NOT_NULL(bpf, "rte_bpf_load_ex failed\n");
+
+	rte_errno = 0;
+	result = rte_bpf_exec_burst(bpf, ctx, rc, 1);
+	saved_errno = rte_errno;
+	rte_bpf_destroy(bpf);
+	RTE_TEST_ASSERT_EQUAL(result, 0, "rte_bpf_exec_burst did not return 0\n");
+	RTE_TEST_ASSERT_EQUAL(saved_errno, EINVAL,
+		"rte_bpf_exec_burst did not set rte_errno to EINVAL\n");
+
+	return 0;
+}
+REGISTER_FAST_TEST(bpf_exec_wrong_nb_prog_arg_autotest, NOHUGE_OK, ASAN_OK,
+		test_bpf_exec_wrong_nb_prog_arg);
+
+/* Test passing unsupported flags when executing an eBPF program. */
+static int
+test_bpf_exec_wrong_flags(void)
+{
+	static const struct ebpf_insn ins[] = {
+		{ .code = (EBPF_ALU64 | EBPF_MOV | BPF_K), .dst_reg = EBPF_REG_0, .imm = 0 },
+		{ .code = (BPF_JMP | EBPF_EXIT), }
+	};
+	static const struct rte_bpf_prm_ex prm = {
+		.sz = sizeof(struct rte_bpf_prm_ex),
+		.origin = RTE_BPF_ORIGIN_RAW,
+		.raw.ins = ins,
+		.raw.nb_ins = RTE_DIM(ins),
+		.prog_arg = { { .type = RTE_BPF_ARG_RAW, .size = sizeof(uint64_t) } },
+		.nb_prog_arg = 1,
+	};
+
+	struct rte_bpf *bpf;
+	uint64_t rc[1];
+	struct rte_bpf_prog_ctx ctx_ex[1] = {};
+	uint32_t result;
+	int saved_errno;
+
+	bpf = rte_bpf_load_ex(&prm);
+	RTE_TEST_ASSERT_NOT_NULL(bpf, "rte_bpf_load_ex failed\n");
+
+	rte_errno = 0;
+	result = rte_bpf_exec_burst_ex(bpf, ctx_ex, rc, 1, UINT64_MAX);
+	saved_errno = rte_errno;
+	rte_bpf_destroy(bpf);
+	RTE_TEST_ASSERT_EQUAL(result, 0, "rte_bpf_exec_burst_ex did not return 0\n");
+	RTE_TEST_ASSERT_EQUAL(saved_errno, EINVAL,
+		"rte_bpf_exec_burst_ex did not set rte_errno to EINVAL\n");
+
+	return 0;
+}
+REGISTER_FAST_TEST(bpf_exec_wrong_flags_autotest, NOHUGE_OK, ASAN_OK, test_bpf_exec_wrong_flags);
+
 static int
 test_bpf(void)
 {
@@ -4444,7 +4559,18 @@ REGISTER_FAST_TEST(bpf_elf_autotest, NOHUGE_OK, ASAN_OK, test_bpf_elf);
 static int
 test_bpf_convert(void)
 {
-	printf("BPF convert RTE_HAS_LIBPCAP is undefined, skipping test\n");
+	int dummy = 0;
+	struct rte_bpf_prm *prm;
+
+	prm = rte_bpf_convert(NULL);
+	rte_free(prm);
+	RTE_TEST_ASSERT_NULL(prm, "rte_bpf_convert(NULL) without libpcap did not return NULL\n");
+
+	prm = rte_bpf_convert((const struct bpf_program *)&dummy);
+	rte_free(prm);
+	RTE_TEST_ASSERT_NULL(prm, "rte_bpf_convert(&dummy) without libpcap did not return NULL\n");
+
+	printf("BPF convert RTE_HAS_LIBPCAP is undefined, skipping full test\n");
 	return TEST_SKIPPED;
 }
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 10/11] doc: add load API to BPF programmer's guide
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Rewrite the basic operations list to focus on a typical use. Provide an
end-to-end example demonstrating loading from an ELF file, executing via
JIT or the interpreter, and properly handling multiple custom arguments
using rte_bpf_prog_ctx.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 doc/guides/prog_guide/bpf_lib.rst | 75 ++++++++++++++++++++++++++++---
 1 file changed, 68 insertions(+), 7 deletions(-)

diff --git a/doc/guides/prog_guide/bpf_lib.rst b/doc/guides/prog_guide/bpf_lib.rst
index 8c820328b984..df3782508829 100644
--- a/doc/guides/prog_guide/bpf_lib.rst
+++ b/doc/guides/prog_guide/bpf_lib.rst
@@ -15,17 +15,79 @@ for more information.
 Also it introduces basic framework to load/unload BPF-based filters
 on eth devices (right now only via SW RX/TX callbacks).
 
-The library API provides the following basic operations:
+The library API provides the following basic operations for working with BPF
+programs:
 
-*  Create a new BPF execution context and load user provided eBPF code into it.
+*   **Loading:** The extensible API (``rte_bpf_load_ex``) is the recommended
+    way to load a BPF program. By utilizing ``struct rte_bpf_prm_ex``, you can
+    load an eBPF program from an ELF file on disk, or load eBPF/cBPF bytecode
+    directly from memory buffers.
 
-*   Destroy an BPF execution context and its runtime structures and free the associated memory.
+*   **Execution via Callbacks:** Once loaded, a BPF program can be attached to
+    a specific ethernet device port and queue to automatically process incoming
+    or outgoing packets using ``rte_bpf_eth_rx_install`` or
+    ``rte_bpf_eth_tx_install``.
 
-*   Execute eBPF bytecode associated with provided input parameter.
+*   **Direct Execution:** You can execute a BPF program directly from your
+    application code using ``rte_bpf_exec_ex`` (or the burst variant
+    ``rte_bpf_exec_burst_ex``). This API allows passing an execution context
+    (``struct rte_bpf_prog_ctx``) containing up to 5 custom arguments.
 
-*   Provide information about natively compiled code for given BPF context.
+*   **JIT Execution:** For maximum performance, you can retrieve the natively
+    compiled (JIT) function pointer for a loaded program using
+    ``rte_bpf_get_jit_ex`` and call it directly from your code with the same
+    arguments.
 
-*   Load BPF program from the ELF file and install callback to execute it on given ethdev port/queue.
+*   **Cleanup:** Destroy a BPF execution context and free the associated memory
+    using ``rte_bpf_destroy``.
+
+The following is a concise example of loading an eBPF program from an ELF file,
+and executing it directly, utilizing the JIT-compiled version if available:
+
+.. code-block:: c
+
+    struct rte_bpf_prm_ex prm = {
+        .sz = sizeof(struct rte_bpf_prm_ex),
+        .origin = RTE_BPF_ORIGIN_ELF_FILE,
+        .elf_file = {
+            .path = "ptype.o",
+            .section = ".text",
+        },
+        .nb_prog_arg = 2,
+        .prog_arg = {
+            [0] = {
+                .type = RTE_BPF_ARG_PTR_MBUF,
+                .size = sizeof(struct rte_mbuf),
+                .buf_size = RTE_MBUF_DEFAULT_BUF_SIZE,
+            },
+            [1] = {
+                .type = RTE_BPF_ARG_RAW,
+                .size = sizeof(uint64_t),
+            },
+        },
+    };
+    struct rte_bpf *bpf = rte_bpf_load_ex(&prm);
+    if (bpf == NULL) {
+        /* Handle load failure */
+    }
+
+    struct rte_bpf_prog_ctx ctx = {
+        .arg[0] = { .ptr = mbuf },
+        .arg[1] = { .u64 = RTE_PTYPE_L2_MASK | RTE_PTYPE_L3_MASK },
+    };
+
+    struct rte_bpf_jit_ex jit;
+    uint64_t ret;
+    if (rte_bpf_get_jit_ex(bpf, &jit) == 0 && jit.func2 != NULL) {
+        /* Call the JIT-compiled function directly for best performance */
+        ret = jit.func2(ctx.arg[0], ctx.arg[1]);
+    } else {
+        /* Fallback to interpreter */
+        uint64_t flags = 0;
+        ret = rte_bpf_exec_ex(bpf, &ctx, flags);
+    }
+
+    rte_bpf_destroy(bpf);
 
 Packet data load instructions
 -----------------------------
@@ -60,7 +122,6 @@ Not currently supported eBPF features
 -------------------------------------
 
  - JIT support only available for X86_64 and arm64 platforms
- - cBPF
  - tail-pointer call
  - eBPF MAP
  - external function calls for 32-bit platforms
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 09/11] doc: add release notes for new extensible BPF API
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  Cc: dev, Konstantin Ananyev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Document the following new eBPF features introduced in this release:
* Extensible BPF loading API (rte_bpf_load_ex, rte_bpf_prm_ex).
* Loading and executing eBPF programs with up to 5 arguments.
* Installing already loaded eBPF programs as port callbacks.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 doc/guides/rel_notes/release_26_07.rst | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index f012d47a4b1b..18810ab81d93 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -63,6 +63,26 @@ New Features
     ``rte_eal_init`` and the application is responsible for probing each device,
   * ``--auto-probing`` enables the initial bus probing, which is the current default behavior.
 
+* **Added extensible BPF loading API.**
+
+  Added an extensible BPF loading API comprising the function
+  ``rte_bpf_load_ex`` and struct ``rte_bpf_prm_ex``. This enables new features
+  such as loading classic BPF (cBPF), loading ELF images directly from memory
+  buffers, and executing multi-argument programs, while avoiding future ABI
+  breakages.
+
+* **Added support for executing BPF programs with multiple arguments.**
+
+  Added support for loading and executing BPF programs with up to 5 arguments.
+  This introduces new API functions ``rte_bpf_exec_ex``,
+  ``rte_bpf_exec_burst_ex``, and ``rte_bpf_get_jit_ex``.
+
+* **Added BPF port callback installation API.**
+
+  Added new API functions ``rte_bpf_eth_rx_install`` and
+  ``rte_bpf_eth_tx_install`` for installing already loaded BPF programs as
+  port callbacks (as opposed to loading them directly from ELF files).
+
 
 Removed Items
 -------------
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 08/11] test/bpf: test loading ELF file from memory
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Run each subtest in test_bpf_elf twice: the old way loading ELF images
via temporary file, and using the new rte_bpf_load_ex API to load them
directly from memory.

In tests loading port/queue filters use new rte_bpf_eth_(rx|tx)_install
API to install an already loaded (via one of the ways) BPF program.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 app/test/test_bpf.c | 194 ++++++++++++++++++++++++++------------------
 1 file changed, 114 insertions(+), 80 deletions(-)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index c43b872a4c83..026ba18b754a 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -3977,12 +3977,61 @@ create_temp_bpf_file(const uint8_t *data, size_t size, const char *name)
 
 #include "test_bpf_load.h"
 
+/* Function loading BPF program from ELF image in memory. */
+typedef struct rte_bpf *
+(*load_elf_image_t)(const void *data, size_t size, const char *section,
+	const struct rte_bpf_xsym *xsym, uint32_t nb_xsym, const struct rte_bpf_arg *prog_arg);
+
+/* Load BPF program by writing ELF image to temporary file and opening this file. */
+static struct rte_bpf *
+load_elf_image_temp_file(const void *data, size_t size, const char *section,
+	const struct rte_bpf_xsym *xsym, uint32_t nb_xsym, const struct rte_bpf_arg *prog_arg)
+{
+	/* Create temp file from embedded BPF object */
+	char *tmpfile = create_temp_bpf_file(data, size, "test");
+	if (tmpfile == NULL) {
+		rte_errno = EIO;
+		return NULL;
+	}
+
+	/* Try to load BPF program from temp file */
+	const struct rte_bpf_prm prm = {
+		.xsym = xsym,
+		.nb_xsym = nb_xsym,
+		.prog_arg = *prog_arg,
+	};
+
+	struct rte_bpf *bpf = rte_bpf_elf_load(&prm, tmpfile, section);
+	unlink(tmpfile);
+	free(tmpfile);
+
+	return bpf;
+}
+
+/* Load BPF program by calling rte_bpf_load_ex and specifying image as the origin. */
+static struct rte_bpf *
+load_elf_image_direct(const void *data, size_t size, const char *section,
+	const struct rte_bpf_xsym *xsym, uint32_t nb_xsym, const struct rte_bpf_arg *prog_arg)
+{
+	return rte_bpf_load_ex(&(struct rte_bpf_prm_ex){
+		.sz = sizeof(struct rte_bpf_prm_ex),
+		.origin = RTE_BPF_ORIGIN_ELF_MEMORY,
+		.elf_memory.data = data,
+		.elf_memory.size = size,
+		.elf_memory.section = section,
+		.xsym = xsym,
+		.nb_xsym = nb_xsym,
+		.prog_arg[0] = *prog_arg,
+		.nb_prog_arg = 1,
+	});
+}
+
 /*
  * Test loading BPF program from an object file.
  * This test uses same arguments as previous test_call1 example.
  */
 static int
-test_bpf_elf_load(void)
+test_bpf_elf_load(load_elf_image_t load_elf_image)
 {
 	static const char test_section[] = "call1";
 	uint8_t tbuf[sizeof(struct dummy_vect8)];
@@ -4010,28 +4059,15 @@ test_bpf_elf_load(void)
 			},
 		},
 	};
-	int ret;
-
-	/* Create temp file from embedded BPF object */
-	char *tmpfile = create_temp_bpf_file(app_test_bpf_load_o,
-					     app_test_bpf_load_o_len,
-					     "load");
-	if (tmpfile == NULL)
-		return -1;
-
-	/* Try to load BPF program from temp file */
-	const struct rte_bpf_prm prm = {
-		.xsym = xsym,
-		.nb_xsym = RTE_DIM(xsym),
-		.prog_arg = {
-			.type = RTE_BPF_ARG_PTR,
-			.size = sizeof(tbuf),
-		},
+	static const struct rte_bpf_arg prog_arg = {
+		.type = RTE_BPF_ARG_PTR,
+		.size = sizeof(tbuf),
 	};
+	struct rte_bpf *bpf;
+	int ret;
 
-	struct rte_bpf *bpf = rte_bpf_elf_load(&prm, tmpfile, test_section);
-	unlink(tmpfile);
-	free(tmpfile);
+	bpf = load_elf_image(app_test_bpf_load_o, app_test_bpf_load_o_len, test_section,
+		xsym, RTE_DIM(xsym), &prog_arg);
 
 	/* If libelf support is not available */
 	if (bpf == NULL && rte_errno == ENOTSUP)
@@ -4174,22 +4210,28 @@ setup_mbufs(struct rte_mbuf *burst[], unsigned int n)
 	return tcp_count;
 }
 
-static int bpf_tx_test(uint16_t port, const char *tmpfile, struct rte_mempool *pool,
-		       const char *section, uint32_t flags)
+static int bpf_tx_test(uint16_t port, struct rte_mempool *pool, load_elf_image_t load_elf_image,
+	const char *section, uint32_t flags)
 {
-	const struct rte_bpf_prm prm = {
-		.prog_arg = {
-			.type = RTE_BPF_ARG_PTR,
-			.size = sizeof(struct dummy_net),
-		},
+	static const struct rte_bpf_arg prog_arg = {
+		.type = RTE_BPF_ARG_PTR,
+		.size = sizeof(struct dummy_net),
 	};
+	struct rte_bpf *bpf;
 	int ret;
 
-	/* Try to load BPF TX program from temp file */
-	ret = rte_bpf_eth_tx_elf_load(port, 0, &prm, tmpfile, section, flags);
+	/* Try to load BPF program from image */
+	bpf = load_elf_image(app_test_bpf_filter_o, app_test_bpf_filter_o_len, section,
+		NULL, 0, &prog_arg);
+	TEST_ASSERT_NOT_NULL(bpf, "failed to load BPF filter from image, error=%d:(%s)\n",
+		       rte_errno, rte_strerror(rte_errno));
+
+	/* Try to install loaded BPF program */
+	ret = rte_bpf_eth_tx_install(port, 0, bpf, flags);
 	if (ret != 0) {
-		printf("%s@%d: failed to load BPF filter from file=%s error=%d:(%s)\n",
-		       __func__, __LINE__, tmpfile, rte_errno, rte_strerror(rte_errno));
+		printf("%s@%d: failed to install BPF filter, error=%d:(%s)\n",
+		       __func__, __LINE__, rte_errno, rte_strerror(rte_errno));
+		rte_bpf_destroy(bpf);
 		return ret;
 	}
 
@@ -4217,10 +4259,9 @@ static int bpf_tx_test(uint16_t port, const char *tmpfile, struct rte_mempool *p
 
 /* Test loading a transmit filter which only allows IPv4 packets */
 static int
-test_bpf_elf_tx_load(void)
+test_bpf_elf_tx_load(load_elf_image_t load_elf_image)
 {
 	static const char null_dev[] = "net_null_bpf0";
-	char *tmpfile = NULL;
 	struct rte_mempool *mb_pool = NULL;
 	uint16_t port = UINT16_MAX;
 	int ret;
@@ -4237,27 +4278,17 @@ test_bpf_elf_tx_load(void)
 	if (ret != 0)
 		goto fail;
 
-	/* Create temp file from embedded BPF object */
-	tmpfile = create_temp_bpf_file(app_test_bpf_filter_o, app_test_bpf_filter_o_len, "tx");
-	if (tmpfile == NULL)
-		goto fail;
-
 	/* Do test with VM */
-	ret = bpf_tx_test(port, tmpfile, mb_pool, "filter", 0);
+	ret = bpf_tx_test(port, mb_pool, load_elf_image, "filter", 0);
 	if (ret != 0)
 		goto fail;
 
 	/* Repeat with JIT */
-	ret = bpf_tx_test(port, tmpfile, mb_pool, "filter", RTE_BPF_ETH_F_JIT);
+	ret = bpf_tx_test(port, mb_pool, load_elf_image, "filter", RTE_BPF_ETH_F_JIT);
 	if (ret == 0)
 		printf("%s: TX ELF load test passed\n", __func__);
 
 fail:
-	if (tmpfile) {
-		unlink(tmpfile);
-		free(tmpfile);
-	}
-
 	if (port != UINT16_MAX)
 		rte_vdev_uninit(null_dev);
 
@@ -4272,23 +4303,29 @@ test_bpf_elf_tx_load(void)
 }
 
 /* Test loading a receive filter */
-static int bpf_rx_test(uint16_t port, const char *tmpfile, struct rte_mempool *pool,
-		       const char *section, uint32_t flags, uint16_t expected)
+static int bpf_rx_test(uint16_t port, struct rte_mempool *pool, load_elf_image_t load_elf_image,
+	const char *section, uint32_t flags, uint16_t expected)
 {
-	struct rte_mbuf *pkts[BPF_TEST_BURST];
-	const struct rte_bpf_prm prm = {
-		.prog_arg = {
-			.type = RTE_BPF_ARG_PTR,
-			.size = sizeof(struct dummy_net),
-		},
+	static const struct rte_bpf_arg prog_arg = {
+		.type = RTE_BPF_ARG_PTR,
+		.size = sizeof(struct dummy_net),
 	};
+	struct rte_mbuf *pkts[BPF_TEST_BURST];
+	struct rte_bpf *bpf;
 	int ret;
 
-	/* Load BPF program to drop all packets */
-	ret = rte_bpf_eth_rx_elf_load(port, 0, &prm, tmpfile, section, flags);
+	/* Try to load BPF program from image */
+	bpf = load_elf_image(app_test_bpf_filter_o, app_test_bpf_filter_o_len, section,
+		NULL, 0, &prog_arg);
+	TEST_ASSERT_NOT_NULL(bpf, "failed to load BPF filter from image, error=%d:(%s)\n",
+		       rte_errno, rte_strerror(rte_errno));
+
+	/* Try to install loaded BPF program */
+	ret = rte_bpf_eth_rx_install(port, 0, bpf, flags);
 	if (ret != 0) {
-		printf("%s@%d: failed to load BPF filter from file=%s error=%d:(%s)\n",
-		       __func__, __LINE__, tmpfile, rte_errno, rte_strerror(rte_errno));
+		printf("%s@%d: failed to install BPF filter, error=%d:(%s)\n",
+		       __func__, __LINE__, rte_errno, rte_strerror(rte_errno));
+		rte_bpf_destroy(bpf);
 		return ret;
 	}
 
@@ -4311,11 +4348,10 @@ static int bpf_rx_test(uint16_t port, const char *tmpfile, struct rte_mempool *p
 
 /* Test loading a receive filters, first with drop all and then with allow all packets */
 static int
-test_bpf_elf_rx_load(void)
+test_bpf_elf_rx_load(load_elf_image_t load_elf_image)
 {
 	static const char null_dev[] = "net_null_bpf0";
 	struct rte_mempool *pool = NULL;
-	char *tmpfile = NULL;
 	uint16_t port = UINT16_MAX;
 	int ret;
 
@@ -4331,28 +4367,23 @@ test_bpf_elf_rx_load(void)
 	if (ret != 0)
 		goto fail;
 
-	/* Create temp file from embedded BPF object */
-	tmpfile = create_temp_bpf_file(app_test_bpf_filter_o, app_test_bpf_filter_o_len, "rx");
-	if (tmpfile == NULL)
-		goto fail;
-
 	/* Do test with VM */
-	ret = bpf_rx_test(port, tmpfile, pool, "drop", 0, 0);
+	ret = bpf_rx_test(port, pool, load_elf_image, "drop", 0, 0);
 	if (ret != 0)
 		goto fail;
 
 	/* Repeat with JIT */
-	ret = bpf_rx_test(port, tmpfile, pool, "drop", RTE_BPF_ETH_F_JIT, 0);
+	ret = bpf_rx_test(port, pool, load_elf_image, "drop", RTE_BPF_ETH_F_JIT, 0);
 	if (ret != 0)
 		goto fail;
 
 	/* Repeat with allow all */
-	ret = bpf_rx_test(port, tmpfile, pool, "allow", 0, BPF_TEST_BURST);
+	ret = bpf_rx_test(port, pool, load_elf_image, "allow", 0, BPF_TEST_BURST);
 	if (ret != 0)
 		goto fail;
 
 	/* Repeat with JIT */
-	ret = bpf_rx_test(port, tmpfile, pool, "allow", RTE_BPF_ETH_F_JIT, BPF_TEST_BURST);
+	ret = bpf_rx_test(port, pool, load_elf_image, "allow", RTE_BPF_ETH_F_JIT, BPF_TEST_BURST);
 	if (ret != 0)
 		goto fail;
 
@@ -4364,11 +4395,6 @@ test_bpf_elf_rx_load(void)
 			  "Mempool available %u != %u leaks?", avail, BPF_TEST_POOLSIZE);
 
 fail:
-	if (tmpfile) {
-		unlink(tmpfile);
-		free(tmpfile);
-	}
-
 	if (port != UINT16_MAX)
 		rte_vdev_uninit(null_dev);
 
@@ -4381,13 +4407,21 @@ test_bpf_elf_rx_load(void)
 static int
 test_bpf_elf(void)
 {
-	int ret;
+	static const load_elf_image_t elf_image_loaders[] = {
+		load_elf_image_temp_file,
+		load_elf_image_direct,
+	};
 
-	ret = test_bpf_elf_load();
-	if (ret == TEST_SUCCESS)
-		ret = test_bpf_elf_tx_load();
-	if (ret == TEST_SUCCESS)
-		ret = test_bpf_elf_rx_load();
+	int ret = TEST_SUCCESS;
+
+	for (int li = 0; li != RTE_DIM(elf_image_loaders); ++li) {
+		if (ret == TEST_SUCCESS)
+			ret = test_bpf_elf_load(elf_image_loaders[li]);
+		if (ret == TEST_SUCCESS)
+			ret = test_bpf_elf_tx_load(elf_image_loaders[li]);
+		if (ret == TEST_SUCCESS)
+			ret = test_bpf_elf_rx_load(elf_image_loaders[li]);
+	}
 
 	return ret;
 }
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 07/11] test/bpf: test loading cBPF directly
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Run cBPF tests twice: via rte_bpf_convert, and using
RTE_BPF_FLAG_ORIGIN_CBPF origin of new rte_bpf_load_ex API.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 app/test/test_bpf.c | 133 +++++++++++++++++++++++++++-----------------
 1 file changed, 81 insertions(+), 52 deletions(-)

diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c
index dd247224504e..c43b872a4c83 100644
--- a/app/test/test_bpf.c
+++ b/app/test/test_bpf.c
@@ -4429,13 +4429,59 @@ test_bpf_dump(struct bpf_program *cbf, const struct rte_bpf_prm *prm)
 	}
 }
 
+/* Function loading BPF program from cBPF instructions array. */
+typedef struct rte_bpf *
+(*load_cbpf_program_t)(struct bpf_program *cbpf_program, const char *str);
+
+/* Load BPF program by converting cBPF array to rte_bpf_prm and then opening it. */
+static struct rte_bpf *
+load_cbpf_program_convert(struct bpf_program *cbpf_program, const char *str)
+{
+	struct rte_bpf_prm *prm = NULL;
+	struct rte_bpf *bpf;
+
+	prm = rte_bpf_convert(cbpf_program);
+	if (prm == NULL) {
+		printf("%s@%d: bpf_convert(\"%s\") failed\n",
+			__func__, __LINE__, str);
+		return NULL;
+	}
+
+	printf("bpf convert(\"%s\") produced:\n", str);
+	rte_bpf_dump(stdout, prm->ins, prm->nb_ins);
+
+	printf("%s \"%s\"\n", __func__, str);
+	test_bpf_dump(cbpf_program, prm);
+
+	bpf = rte_bpf_load(prm);
+	rte_free(prm);
+
+	return bpf;
+}
+
+/* Load BPF program by calling rte_bpf_load_ex and specifying cBPF array as the origin. */
+static struct rte_bpf *
+load_cbpf_program_direct(struct bpf_program *cbpf_program, const char *str __rte_unused)
+{
+	return rte_bpf_load_ex(&(struct rte_bpf_prm_ex){
+		.sz = sizeof(struct rte_bpf_prm_ex),
+		.origin = RTE_BPF_ORIGIN_CBPF,
+		.cbpf.ins = cbpf_program->bf_insns,
+		.cbpf.nb_ins = cbpf_program->bf_len,
+		.prog_arg[0] = {
+			.type = RTE_BPF_ARG_PTR_MBUF,
+			.size = sizeof(struct rte_mbuf),
+		},
+		.nb_prog_arg = 1,
+	});
+}
+
 static int
-test_bpf_match(pcap_t *pcap, const char *str,
-	       struct rte_mbuf *mb)
+test_bpf_match(pcap_t *pcap, const char *str, struct rte_mbuf *mb,
+	load_cbpf_program_t load_cbpf_program)
 {
 	struct bpf_program fcode;
-	struct rte_bpf_prm *prm = NULL;
-	struct rte_bpf *bpf = NULL;
+	struct rte_bpf *bpf;
 	int ret = -1;
 	uint64_t rc;
 
@@ -4445,17 +4491,10 @@ test_bpf_match(pcap_t *pcap, const char *str,
 		return -1;
 	}
 
-	prm = rte_bpf_convert(&fcode);
-	if (prm == NULL) {
-		printf("%s@%d: bpf_convert('%s') failed,, error=%d(%s);\n",
-		       __func__, __LINE__, str, rte_errno, strerror(rte_errno));
-		goto error;
-	}
-
-	bpf = rte_bpf_load(prm);
+	bpf = load_cbpf_program(&fcode, str);
 	if (bpf == NULL) {
-		printf("%s@%d: failed to load bpf code, error=%d(%s);\n",
-			__func__, __LINE__, rte_errno, strerror(rte_errno));
+		printf("%s@%d: failed to load cbpf program for \"%s\", error=%d(%s);\n",
+			__func__, __LINE__, str, rte_errno, strerror(rte_errno));
 		goto error;
 	}
 
@@ -4465,7 +4504,6 @@ test_bpf_match(pcap_t *pcap, const char *str,
 error:
 	if (bpf)
 		rte_bpf_destroy(bpf);
-	rte_free(prm);
 	pcap_freecode(&fcode);
 	return ret;
 }
@@ -4474,6 +4512,11 @@ test_bpf_match(pcap_t *pcap, const char *str,
 static int
 test_bpf_filter_sanity(pcap_t *pcap)
 {
+	static const load_cbpf_program_t cbpf_program_loaders[] = {
+		load_cbpf_program_convert,
+		load_cbpf_program_direct,
+	};
+
 	const uint32_t plen = 100;
 	struct rte_mbuf mb, *m;
 	uint8_t tbuf[RTE_MBUF_DEFAULT_BUF_SIZE];
@@ -4500,15 +4543,17 @@ test_bpf_filter_sanity(pcap_t *pcap)
 		.dst_addr = rte_cpu_to_be_32(RTE_IPV4_BROADCAST),
 	};
 
-	if (test_bpf_match(pcap, "ip", m) != 0) {
-		printf("%s@%d: filter \"ip\" doesn't match test data\n",
-		       __func__, __LINE__);
-		return -1;
-	}
-	if (test_bpf_match(pcap, "not ip", m) == 0) {
-		printf("%s@%d: filter \"not ip\" does match test data\n",
-		       __func__, __LINE__);
-		return -1;
+	for (int li = 0; li != RTE_DIM(cbpf_program_loaders); ++li) {
+		if (test_bpf_match(pcap, "ip", m, cbpf_program_loaders[li]) != 0) {
+			printf("%s@%d: filter \"ip\" doesn't match test data\n",
+			       __func__, __LINE__);
+			return -1;
+		}
+		if (test_bpf_match(pcap, "not ip", m, cbpf_program_loaders[li]) == 0) {
+			printf("%s@%d: filter \"not ip\" does match test data\n",
+			       __func__, __LINE__);
+			return -1;
+		}
 	}
 
 	return 0;
@@ -4556,44 +4601,26 @@ static const char * const sample_filters[] = {
 };
 
 static int
-test_bpf_filter(pcap_t *pcap, const char *s)
+test_bpf_filter(pcap_t *pcap, const char *s, load_cbpf_program_t load_cbpf_program)
 {
 	struct bpf_program fcode;
-	struct rte_bpf_prm *prm = NULL;
-	struct rte_bpf *bpf = NULL;
+	struct rte_bpf *bpf;
 
 	if (pcap_compile(pcap, &fcode, s, 1, PCAP_NETMASK_UNKNOWN)) {
-		printf("%s@%d: pcap_compile('%s') failed: %s;\n",
+		printf("%s@%d: pcap_compile(\"%s\") failed: %s;\n",
 		       __func__, __LINE__, s, pcap_geterr(pcap));
 		return -1;
 	}
 
-	prm = rte_bpf_convert(&fcode);
-	if (prm == NULL) {
-		printf("%s@%d: bpf_convert('%s') failed,, error=%d(%s);\n",
-		       __func__, __LINE__, s, rte_errno, strerror(rte_errno));
-		goto error;
-	}
-
-	printf("bpf convert for \"%s\" produced:\n", s);
-	rte_bpf_dump(stdout, prm->ins, prm->nb_ins);
-
-	bpf = rte_bpf_load(prm);
+	bpf = load_cbpf_program(&fcode, s);
 	if (bpf == NULL) {
-		printf("%s@%d: failed to load bpf code, error=%d(%s);\n",
-			__func__, __LINE__, rte_errno, strerror(rte_errno));
-		goto error;
+		printf("%s@%d: failed to load cbpf program for \"%s\" , error=%d(%s);\n",
+			__func__, __LINE__, s, rte_errno, strerror(rte_errno));
+		test_bpf_dump(&fcode, NULL);
 	}
 
-error:
-	if (bpf)
-		rte_bpf_destroy(bpf);
-	else {
-		printf("%s \"%s\"\n", __func__, s);
-		test_bpf_dump(&fcode, prm);
-	}
+	rte_bpf_destroy(bpf);
 
-	rte_free(prm);
 	pcap_freecode(&fcode);
 	return (bpf == NULL) ? -1 : 0;
 }
@@ -4612,8 +4639,10 @@ test_bpf_convert(void)
 	}
 
 	rc = test_bpf_filter_sanity(pcap);
-	for (i = 0; i < RTE_DIM(sample_filters); i++)
-		rc |= test_bpf_filter(pcap, sample_filters[i]);
+	for (i = 0; i < RTE_DIM(sample_filters); i++) {
+		rc |= test_bpf_filter(pcap, sample_filters[i], load_cbpf_program_convert);
+		rc |= test_bpf_filter(pcap, sample_filters[i], load_cbpf_program_direct);
+	}
 
 	pcap_close(pcap);
 	return rc;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 06/11] bpf: support loading ELF files from memory
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Introduce new ELF origin RTE_BPF_ORIGIN_ELF_MEMORY allowing one to
specify data area containing ELF image.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 lib/bpf/bpf_impl.h     |  5 +++++
 lib/bpf/bpf_load.c     |  4 ++++
 lib/bpf/bpf_load_elf.c | 40 +++++++++++++++++++++++++++++++++++++++-
 lib/bpf/rte_bpf.h      |  6 ++++++
 4 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/lib/bpf/bpf_impl.h b/lib/bpf/bpf_impl.h
index 92d03583d977..14ad772d4beb 100644
--- a/lib/bpf/bpf_impl.h
+++ b/lib/bpf/bpf_impl.h
@@ -27,6 +27,7 @@ struct __rte_bpf_load {
 	/* Loading ELF and applying relocations. */
 	int elf_fd;  /* ELF fd, must be negative (not zero) by default. */
 	void *elf;  /* Using void to avoid dependency on libelf. */
+	const char *elf_section;
 
 	/* Value we are going to return, if any. */
 	struct rte_bpf *bpf;
@@ -53,6 +54,10 @@ __rte_bpf_load_elf_cleanup(struct __rte_bpf_load *load);
 int
 __rte_bpf_load_elf_file(struct __rte_bpf_load *load);
 
+/* Open the ELF memory image. */
+int
+__rte_bpf_load_elf_memory(struct __rte_bpf_load *load);
+
 /* Get code from ELF and apply relocations to it. */
 int
 __rte_bpf_load_elf_code(struct __rte_bpf_load *load);
diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c
index e3265e97ff7f..e406211e0e47 100644
--- a/lib/bpf/bpf_load.c
+++ b/lib/bpf/bpf_load.c
@@ -247,6 +247,10 @@ load_try(struct __rte_bpf_load *load, const struct rte_bpf_prm_ex *app_prm)
 		rc = rc < 0 ? rc : __rte_bpf_load_elf_file(load);
 		rc = rc < 0 ? rc : __rte_bpf_load_elf_code(load);
 		break;
+	case RTE_BPF_ORIGIN_ELF_MEMORY:
+		rc = rc < 0 ? rc : __rte_bpf_load_elf_memory(load);
+		rc = rc < 0 ? rc : __rte_bpf_load_elf_code(load);
+		break;
 	default:
 		rc = rc < 0 ? rc : -EINVAL;
 	}
diff --git a/lib/bpf/bpf_load_elf.c b/lib/bpf/bpf_load_elf.c
index 4ae7492351ae..80443cb63a61 100644
--- a/lib/bpf/bpf_load_elf.c
+++ b/lib/bpf/bpf_load_elf.c
@@ -310,6 +310,36 @@ __rte_bpf_load_elf_file(struct __rte_bpf_load *load)
 		return -EINVAL;
 	}
 
+	load->elf_section = prm->elf_file.section;
+
+	return 0;
+}
+
+int
+__rte_bpf_load_elf_memory(struct __rte_bpf_load *load)
+{
+	const struct rte_bpf_prm_ex *const prm = &load->prm;
+
+	RTE_ASSERT(prm->origin == RTE_BPF_ORIGIN_ELF_MEMORY);
+
+	if (prm->elf_memory.data == NULL || prm->elf_memory.section == NULL)
+		return -EINVAL;
+
+	if (elf_version(EV_CURRENT) == EV_NONE)
+		return -ENOTSUP;
+
+	load->elf = elf_memory(
+		/* Cast away const, we are not going to modify the ELF image. */
+		(char *)(uintptr_t)prm->elf_memory.data, prm->elf_memory.size);
+	if (load->elf == NULL) {
+		const int rc = elf_errno();
+		RTE_BPF_LOG_FUNC_LINE(ERR, "error %d opening ELF image: %s",
+			rc, elf_errmsg(rc));
+		return -EINVAL;
+	}
+
+	load->elf_section = prm->elf_memory.section;
+
 	return 0;
 }
 
@@ -321,7 +351,7 @@ __rte_bpf_load_elf_code(struct __rte_bpf_load *load)
 	size_t sidx;
 	int rc;
 
-	rc = find_elf_code(load->elf, prm->elf_file.section, &sd, &sidx);
+	rc = find_elf_code(load->elf, load->elf_section, &sd, &sidx);
 	if (rc < 0)
 		return rc;
 
@@ -353,6 +383,14 @@ __rte_bpf_load_elf_file(struct __rte_bpf_load *load)
 	return -ENOTSUP;
 }
 
+int
+__rte_bpf_load_elf_memory(struct __rte_bpf_load *load)
+{
+	RTE_SET_USED(load);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libelf installed");
+	return -ENOTSUP;
+}
+
 int
 __rte_bpf_load_elf_code(struct __rte_bpf_load *load)
 {
diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h
index da2bdea7e0a8..413ccf049755 100644
--- a/lib/bpf/rte_bpf.h
+++ b/lib/bpf/rte_bpf.h
@@ -97,6 +97,7 @@ enum rte_bpf_origin {
 	RTE_BPF_ORIGIN_RAW,		/**< code loaded from raw array */
 	RTE_BPF_ORIGIN_CBPF,		/**< code converted from cbpf */
 	RTE_BPF_ORIGIN_ELF_FILE,	/**< code loaded from elf_file */
+	RTE_BPF_ORIGIN_ELF_MEMORY,	/**< code loaded from elf_memory */
 };
 
 struct bpf_insn;
@@ -127,6 +128,11 @@ struct rte_bpf_prm_ex {
 			const char *path;  /**< path to the ELF file */
 			const char *section;  /**< ELF section with the code */
 		} elf_file;
+		struct {
+			const void *data;  /**< pointer to the ELF image */
+			size_t size;  /**< size of the ELF image */
+			const char *section;  /**< ELF section with the code */
+		} elf_memory;
 	};
 
 	const struct rte_bpf_xsym *xsym;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 05/11] bpf: support rte_bpf_prm_ex with port callbacks
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Introduce new functions to install an already loaded BPF program into RX
or TX port/queue, since previous API was tied to rte_bpf_prm.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 lib/bpf/bpf_pkt.c        | 65 ++++++++++++++++++++++++++++++----------
 lib/bpf/rte_bpf_ethdev.h | 54 +++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+), 15 deletions(-)

diff --git a/lib/bpf/bpf_pkt.c b/lib/bpf/bpf_pkt.c
index 5007f6aef57d..87065e939f31 100644
--- a/lib/bpf/bpf_pkt.c
+++ b/lib/bpf/bpf_pkt.c
@@ -490,13 +490,11 @@ rte_bpf_eth_tx_unload(uint16_t port, uint16_t queue)
 }
 
 static int
-bpf_eth_elf_load(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue,
-	const struct rte_bpf_prm *prm, const char *fname, const char *sname,
-	uint32_t flags)
+bpf_eth_elf_install(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue,
+	struct rte_bpf *bpf, uint32_t flags)
 {
 	int32_t rc;
 	struct bpf_eth_cbi *bc;
-	struct rte_bpf *bpf;
 	rte_rx_callback_fn frx;
 	rte_tx_callback_fn ftx;
 	struct rte_bpf_jit jit;
@@ -504,14 +502,17 @@ bpf_eth_elf_load(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue,
 	frx = NULL;
 	ftx = NULL;
 
-	if (prm == NULL || rte_eth_dev_is_valid_port(port) == 0 ||
+	if (bpf == NULL || rte_eth_dev_is_valid_port(port) == 0 ||
 			queue >= RTE_MAX_QUEUES_PER_PORT)
 		return -EINVAL;
 
+	if (bpf->prm.nb_prog_arg != 1)
+		return -EINVAL;
+
 	if (cbh->type == BPF_ETH_RX)
-		frx = select_rx_callback(prm->prog_arg.type, flags);
+		frx = select_rx_callback(bpf->prm.prog_arg[0].type, flags);
 	else
-		ftx = select_tx_callback(prm->prog_arg.type, flags);
+		ftx = select_tx_callback(bpf->prm.prog_arg[0].type, flags);
 
 	if (frx == NULL && ftx == NULL) {
 		RTE_BPF_LOG_LINE(ERR, "%s(%u, %u): no callback selected;",
@@ -519,16 +520,11 @@ bpf_eth_elf_load(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue,
 		return -EINVAL;
 	}
 
-	bpf = rte_bpf_elf_load(prm, fname, sname);
-	if (bpf == NULL)
-		return -rte_errno;
-
 	rte_bpf_get_jit(bpf, &jit);
 
 	if ((flags & RTE_BPF_ETH_F_JIT) != 0 && jit.func == NULL) {
 		RTE_BPF_LOG_LINE(ERR, "%s(%u, %u): no JIT generated;",
 			__func__, port, queue);
-		rte_bpf_destroy(bpf);
 		return -ENOTSUP;
 	}
 
@@ -551,7 +547,6 @@ bpf_eth_elf_load(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue,
 
 	if (bc->cb == NULL) {
 		rc = -rte_errno;
-		rte_bpf_destroy(bpf);
 		bpf_eth_cbi_cleanup(bc);
 	} else
 		rc = 0;
@@ -564,13 +559,33 @@ int
 rte_bpf_eth_rx_elf_load(uint16_t port, uint16_t queue,
 	const struct rte_bpf_prm *prm, const char *fname, const char *sname,
 	uint32_t flags)
+{
+	struct rte_bpf *bpf;
+	int32_t rc;
+
+	bpf = rte_bpf_elf_load(prm, fname, sname);
+	if (bpf == NULL)
+		return -rte_errno;
+
+	rc = rte_bpf_eth_rx_install(port, queue, bpf, flags);
+
+	if (rc < 0)
+		rte_bpf_destroy(bpf);
+
+	return rc;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_eth_rx_install, 26.11)
+int
+rte_bpf_eth_rx_install(uint16_t port, uint16_t queue, struct rte_bpf *bpf,
+	uint32_t flags)
 {
 	int32_t rc;
 	struct bpf_eth_cbh *cbh;
 
 	cbh = &rx_cbh;
 	rte_spinlock_lock(&cbh->lock);
-	rc = bpf_eth_elf_load(cbh, port, queue, prm, fname, sname, flags);
+	rc = bpf_eth_elf_install(cbh, port, queue, bpf, flags);
 	rte_spinlock_unlock(&cbh->lock);
 
 	return rc;
@@ -581,13 +596,33 @@ int
 rte_bpf_eth_tx_elf_load(uint16_t port, uint16_t queue,
 	const struct rte_bpf_prm *prm, const char *fname, const char *sname,
 	uint32_t flags)
+{
+	struct rte_bpf *bpf;
+	int32_t rc;
+
+	bpf = rte_bpf_elf_load(prm, fname, sname);
+	if (bpf == NULL)
+		return -rte_errno;
+
+	rc = rte_bpf_eth_tx_install(port, queue, bpf, flags);
+
+	if (rc < 0)
+		rte_bpf_destroy(bpf);
+
+	return rc;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_eth_tx_install, 26.11)
+int
+rte_bpf_eth_tx_install(uint16_t port, uint16_t queue, struct rte_bpf *bpf,
+	uint32_t flags)
 {
 	int32_t rc;
 	struct bpf_eth_cbh *cbh;
 
 	cbh = &tx_cbh;
 	rte_spinlock_lock(&cbh->lock);
-	rc = bpf_eth_elf_load(cbh, port, queue, prm, fname, sname, flags);
+	rc = bpf_eth_elf_install(cbh, port, queue, bpf, flags);
 	rte_spinlock_unlock(&cbh->lock);
 
 	return rc;
diff --git a/lib/bpf/rte_bpf_ethdev.h b/lib/bpf/rte_bpf_ethdev.h
index cab8e9e3887a..5f605ec34d56 100644
--- a/lib/bpf/rte_bpf_ethdev.h
+++ b/lib/bpf/rte_bpf_ethdev.h
@@ -109,6 +109,60 @@ rte_bpf_eth_tx_elf_load(uint16_t port, uint16_t queue,
 	const struct rte_bpf_prm *prm, const char *fname, const char *sname,
 	uint32_t flags);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: This API may change, or be removed, without prior notice.
+ *
+ * Install callback to execute specified BPF program on given RX port/queue.
+ *
+ * On success the ownership of the program passes to the library,
+ * rte_bpf_eth_unload must be used to unload it, and rte_bpf_destroy must no
+ * longer be called.
+ *
+ * @param port
+ *   The identifier of the ethernet port
+ * @param queue
+ *   The identifier of the RX queue on the given port
+ * @param bpf
+ *   BPF program
+ * @param flags
+ *   Flags that define expected behavior of the loaded filter
+ *   (i.e. jited/non-jited version to use).
+ * @return
+ *   Zero on successful completion or negative error code otherwise.
+ */
+__rte_experimental
+int
+rte_bpf_eth_rx_install(uint16_t port, uint16_t queue, struct rte_bpf *bpf,
+	uint32_t flags);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: This API may change, or be removed, without prior notice.
+ *
+ * Install callback to execute specified BPF program on given TX port/queue.
+ *
+ * On success the ownership of the program passes to the library,
+ * rte_bpf_eth_unload must be used to unload it, and rte_bpf_destroy must no
+ * longer be called.
+ *
+ * @param port
+ *   The identifier of the ethernet port
+ * @param queue
+ *   The identifier of the TX queue on the given port
+ * @param bpf
+ *   BPF program
+ * @param flags
+ *   Flags that define expected behavior of the loaded filter
+ *   (i.e. jited/non-jited version to use).
+ * @return
+ *   Zero on successful completion or negative error code otherwise.
+ */
+__rte_experimental
+int
+rte_bpf_eth_tx_install(uint16_t port, uint16_t queue, struct rte_bpf *bpf,
+	uint32_t flags);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 02/11] bpf: introduce extensible load API
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev, Wathsala Vithanage; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Introduce new BPF load parameters struct rte_bpf_prm_ex that can be
extended without breaking backward or forward compatibility. Introduce
new function rte_bpf_load_ex consolidating in one code path loading from
both ELF file and raw memory image, with possibility to add more options
in the future.

Some changes in code layout and sequence:
* Both old APIs now only forwarding calls to a new single entry point.
* There is now a centralized cleanup point for all temporary resources
  created during the load process.
* External symbols (xsyms) are now checked for validity just after the
  load started, not after they were already used for relocation.
* File bpf_load_elf.c now only handles opening ELF file and providing
  patched instruction array to the load process. These are left as two
  separate functions to support other ELF sources like memory image in
  the future.
* Function stubs for the case libelf is not available are moved to
  bpf_load_elf.c to make keeping track of them easier (forgetting to
  update stubs is a common problem).

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 lib/bpf/bpf_exec.c      |  10 +--
 lib/bpf/bpf_impl.h      |  32 ++++++-
 lib/bpf/bpf_jit_arm64.c |  12 +--
 lib/bpf/bpf_jit_x86.c   |   8 +-
 lib/bpf/bpf_load.c      | 195 +++++++++++++++++++++++++++++++++++-----
 lib/bpf/bpf_load_elf.c  | 151 ++++++++++++++++++-------------
 lib/bpf/bpf_stub.c      |  17 ----
 lib/bpf/bpf_validate.c  |  32 +++----
 lib/bpf/meson.build     |   4 +-
 lib/bpf/rte_bpf.h       |  68 +++++++++++++-
 10 files changed, 392 insertions(+), 137 deletions(-)

diff --git a/lib/bpf/bpf_exec.c b/lib/bpf/bpf_exec.c
index 18013753b147..e4668ba10b64 100644
--- a/lib/bpf/bpf_exec.c
+++ b/lib/bpf/bpf_exec.c
@@ -47,7 +47,7 @@
 		RTE_BPF_LOG_LINE(ERR, \
 			"%s(%p): division by 0 at pc: %#zx;", \
 			__func__, bpf, \
-			(uintptr_t)(ins) - (uintptr_t)(bpf)->prm.ins); \
+			(uintptr_t)(ins) - (uintptr_t)(bpf)->prm.raw.ins); \
 		return 0; \
 	} \
 } while (0)
@@ -81,7 +81,7 @@
 		RTE_BPF_LOG_LINE(ERR, \
 			"%s(%p): unsupported atomic operation at pc: %#zx;", \
 			__func__, bpf, \
-			(uintptr_t)(ins) - (uintptr_t)(bpf)->prm.ins); \
+			(uintptr_t)(ins) - (uintptr_t)(bpf)->prm.raw.ins); \
 		return 0; \
 	} \
 } while (0)
@@ -157,7 +157,7 @@ bpf_ld_mbuf(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM],
 		RTE_BPF_LOG_LINE(DEBUG, "%s(bpf=%p, mbuf=%p, ofs=%u, len=%u): "
 			"load beyond packet boundary at pc: %#zx;",
 			__func__, bpf, mb, off, len,
-			(uintptr_t)(ins) - (uintptr_t)(bpf)->prm.ins);
+			(uintptr_t)(ins) - (uintptr_t)(bpf)->prm.raw.ins);
 	return p;
 }
 
@@ -166,7 +166,7 @@ bpf_exec(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM])
 {
 	const struct ebpf_insn *ins;
 
-	for (ins = bpf->prm.ins; ; ins++) {
+	for (ins = bpf->prm.raw.ins; ; ins++) {
 		switch (ins->code) {
 		/* 32 bit ALU IMM operations */
 		case (BPF_ALU | BPF_ADD | BPF_K):
@@ -483,7 +483,7 @@ bpf_exec(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM])
 			RTE_BPF_LOG_LINE(ERR,
 				"%s(%p): invalid opcode %#x at pc: %#zx;",
 				__func__, bpf, ins->code,
-				(uintptr_t)ins - (uintptr_t)bpf->prm.ins);
+				(uintptr_t)ins - (uintptr_t)bpf->prm.raw.ins);
 			return 0;
 		}
 	}
diff --git a/lib/bpf/bpf_impl.h b/lib/bpf/bpf_impl.h
index fb5ec3c4d65f..1cee109bc98a 100644
--- a/lib/bpf/bpf_impl.h
+++ b/lib/bpf/bpf_impl.h
@@ -11,17 +11,45 @@
 #define MAX_BPF_STACK_SIZE	0x200
 
 struct rte_bpf {
-	struct rte_bpf_prm prm;
+	struct rte_bpf_prm_ex prm;
 	struct rte_bpf_jit jit;
 	size_t sz;
 	uint32_t stack_sz;
 };
 
+/* Temporary copies etc. used by the load process. */
+struct __rte_bpf_load {
+	struct rte_bpf_prm_ex prm;
+
+	/* Loading ELF and applying relocations. */
+	int elf_fd;  /* ELF fd, must be negative (not zero) by default. */
+	void *elf;  /* Using void to avoid dependency on libelf. */
+
+	/* Value we are going to return, if any. */
+	struct rte_bpf *bpf;
+};
+
 /*
  * Use '__rte' prefix for non-static internal functions
  * to avoid potential name conflict with other libraries.
  */
-int __rte_bpf_validate(struct rte_bpf *bpf);
+
+/* Free temporary resources created by opening ELF. */
+void
+__rte_bpf_load_elf_cleanup(struct __rte_bpf_load *load);
+
+/* Open the ELF file. */
+int
+__rte_bpf_load_elf_file(struct __rte_bpf_load *load);
+
+/* Get code from ELF and apply relocations to it. */
+int
+__rte_bpf_load_elf_code(struct __rte_bpf_load *load);
+
+/* Validate final BPF code and calculate stack size. */
+int
+__rte_bpf_validate(const struct rte_bpf_prm_ex *prm, uint32_t *stack_sz);
+
 int __rte_bpf_jit(struct rte_bpf *bpf);
 int __rte_bpf_jit_x86(struct rte_bpf *bpf);
 int __rte_bpf_jit_arm64(struct rte_bpf *bpf);
diff --git a/lib/bpf/bpf_jit_arm64.c b/lib/bpf/bpf_jit_arm64.c
index 4bbb97da1b89..9e5e142c13ba 100644
--- a/lib/bpf/bpf_jit_arm64.c
+++ b/lib/bpf/bpf_jit_arm64.c
@@ -111,12 +111,12 @@ jump_offset_init(struct a64_jit_ctx *ctx, struct rte_bpf *bpf)
 {
 	uint32_t i;
 
-	ctx->map = malloc(bpf->prm.nb_ins * sizeof(ctx->map[0]));
+	ctx->map = malloc(bpf->prm.raw.nb_ins * sizeof(ctx->map[0]));
 	if (ctx->map == NULL)
 		return -ENOMEM;
 
 	/* Fill with fake offsets */
-	for (i = 0; i != bpf->prm.nb_ins; i++) {
+	for (i = 0; i != bpf->prm.raw.nb_ins; i++) {
 		ctx->map[i].off = INT32_MAX;
 		ctx->map[i].off_to_b = 0;
 	}
@@ -1130,8 +1130,8 @@ check_program_has_call(struct a64_jit_ctx *ctx, struct rte_bpf *bpf)
 	uint8_t op;
 	uint32_t i;
 
-	for (i = 0; i != bpf->prm.nb_ins; i++) {
-		ins = bpf->prm.ins + i;
+	for (i = 0; i != bpf->prm.raw.nb_ins; i++) {
+		ins = bpf->prm.raw.ins + i;
 		op = ins->code;
 
 		switch (op) {
@@ -1168,10 +1168,10 @@ emit(struct a64_jit_ctx *ctx, struct rte_bpf *bpf)
 
 	emit_prologue(ctx);
 
-	for (i = 0; i != bpf->prm.nb_ins; i++) {
+	for (i = 0; i != bpf->prm.raw.nb_ins; i++) {
 
 		jump_offset_update(ctx, i);
-		ins = bpf->prm.ins + i;
+		ins = bpf->prm.raw.ins + i;
 		op = ins->code;
 		off = ins->off;
 		imm = ins->imm;
diff --git a/lib/bpf/bpf_jit_x86.c b/lib/bpf/bpf_jit_x86.c
index 88b1b5aeab1a..6f4235d43499 100644
--- a/lib/bpf/bpf_jit_x86.c
+++ b/lib/bpf/bpf_jit_x86.c
@@ -1324,12 +1324,12 @@ emit(struct bpf_jit_state *st, const struct rte_bpf *bpf)
 
 	emit_prolog(st, bpf->stack_sz);
 
-	for (i = 0; i != bpf->prm.nb_ins; i++) {
+	for (i = 0; i != bpf->prm.raw.nb_ins; i++) {
 
 		st->idx = i;
 		st->off[i] = st->sz;
 
-		ins = bpf->prm.ins + i;
+		ins = bpf->prm.raw.ins + i;
 
 		dr = ebpf2x86[ins->dst_reg];
 		sr = ebpf2x86[ins->src_reg];
@@ -1532,13 +1532,13 @@ __rte_bpf_jit_x86(struct rte_bpf *bpf)
 
 	/* init state */
 	memset(&st, 0, sizeof(st));
-	st.off = malloc(bpf->prm.nb_ins * sizeof(st.off[0]));
+	st.off = malloc(bpf->prm.raw.nb_ins * sizeof(st.off[0]));
 	if (st.off == NULL)
 		return -ENOMEM;
 
 	/* fill with fake offsets */
 	st.exit.off = INT32_MAX;
-	for (i = 0; i != bpf->prm.nb_ins; i++)
+	for (i = 0; i != bpf->prm.raw.nb_ins; i++)
 		st.off[i] = INT32_MAX;
 
 	/*
diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c
index b8a0426fe2ed..2d9e503d1f7e 100644
--- a/lib/bpf/bpf_load.c
+++ b/lib/bpf/bpf_load.c
@@ -14,14 +14,14 @@
 #include "bpf_impl.h"
 
 static struct rte_bpf *
-bpf_load(const struct rte_bpf_prm *prm)
+bpf_load(const struct rte_bpf_prm_ex *prm)
 {
 	uint8_t *buf;
 	struct rte_bpf *bpf;
 	size_t sz, bsz, insz, xsz;
 
 	xsz =  prm->nb_xsym * sizeof(prm->xsym[0]);
-	insz = prm->nb_ins * sizeof(prm->ins[0]);
+	insz = prm->raw.nb_ins * sizeof(prm->raw.ins[0]);
 	bsz = sizeof(bpf[0]);
 	sz = insz + xsz + bsz;
 
@@ -37,10 +37,10 @@ bpf_load(const struct rte_bpf_prm *prm)
 
 	if (xsz > 0)
 		memcpy(buf + bsz, prm->xsym, xsz);
-	memcpy(buf + bsz + xsz, prm->ins, insz);
+	memcpy(buf + bsz + xsz, prm->raw.ins, insz);
 
 	bpf->prm.xsym = (void *)(buf + bsz);
-	bpf->prm.ins = (void *)(buf + bsz + xsz);
+	bpf->prm.raw.ins = (void *)(buf + bsz + xsz);
 
 	return bpf;
 }
@@ -80,37 +80,44 @@ bpf_check_xsym(const struct rte_bpf_xsym *xsym)
 	return 0;
 }
 
-RTE_EXPORT_SYMBOL(rte_bpf_load)
-struct rte_bpf *
-rte_bpf_load(const struct rte_bpf_prm *prm)
+static int
+bpf_check_xsyms(const struct rte_bpf_xsym *xsym, uint32_t nb_xsym)
 {
-	struct rte_bpf *bpf;
 	int32_t rc;
 	uint32_t i;
 
-	if (prm == NULL || prm->ins == NULL || prm->nb_ins == 0 ||
-			(prm->nb_xsym != 0 && prm->xsym == NULL)) {
-		rte_errno = EINVAL;
-		return NULL;
-	}
+	if (nb_xsym != 0 && xsym == NULL)
+		return -EINVAL;
 
 	rc = 0;
-	for (i = 0; i != prm->nb_xsym && rc == 0; i++)
-		rc = bpf_check_xsym(prm->xsym + i);
+	for (i = 0; i != nb_xsym && rc == 0; i++)
+		rc = bpf_check_xsym(xsym + i);
 
 	if (rc != 0) {
-		rte_errno = -rc;
 		RTE_BPF_LOG_FUNC_LINE(ERR, "%d-th xsym is invalid", i);
-		return NULL;
+		return rc;
 	}
 
+	return 0;
+}
+
+static int
+bpf_load_raw(struct __rte_bpf_load *load)
+{
+	const struct rte_bpf_prm_ex *const prm = &load->prm;
+	struct rte_bpf *bpf;
+	int32_t rc;
+
+	RTE_ASSERT(prm->origin == RTE_BPF_ORIGIN_RAW);
+
+	if (prm->raw.ins == NULL || prm->raw.nb_ins == 0)
+		return -EINVAL;
+
 	bpf = bpf_load(prm);
-	if (bpf == NULL) {
-		rte_errno = ENOMEM;
-		return NULL;
-	}
+	if (bpf == NULL)
+		return -ENOMEM;
 
-	rc = __rte_bpf_validate(bpf);
+	rc = __rte_bpf_validate(&load->prm, &bpf->stack_sz);
 	if (rc == 0) {
 		__rte_bpf_jit(bpf);
 		if (mprotect(bpf, bpf->sz, PROT_READ) != 0)
@@ -119,9 +126,151 @@ rte_bpf_load(const struct rte_bpf_prm *prm)
 
 	if (rc != 0) {
 		rte_bpf_destroy(bpf);
+		return rc;
+	}
+
+	load->bpf = bpf;
+	return 0;
+}
+
+RTE_EXPORT_SYMBOL(rte_bpf_load)
+struct rte_bpf *
+rte_bpf_load(const struct rte_bpf_prm *prm)
+{
+	if (prm == NULL) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	return rte_bpf_load_ex(&(struct rte_bpf_prm_ex){
+			.sz = sizeof(struct rte_bpf_prm_ex),
+			.origin = RTE_BPF_ORIGIN_RAW,
+			.raw.ins = prm->ins,
+			.raw.nb_ins = prm->nb_ins,
+			.xsym = prm->xsym,
+			.nb_xsym = prm->nb_xsym,
+			.prog_arg = prm->prog_arg,
+		});
+}
+
+RTE_EXPORT_SYMBOL(rte_bpf_elf_load)
+struct rte_bpf *
+rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,
+	const char *sname)
+{
+	if (prm == NULL) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	return rte_bpf_load_ex(&(struct rte_bpf_prm_ex){
+			.sz = sizeof(struct rte_bpf_prm_ex),
+			.origin = RTE_BPF_ORIGIN_ELF_FILE,
+			.elf_file.path = fname,
+			.elf_file.section = sname,
+			.xsym = prm->xsym,
+			.nb_xsym = prm->nb_xsym,
+			.prog_arg = prm->prog_arg,
+		});
+}
+
+/*
+ * Check extensible opts for invalid size or non-zero unsupported members.
+ *
+ * This code provides forward compatibility with applications compiled against
+ * newer version of this library. `opts_sz` is the size of struct `opts` in the
+ * version used for compiling the application, read from the member `sz`;
+ * `type_sz` is the size of same struct in the version used for compiling the
+ * library.
+ *
+ * If new fields were added to the struct in the application version, `opts_sz`
+ * will be greater than `type_sz`. In this case we are making sure all bytes we
+ * don't know how to interpret are zeroes, that is any new features that are
+ * there are not being used.
+ *
+ * This function can be used to check any struct following this convention.
+ */
+static bool
+opts_valid(const void *opts, size_t opts_sz, size_t type_sz)
+{
+	if (opts == NULL)
+		return true;
+
+	if (opts_sz < sizeof(opts_sz))
+		/* Size of the struct is too small even for sz member. */
+		return false;
+
+	/* Verify that all extra bytes are zeroed. */
+	for (size_t offset = type_sz; offset < opts_sz; ++offset)
+		if (((const char *)opts)[offset] != 0)
+			return false;
+
+	return true;
+}
+
+static int
+load_try(struct __rte_bpf_load *load, const struct rte_bpf_prm_ex *app_prm)
+{
+	int rc;
+
+	if (app_prm == NULL || !opts_valid(app_prm, app_prm->sz, sizeof(load->prm)))
+		return -EINVAL;
+
+	/*
+	 * Convert extensible prm of application size to the size known to us.
+	 *
+	 * This code provides compatibility with applications compiled against
+	 * different version of this library. `app_prm->sz` is the size of
+	 * struct `rte_bpf_prm_ex` in the version used for compiling the
+	 * application; `sizeof(load->prm)` is the size of the same struct in
+	 * the version used for compiling the library.
+	 *
+	 * We are copying only the fields known to the application and leave
+	 * the rest filled with zeroes. Any features that not known to the
+	 * application will have backward-compatible default behaviour.
+	 */
+	memcpy(&load->prm, app_prm, RTE_MIN(app_prm->sz, sizeof(load->prm)));
+	load->prm.sz = sizeof(load->prm);
+
+	rc = bpf_check_xsyms(load->prm.xsym, load->prm.nb_xsym);
+
+	/* Convert prm origin to raw unless it already is. */
+	switch (load->prm.origin) {
+	case RTE_BPF_ORIGIN_RAW:
+		break;
+	case RTE_BPF_ORIGIN_ELF_FILE:
+		rc = rc < 0 ? rc : __rte_bpf_load_elf_file(load);
+		rc = rc < 0 ? rc : __rte_bpf_load_elf_code(load);
+		break;
+	default:
+		rc = rc < 0 ? rc : -EINVAL;
+	}
+
+	/* Now that it is raw load it as such. */
+	rc = rc < 0 ? rc : bpf_load_raw(load);
+
+	return rc;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_load_ex, 26.11)
+struct rte_bpf *
+rte_bpf_load_ex(const struct rte_bpf_prm_ex *prm)
+{
+	struct __rte_bpf_load load = { .elf_fd = -1 };
+
+	const int rc = load_try(&load, prm);
+
+	__rte_bpf_load_elf_cleanup(&load);
+
+	RTE_ASSERT((rc < 0) == (load.bpf == NULL));
+
+	if (rc < 0) {
+		RTE_BPF_LOG_FUNC_LINE(ERR, "failed, error code: %d", -rc);
 		rte_errno = -rc;
 		return NULL;
 	}
 
-	return bpf;
+	RTE_BPF_LOG_FUNC_LINE(INFO, "successfully creates %p(jit={.func=%p,.sz=%zu});",
+		load.bpf, load.bpf->jit.raw, load.bpf->jit.sz);
+	return load.bpf;
 }
diff --git a/lib/bpf/bpf_load_elf.c b/lib/bpf/bpf_load_elf.c
index 2390823cbf30..4ae7492351ae 100644
--- a/lib/bpf/bpf_load_elf.c
+++ b/lib/bpf/bpf_load_elf.c
@@ -2,6 +2,13 @@
  * Copyright(c) 2018 Intel Corporation
  */
 
+#include "bpf_impl.h"
+
+#include <errno.h>
+
+#ifdef RTE_LIBRTE_BPF_ELF
+
+#include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
@@ -26,8 +33,6 @@
 #include <rte_byteorder.h>
 #include <rte_errno.h>
 
-#include "bpf_impl.h"
-
 /* To overcome compatibility issue */
 #ifndef EM_BPF
 #define	EM_BPF	247
@@ -56,7 +61,7 @@ bpf_find_xsym(const char *sn, enum rte_bpf_xtype type,
  */
 static int
 resolve_xsym(const char *sn, size_t ofs, struct ebpf_insn *ins, size_t ins_sz,
-	const struct rte_bpf_prm *prm)
+	const struct rte_bpf_prm_ex *prm)
 {
 	uint32_t idx, fidx;
 	enum rte_bpf_xtype type;
@@ -183,7 +188,7 @@ find_elf_code(Elf *elf, const char *section, Elf_Data **psd, size_t *pidx)
  */
 static int
 process_reloc(Elf *elf, size_t sym_idx, Elf64_Rel *re, size_t re_sz,
-	struct ebpf_insn *ins, size_t ins_sz, const struct rte_bpf_prm *prm)
+	struct ebpf_insn *ins, size_t ins_sz, const struct rte_bpf_prm_ex *prm)
 {
 	int32_t rc;
 	uint32_t i, n;
@@ -232,8 +237,8 @@ process_reloc(Elf *elf, size_t sym_idx, Elf64_Rel *re, size_t re_sz,
  * and update bpf code.
  */
 static int
-elf_reloc_code(Elf *elf, Elf_Data *ed, size_t sidx,
-	const struct rte_bpf_prm *prm)
+elf_reloc_code(Elf *elf, struct ebpf_insn *ins, size_t ins_sz, size_t sidx,
+	const struct rte_bpf_prm_ex *prm)
 {
 	Elf64_Rel *re;
 	Elf_Scn *sc;
@@ -256,7 +261,7 @@ elf_reloc_code(Elf *elf, Elf_Data *ed, size_t sidx,
 					sd->d_size % sizeof(re[0]) != 0)
 				return -EINVAL;
 			rc = process_reloc(elf, sh->sh_link,
-				sd->d_buf, sd->d_size, ed->d_buf, ed->d_size,
+				sd->d_buf, sd->d_size, ins, ins_sz,
 				prm);
 		}
 	}
@@ -264,72 +269,96 @@ elf_reloc_code(Elf *elf, Elf_Data *ed, size_t sidx,
 	return rc;
 }
 
-static struct rte_bpf *
-bpf_load_elf(const struct rte_bpf_prm *prm, int32_t fd, const char *section)
+void
+__rte_bpf_load_elf_cleanup(struct __rte_bpf_load *load)
 {
-	Elf *elf;
-	Elf_Data *sd;
-	size_t sidx;
-	int32_t rc;
-	struct rte_bpf *bpf;
-	struct rte_bpf_prm np;
+	elf_end(load->elf);
 
-	elf_version(EV_CURRENT);
-	elf = elf_begin(fd, ELF_C_READ, NULL);
+	if (load->elf_fd >= 0 && close(load->elf_fd) < 0) {
+		const int close_errno = errno;
+		RTE_BPF_LOG_FUNC_LINE(ERR, "error %d closing: %s",
+			close_errno, strerror(close_errno));
+	}
+}
 
-	rc = find_elf_code(elf, section, &sd, &sidx);
-	if (rc == 0)
-		rc = elf_reloc_code(elf, sd, sidx, prm);
+int
+__rte_bpf_load_elf_file(struct __rte_bpf_load *load)
+{
+	const struct rte_bpf_prm_ex *const prm = &load->prm;
 
-	if (rc == 0) {
-		np = prm[0];
-		np.ins = sd->d_buf;
-		np.nb_ins = sd->d_size / sizeof(struct ebpf_insn);
-		bpf = rte_bpf_load(&np);
-	} else {
-		bpf = NULL;
-		rte_errno = -rc;
+	RTE_ASSERT(prm->origin == RTE_BPF_ORIGIN_ELF_FILE);
+
+	if (prm->elf_file.path == NULL || prm->elf_file.section == NULL)
+		return -EINVAL;
+
+	if (elf_version(EV_CURRENT) == EV_NONE)
+		return -ENOTSUP;
+
+	load->elf_fd = open(prm->elf_file.path, O_RDONLY);
+	if (load->elf_fd < 0) {
+		const int open_errno = errno;
+		RTE_BPF_LOG_FUNC_LINE(ERR, "error %d opening \"%s\": %s",
+			open_errno, prm->elf_file.path, strerror(open_errno));
+		return -open_errno;
+	}
+
+	load->elf = elf_begin(load->elf_fd, ELF_C_READ, NULL);
+	if (load->elf == NULL) {
+		const int rc = elf_errno();
+		RTE_BPF_LOG_FUNC_LINE(ERR, "error %d opening ELF \"%s\": %s",
+			rc, prm->elf_file.path, elf_errmsg(rc));
+		return -EINVAL;
 	}
 
-	elf_end(elf);
-	return bpf;
+	return 0;
 }
 
-RTE_EXPORT_SYMBOL(rte_bpf_elf_load)
-struct rte_bpf *
-rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,
-	const char *sname)
+int
+__rte_bpf_load_elf_code(struct __rte_bpf_load *load)
 {
-	int32_t fd, rc;
-	struct rte_bpf *bpf;
+	struct rte_bpf_prm_ex *const prm = &load->prm;
+	Elf_Data *sd;
+	size_t sidx;
+	int rc;
 
-	if (prm == NULL || fname == NULL || sname == NULL) {
-		rte_errno = EINVAL;
-		return NULL;
-	}
+	rc = find_elf_code(load->elf, prm->elf_file.section, &sd, &sidx);
+	if (rc < 0)
+		return rc;
 
-	fd = open(fname, O_RDONLY);
-	if (fd < 0) {
-		rc = errno;
-		RTE_BPF_LOG_LINE(ERR, "%s(%s) error code: %d(%s)",
-			__func__, fname, rc, strerror(rc));
-		rte_errno = EINVAL;
-		return NULL;
-	}
+	prm->origin = RTE_BPF_ORIGIN_RAW;
+	prm->raw.ins = sd->d_buf;
+	prm->raw.nb_ins = sd->d_size / sizeof(struct ebpf_insn);
 
-	bpf = bpf_load_elf(prm, fd, sname);
-	close(fd);
+	rc = elf_reloc_code(load->elf, sd->d_buf, sd->d_size, sidx, prm);
+	if (rc < 0)
+		return -EINVAL;
 
-	if (bpf == NULL) {
-		RTE_BPF_LOG_LINE(ERR,
-			"%s(fname=\"%s\", sname=\"%s\") failed, "
-			"error code: %d",
-			__func__, fname, sname, rte_errno);
-		return NULL;
-	}
+	return 0;
+}
+
+#else /* RTE_LIBRTE_BPF_ELF */
+
+void
+__rte_bpf_load_elf_cleanup(struct __rte_bpf_load *load)
+{
+	RTE_ASSERT(load->elf == NULL);
+	RTE_ASSERT(load->elf_fd < 0);
+}
 
-	RTE_BPF_LOG_LINE(INFO, "%s(fname=\"%s\", sname=\"%s\") "
-		"successfully creates %p(jit={.func=%p,.sz=%zu});",
-		__func__, fname, sname, bpf, bpf->jit.func, bpf->jit.sz);
-	return bpf;
+int
+__rte_bpf_load_elf_file(struct __rte_bpf_load *load)
+{
+	RTE_SET_USED(load);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libelf installed");
+	return -ENOTSUP;
 }
+
+int
+__rte_bpf_load_elf_code(struct __rte_bpf_load *load)
+{
+	RTE_SET_USED(load);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libelf installed");
+	return -ENOTSUP;
+}
+
+#endif /* RTE_LIBRTE_BPF_ELF */
diff --git a/lib/bpf/bpf_stub.c b/lib/bpf/bpf_stub.c
index e06e820d8327..4c329832c264 100644
--- a/lib/bpf/bpf_stub.c
+++ b/lib/bpf/bpf_stub.c
@@ -10,23 +10,6 @@
  * Contains stubs for unimplemented public API functions
  */
 
-#ifndef RTE_LIBRTE_BPF_ELF
-RTE_EXPORT_SYMBOL(rte_bpf_elf_load)
-struct rte_bpf *
-rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,
-	const char *sname)
-{
-	if (prm == NULL || fname == NULL || sname == NULL) {
-		rte_errno = EINVAL;
-		return NULL;
-	}
-
-	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libelf installed");
-	rte_errno = ENOTSUP;
-	return NULL;
-}
-#endif
-
 #ifndef RTE_HAS_LIBPCAP
 RTE_EXPORT_SYMBOL(rte_bpf_convert)
 struct rte_bpf_prm *
diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c
index a7f4f576c9d6..5bfc59296d05 100644
--- a/lib/bpf/bpf_validate.c
+++ b/lib/bpf/bpf_validate.c
@@ -80,7 +80,7 @@ struct evst_pool {
 };
 
 struct bpf_verifier {
-	const struct rte_bpf_prm *prm;
+	const struct rte_bpf_prm_ex *prm;
 	struct inst_node *in;
 	uint64_t stack_sz;
 	uint32_t nb_nodes;
@@ -1837,7 +1837,7 @@ add_edge(struct bpf_verifier *bvf, struct inst_node *node, uint32_t nidx)
 {
 	uint32_t ne;
 
-	if (nidx >= bvf->prm->nb_ins) {
+	if (nidx >= bvf->prm->raw.nb_ins) {
 		RTE_BPF_LOG_FUNC_LINE(ERR,
 			"program boundary violation at pc: %u, next pc: %u",
 			get_node_idx(bvf, node), nidx);
@@ -1946,10 +1946,10 @@ log_unreachable(const struct bpf_verifier *bvf)
 	struct inst_node *node;
 	const struct ebpf_insn *ins;
 
-	for (i = 0; i != bvf->prm->nb_ins; i++) {
+	for (i = 0; i != bvf->prm->raw.nb_ins; i++) {
 
 		node = bvf->in + i;
-		ins = bvf->prm->ins + i;
+		ins = bvf->prm->raw.ins + i;
 
 		if (node->colour == WHITE &&
 				ins->code != (BPF_LD | BPF_IMM | EBPF_DW))
@@ -1966,7 +1966,7 @@ log_loop(const struct bpf_verifier *bvf)
 	uint32_t i, j;
 	struct inst_node *node;
 
-	for (i = 0; i != bvf->prm->nb_ins; i++) {
+	for (i = 0; i != bvf->prm->raw.nb_ins; i++) {
 
 		node = bvf->in + i;
 		if (node->colour != BLACK)
@@ -1998,9 +1998,9 @@ validate(struct bpf_verifier *bvf)
 	const char *err;
 
 	rc = 0;
-	for (i = 0; i < bvf->prm->nb_ins; i++) {
+	for (i = 0; i < bvf->prm->raw.nb_ins; i++) {
 
-		ins = bvf->prm->ins + i;
+		ins = bvf->prm->raw.ins + i;
 		node = bvf->in + i;
 
 		err = check_syntax(ins);
@@ -2432,7 +2432,7 @@ evaluate(struct bpf_verifier *bvf)
 
 	bvf->evst->rv[EBPF_REG_10] = rvfp;
 
-	ins = bvf->prm->ins;
+	ins = bvf->prm->raw.ins;
 	node = bvf->in;
 	next = node;
 	rc = 0;
@@ -2522,23 +2522,23 @@ evaluate(struct bpf_verifier *bvf)
 }
 
 int
-__rte_bpf_validate(struct rte_bpf *bpf)
+__rte_bpf_validate(const struct rte_bpf_prm_ex *prm, uint32_t *stack_sz)
 {
 	int32_t rc;
 	struct bpf_verifier bvf;
 
 	/* check input argument type, don't allow mbuf ptr on 32-bit */
-	if (bpf->prm.prog_arg.type != RTE_BPF_ARG_RAW &&
-			bpf->prm.prog_arg.type != RTE_BPF_ARG_PTR &&
+	if (prm->prog_arg.type != RTE_BPF_ARG_RAW &&
+			prm->prog_arg.type != RTE_BPF_ARG_PTR &&
 			(sizeof(uint64_t) != sizeof(uintptr_t) ||
-			bpf->prm.prog_arg.type != RTE_BPF_ARG_PTR_MBUF)) {
+			prm->prog_arg.type != RTE_BPF_ARG_PTR_MBUF)) {
 		RTE_BPF_LOG_FUNC_LINE(ERR, "unsupported argument type");
 		return -ENOTSUP;
 	}
 
 	memset(&bvf, 0, sizeof(bvf));
-	bvf.prm = &bpf->prm;
-	bvf.in = calloc(bpf->prm.nb_ins, sizeof(bvf.in[0]));
+	bvf.prm = prm;
+	bvf.in = calloc(prm->raw.nb_ins, sizeof(bvf.in[0]));
 	if (bvf.in == NULL)
 		return -ENOMEM;
 
@@ -2555,11 +2555,11 @@ __rte_bpf_validate(struct rte_bpf *bpf)
 
 	/* copy collected info */
 	if (rc == 0) {
-		bpf->stack_sz = bvf.stack_sz;
+		*stack_sz = bvf.stack_sz;
 
 		/* for LD_ABS/LD_IND, we'll need extra space on the stack */
 		if (bvf.nb_ldmb_nodes != 0)
-			bpf->stack_sz = RTE_ALIGN_CEIL(bpf->stack_sz +
+			*stack_sz = RTE_ALIGN_CEIL(*stack_sz +
 				sizeof(uint64_t), sizeof(uint64_t));
 	}
 
diff --git a/lib/bpf/meson.build b/lib/bpf/meson.build
index 28df7f469a4c..4901b6ee1463 100644
--- a/lib/bpf/meson.build
+++ b/lib/bpf/meson.build
@@ -19,6 +19,7 @@ sources = files('bpf.c',
         'bpf_dump.c',
         'bpf_exec.c',
         'bpf_load.c',
+        'bpf_load_elf.c',
         'bpf_pkt.c',
         'bpf_stub.c',
         'bpf_validate.c')
@@ -38,10 +39,9 @@ deps += ['mbuf', 'net', 'ethdev']
 dep = dependency('libelf', required: false, method: 'pkg-config')
 if dep.found()
     dpdk_conf.set('RTE_LIBRTE_BPF_ELF', 1)
-    sources += files('bpf_load_elf.c')
     ext_deps += dep
 else
-    warning('libelf is missing, rte_bpf_elf_load API will be disabled')
+    warning('libelf is missing, ELF API will be disabled')
 endif
 
 if dpdk_conf.has('RTE_HAS_LIBPCAP')
diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h
index 309d84bc516a..bf58a418191e 100644
--- a/lib/bpf/rte_bpf.h
+++ b/lib/bpf/rte_bpf.h
@@ -86,7 +86,47 @@ struct rte_bpf_xsym {
 };
 
 /**
- * Input parameters for loading eBPF code.
+ * Possible origins of eBPF program code.
+ */
+enum rte_bpf_origin {
+	RTE_BPF_ORIGIN_RAW,		/**< code loaded from raw array */
+	RTE_BPF_ORIGIN_RESERVED,	/**< reserved for cBPF */
+	RTE_BPF_ORIGIN_ELF_FILE,	/**< code loaded from elf_file */
+};
+
+/**
+ * Input parameters for loading eBPF code, extensible version.
+ *
+ * Follows libbpf conventions for extensible structs.
+ */
+struct rte_bpf_prm_ex {
+	size_t sz;  /**< size of this struct for backward compatibility */
+
+	uint32_t flags;  /**< flags controlling eBPF load and other options */
+
+	enum rte_bpf_origin origin;  /**< origin of eBPF program code */
+
+	/** program origin parameters, member in use depends on origin */
+	union {
+		struct {
+			const struct ebpf_insn *ins;  /**< eBPF instructions */
+			uint32_t nb_ins;  /**< number of instructions in ins */
+		} raw;
+		struct {
+			const char *path;  /**< path to the ELF file */
+			const char *section;  /**< ELF section with the code */
+		} elf_file;
+	};
+
+	const struct rte_bpf_xsym *xsym;
+	/**< array of external symbols that eBPF code is allowed to reference */
+	uint32_t nb_xsym;  /**< number of elements in xsym */
+
+	struct rte_bpf_arg prog_arg;  /**< input arg description */
+};
+
+/**
+ * Input parameters for loading eBPF code, legacy version.
  */
 struct rte_bpf_prm {
 	const struct ebpf_insn *ins; /**< array of eBPF instructions */
@@ -116,6 +156,32 @@ struct rte_bpf;
 void
 rte_bpf_destroy(struct rte_bpf *bpf);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: This API may change, or be removed, without prior notice.
+ *
+ * Create a new eBPF execution context, load code from specified origin into it.
+ *
+ * @param prm
+ *   Parameters used to create and initialise the BPF execution context.
+ *
+ *   Member sz must be set to the struct size as known to the application.
+ *   If it exceeds the size known to the library, and the extra part has
+ *   non-zero bytes, parameter is rejected. If it's smaller than the size known
+ *   to the library, defaults are used for the members that are not present.
+ * @return
+ *   BPF handle that is used in future BPF operations,
+ *   or NULL on error, with error code set in rte_errno.
+ *   Possible rte_errno errors include:
+ *   - EINVAL  - invalid parameter passed to function
+ *   - ENOMEM  - can't reserve enough memory
+ *   - ENOTSUP - requested feature is not supported (e.g. no libelf to load ELF)
+ */
+__rte_experimental
+struct rte_bpf *
+rte_bpf_load_ex(const struct rte_bpf_prm_ex *prm)
+	__rte_malloc __rte_dealloc(rte_bpf_destroy, 1);
+
 /**
  * Create a new eBPF execution context and load given BPF code into it.
  *
-- 
2.43.0


^ permalink raw reply related

* [PATCH v4 04/11] bpf: add cBPF origin to rte_bpf_load_ex
From: Marat Khalili @ 2026-05-20 12:49 UTC (permalink / raw)
  To: Konstantin Ananyev; +Cc: dev
In-Reply-To: <20260520124922.42445-1-marat.khalili@huawei.com>

Add cBPF origin to rte_bpf_load_ex to allow loading PCAP filters and
other cBPF code through the unified interface.

Note that for the no-libpcap stub of rte_bpf_convert, the behavior when
called with a NULL program has changed from setting rte_errno to EINVAL
to setting it to ENOTSUP. Since both cases return NULL, callers relying
on pointer checking are unaffected.

Signed-off-by: Marat Khalili <marat.khalili@huawei.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@huawei.com>
---
 lib/bpf/bpf_convert.c | 81 +++++++++++++++++++++++++++++++++++++++++--
 lib/bpf/bpf_impl.h    | 11 ++++++
 lib/bpf/bpf_load.c    | 12 ++++++-
 lib/bpf/bpf_stub.c    | 27 ---------------
 lib/bpf/meson.build   | 11 +++---
 lib/bpf/rte_bpf.h     |  8 ++++-
 6 files changed, 113 insertions(+), 37 deletions(-)
 delete mode 100644 lib/bpf/bpf_stub.c

diff --git a/lib/bpf/bpf_convert.c b/lib/bpf/bpf_convert.c
index 953ca80670c4..e8074b13d037 100644
--- a/lib/bpf/bpf_convert.c
+++ b/lib/bpf/bpf_convert.c
@@ -9,6 +9,12 @@
  * Copyright (c) 2011 - 2014 PLUMgrid, http://plumgrid.com
  */
 
+#include "bpf_impl.h"
+#include <eal_export.h>
+#include <rte_errno.h>
+
+#ifdef RTE_HAS_LIBPCAP
+
 #include <assert.h>
 #include <errno.h>
 #include <stdbool.h>
@@ -17,17 +23,14 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <eal_export.h>
 #include <rte_common.h>
 #include <rte_bpf.h>
 #include <rte_log.h>
 #include <rte_malloc.h>
-#include <rte_errno.h>
 
 #include <pcap/pcap.h>
 #include <pcap/bpf.h>
 
-#include "bpf_impl.h"
 #include "bpf_def.h"
 
 #ifndef BPF_MAXINSNS
@@ -572,3 +575,75 @@ rte_bpf_convert(const struct bpf_program *prog)
 
 	return prm;
 }
+
+void
+__rte_bpf_convert_cleanup(struct __rte_bpf_load *load)
+{
+	free(load->ins);
+}
+
+int
+__rte_bpf_convert(struct __rte_bpf_load *load)
+{
+	struct rte_bpf_prm_ex *const prm = &load->prm;
+	uint32_t nb_ins = 0;
+	int ret;
+
+	RTE_ASSERT(prm->origin == RTE_BPF_ORIGIN_CBPF);
+
+	if (prm->cbpf.ins == NULL || prm->cbpf.nb_ins == 0)
+		return -EINVAL;
+
+	/* 1st pass: calculate the eBPF program length */
+	ret = bpf_convert_filter(prm->cbpf.ins, prm->cbpf.nb_ins, NULL, &nb_ins);
+	if (ret < 0) {
+		RTE_BPF_LOG_FUNC_LINE(ERR, "cannot get eBPF length");
+		return ret;
+	}
+
+	RTE_ASSERT(load->ins == NULL);
+	load->ins = malloc(nb_ins * sizeof(load->ins[0]));
+	if (load->ins == NULL)
+		return -ENOMEM;
+
+	/* 2nd pass: remap cBPF to eBPF instructions  */
+	ret = bpf_convert_filter(prm->cbpf.ins, prm->cbpf.nb_ins, load->ins, &nb_ins);
+	if (ret < 0) {
+		RTE_BPF_LOG_FUNC_LINE(ERR, "cannot convert cBPF to eBPF");
+		return ret;
+	}
+
+	prm->origin = RTE_BPF_ORIGIN_RAW;
+	prm->raw.ins = load->ins;
+	prm->raw.nb_ins = nb_ins;
+
+	return 0;
+}
+
+#else /* RTE_HAS_LIBPCAP */
+
+RTE_EXPORT_SYMBOL(rte_bpf_convert)
+struct rte_bpf_prm *
+rte_bpf_convert(const struct bpf_program *prog)
+{
+	RTE_SET_USED(prog);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libpcap installed");
+	rte_errno = ENOTSUP;
+	return NULL;
+}
+
+void
+__rte_bpf_convert_cleanup(struct __rte_bpf_load *load)
+{
+	RTE_ASSERT(load->ins == NULL);
+}
+
+int
+__rte_bpf_convert(struct __rte_bpf_load *load)
+{
+	RTE_SET_USED(load);
+	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libpcap installed");
+	return -ENOTSUP;
+}
+
+#endif /* RTE_HAS_LIBPCAP */
diff --git a/lib/bpf/bpf_impl.h b/lib/bpf/bpf_impl.h
index 4a98b3373067..92d03583d977 100644
--- a/lib/bpf/bpf_impl.h
+++ b/lib/bpf/bpf_impl.h
@@ -21,6 +21,9 @@ struct rte_bpf {
 struct __rte_bpf_load {
 	struct rte_bpf_prm_ex prm;
 
+	/* Conversion from cBPF. */
+	struct ebpf_insn *ins;
+
 	/* Loading ELF and applying relocations. */
 	int elf_fd;  /* ELF fd, must be negative (not zero) by default. */
 	void *elf;  /* Using void to avoid dependency on libelf. */
@@ -34,6 +37,14 @@ struct __rte_bpf_load {
  * to avoid potential name conflict with other libraries.
  */
 
+/* Free temporary resources created by converting from cBPF to eBPF. */
+void
+__rte_bpf_convert_cleanup(struct __rte_bpf_load *load);
+
+/* Convert program from cBPF to eBPF. */
+int
+__rte_bpf_convert(struct __rte_bpf_load *load);
+
 /* Free temporary resources created by opening ELF. */
 void
 __rte_bpf_load_elf_cleanup(struct __rte_bpf_load *load);
diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c
index f63093b9bc09..e3265e97ff7f 100644
--- a/lib/bpf/bpf_load.c
+++ b/lib/bpf/bpf_load.c
@@ -240,6 +240,9 @@ load_try(struct __rte_bpf_load *load, const struct rte_bpf_prm_ex *app_prm)
 	switch (load->prm.origin) {
 	case RTE_BPF_ORIGIN_RAW:
 		break;
+	case RTE_BPF_ORIGIN_CBPF:
+		rc = rc < 0 ? rc : __rte_bpf_convert(load);
+		break;
 	case RTE_BPF_ORIGIN_ELF_FILE:
 		rc = rc < 0 ? rc : __rte_bpf_load_elf_file(load);
 		rc = rc < 0 ? rc : __rte_bpf_load_elf_code(load);
@@ -254,6 +257,13 @@ load_try(struct __rte_bpf_load *load, const struct rte_bpf_prm_ex *app_prm)
 	return rc;
 }
 
+static void
+load_cleanup(struct __rte_bpf_load *load)
+{
+	__rte_bpf_convert_cleanup(load);
+	__rte_bpf_load_elf_cleanup(load);
+}
+
 RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_load_ex, 26.11)
 struct rte_bpf *
 rte_bpf_load_ex(const struct rte_bpf_prm_ex *prm)
@@ -262,7 +272,7 @@ rte_bpf_load_ex(const struct rte_bpf_prm_ex *prm)
 
 	const int rc = load_try(&load, prm);
 
-	__rte_bpf_load_elf_cleanup(&load);
+	load_cleanup(&load);
 
 	RTE_ASSERT((rc < 0) == (load.bpf == NULL));
 
diff --git a/lib/bpf/bpf_stub.c b/lib/bpf/bpf_stub.c
deleted file mode 100644
index 4c329832c264..000000000000
--- a/lib/bpf/bpf_stub.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2018-2021 Intel Corporation
- */
-
-#include "bpf_impl.h"
-#include <eal_export.h>
-#include <rte_errno.h>
-
-/**
- * Contains stubs for unimplemented public API functions
- */
-
-#ifndef RTE_HAS_LIBPCAP
-RTE_EXPORT_SYMBOL(rte_bpf_convert)
-struct rte_bpf_prm *
-rte_bpf_convert(const struct bpf_program *prog)
-{
-	if (prog == NULL) {
-		rte_errno = EINVAL;
-		return NULL;
-	}
-
-	RTE_BPF_LOG_FUNC_LINE(ERR, "not supported, rebuild with libpcap installed");
-	rte_errno = ENOTSUP;
-	return NULL;
-}
-#endif
diff --git a/lib/bpf/meson.build b/lib/bpf/meson.build
index 4901b6ee1463..7e8a300e3f87 100644
--- a/lib/bpf/meson.build
+++ b/lib/bpf/meson.build
@@ -15,14 +15,16 @@ if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_32')
     subdir_done()
 endif
 
-sources = files('bpf.c',
+sources = files(
+        'bpf.c',
+        'bpf_convert.c',
         'bpf_dump.c',
         'bpf_exec.c',
         'bpf_load.c',
         'bpf_load_elf.c',
         'bpf_pkt.c',
-        'bpf_stub.c',
-        'bpf_validate.c')
+        'bpf_validate.c',
+)
 
 if arch_subdir == 'x86' and dpdk_conf.get('RTE_ARCH_64')
     sources += files('bpf_jit_x86.c')
@@ -45,8 +47,7 @@ else
 endif
 
 if dpdk_conf.has('RTE_HAS_LIBPCAP')
-    sources += files('bpf_convert.c')
     ext_deps += pcap_dep
 else
-    warning('libpcap is missing, rte_bpf_convert API will be disabled')
+    warning('libpcap is missing, cBPF API will be disabled')
 endif
diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h
index 0e7eaa3c18eb..da2bdea7e0a8 100644
--- a/lib/bpf/rte_bpf.h
+++ b/lib/bpf/rte_bpf.h
@@ -95,10 +95,12 @@ struct rte_bpf_xsym {
  */
 enum rte_bpf_origin {
 	RTE_BPF_ORIGIN_RAW,		/**< code loaded from raw array */
-	RTE_BPF_ORIGIN_RESERVED,	/**< reserved for cBPF */
+	RTE_BPF_ORIGIN_CBPF,		/**< code converted from cbpf */
 	RTE_BPF_ORIGIN_ELF_FILE,	/**< code loaded from elf_file */
 };
 
+struct bpf_insn;
+
 /**
  * Input parameters for loading eBPF code, extensible version.
  *
@@ -117,6 +119,10 @@ struct rte_bpf_prm_ex {
 			const struct ebpf_insn *ins;  /**< eBPF instructions */
 			uint32_t nb_ins;  /**< number of instructions in ins */
 		} raw;
+		struct {
+			const struct bpf_insn *ins;  /**< cBPF instructions */
+			uint32_t nb_ins;  /**< number of instructions in ins */
+		} cbpf;
 		struct {
 			const char *path;  /**< path to the ELF file */
 			const char *section;  /**< ELF section with the code */
-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox